Jak mogę ustalić, czy tabela istnieje w bieżącej ścieżce wyszukiwania za pomocą PLPGSQL?

10

Piszę skrypt instalacyjny aplikacji, która jest dodatkiem do innej aplikacji, więc chcę sprawdzić, czy istnieją tabele dla innej aplikacji. Jeśli nie, chcę dać użytkownikowi użyteczny błąd. Jednak nie wiem, jaki schemat będzie trzymał tabele.

DO LANGUAGE plpgsql $$
BEGIN
    PERFORM 1
    FROM
        pg_catalog.pg_class c
        JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    WHERE
        n.nspname = current_setting('search_path')
        AND c.relname = 'foo'
        AND c.relkind = 'r'; -- not sure if I actually need this or not...

    IF NOT FOUND THEN
        RAISE 'This application depends on tables created by another application';
    END IF;
END;
$$;

Jednak current_setting('search_path')zwraca tekst zawierający "$user",publicdomyślnie, co nie jest strasznie użyteczne.

Jedyną rzeczą, o której mogę myśleć, jest próba wybrania z tabeli i wyłapanie wyjątku. Wykonałby to zadanie, ale nie sądzę, że jest bardzo elegancki i czytałem, że jest drogi w użyciu (choć może byłoby to w tym scenariuszu odpowiednie, ponieważ uruchamiam go tylko raz?).

Cimmanon
źródło

Odpowiedzi:

18

Szybko i brudno

W PostgreSQL 9.4+ użytku

SELECT to_regclass('foo');

Zwraca NULL, jeśli identyfikator nie został znaleziony w ścieżce wyszukiwania.
W Postgresie 9.3 lub starszym użyj obsady, abyregclass :

SELECT 'foo'::regclass;

To podnosi wyjątek , jeśli obiekt nie został znaleziony!

Jeśli 'foo'zostanie znaleziony, oidzostanie zwrócony w swojej textreprezentacji. To tylko nazwa tabeli, kwalifikowana do schematu zgodnie z bieżącą ścieżką wyszukiwania i w razie potrzeby podwójnie cytowana.

Jeśli obiekt nie zostanie znaleziony, możesz być pewien, że nie istnieje on nigdzie na ścieżce wyszukiwania - lub wcale nie ma nazwy kwalifikowanej do schematu ( schema.foo).

Jeśli zostanie znaleziony, występują dwie wady :

  1. Wyszukiwanie obejmuje niejawne schematy ścieżki szukania , a mianowicie pg_catalogipg_temp . Ale możesz chcieć wykluczyć tabele temp i systemowe do swoich celów. (?)

  2. Rzutowanie regclassdziała na wszystkie obiekty w katalogu systemowym pg_class: indeksy, widoki, sekwencje itp. Nie tylko tabele. Wygląda na to, że szukasz wyłącznie zwykłego stołu. Prawdopodobnie będziesz mieć również problemy z innymi obiektami o tej samej nazwie. Detale:

Powolny i pewny

Wróciliśmy do Twojego zapytania, ale nie używaj go current_setting('search_path'), co powoduje powrót do samego ustawienia. Użyj dedykowanej funkcji informacji o systemie current_schemas(). Według dokumentacji:

current_schemas(boolean) name[]
nazwy schematów w ścieżce wyszukiwania, opcjonalnie włączając schematy niejawne

"$user"w ścieżce wyszukiwania jest rozwiązany elegancko. Jeśli nie SESSION_USERistnieje żaden schemat o nazwie istnieje, schemat nie jest zwracany na początek. Ponadto, w zależności od tego, czego dokładnie chcesz, możesz dodatkowo generować schematy niejawne ( pg_catalogi być może pg_temp) - ale zakładam, że nie chcesz ich dla danej sprawy, więc użyj:

DO 
$do$
BEGIN
   IF EXISTS (
      SELECT  -- list can be empty
      FROM   pg_catalog.pg_class c
      JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
      WHERE  n.nspname = ANY(current_schemas(FALSE))
      AND    n.nspname NOT LIKE 'pg_%'  -- exclude system schemas!
      AND    c.relname = 'foo'
      AND    c.relkind = 'r')           -- you probably need this
   THEN
      RAISE 'This application depends on tables created by another application';
   END IF;
END
$do$;

SQL Fiddle , pokazujący wszystko oprócz ostatniejDOinstrukcji.
SQL Fiddle (JDBC) ma problemy z DOinstrukcjami zawierającymi znaki zakończenia.

Erwin Brandstetter
źródło
1

Możesz przekonwertować wartość config na tablicę i zastąpić $userją bieżącą nazwą użytkownika. Tablicy można następnie użyć w warunku where:

where n.nspname = any(string_to_array(replace(current_setting('search_path'), '$user', current_user), ','))
koń bez imienia
źródło
0
./sshi.sh vb20deployment controller <<'HERE'
export PGPASSWORD="postgres"
cd logu/postgresql/bin
row=1
tableArray=(table1 table2 table3 table4 table5 table6)

for (( x=0 ; x<=5 ; x++)) ; do        

./psql.bin --port=5432 --username=postgres --host=hostname.rds.amazonaws.com --dbname=mydb -c "SELECT * FROM information_schema.tables WHERE '${tableArray[$x]}' = table_name" | while read -a Record ; do
  row=$((row + 1))
  if [[ $row -gt 3 ]]; then

     echo ${Record[4]}

   fi
done

done


HERE
Vishal Bendre
źródło