Wie kann ich mit PLPGSQL feststellen, ob im aktuellen Suchpfad eine Tabelle vorhanden ist?

10

Ich schreibe ein Setup-Skript für eine Anwendung, die ein Addon für eine andere Anwendung ist, und möchte daher überprüfen, ob die Tabellen für die andere Anwendung vorhanden sind. Wenn nicht, möchte ich dem Benutzer einen nützlichen Fehler geben. Ich weiß jedoch nicht, in welchem ​​Schema die Tabellen gespeichert werden.

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;
$$;

Allerdings current_setting('search_path')gibt einen Text enthält "$user",publicstandardmäßig die nicht sehr nützlich ist.

Das einzige andere, was mir einfällt, ist, aus der Tabelle auszuwählen und die Ausnahme abzufangen. Es würde den Job machen, aber ich denke nicht, dass es sehr elegant ist und ich habe gelesen, dass es teuer zu benutzen ist (obwohl das in diesem Szenario vielleicht in Ordnung wäre, da ich es nur einmal laufen lasse?).

Cimmanon
quelle

Antworten:

18

Schnell und dreckig

In Postgres 9.4+ verwenden

SELECT to_regclass('foo');

Gibt NULL zurück, wenn der Bezeichner nicht im Suchpfad gefunden wird. Verwenden Sie
in Postgres 9.3 oder älter eine Besetzung, umregclass :

SELECT 'foo'::regclass;

Dies löst eine Ausnahme aus , wenn das Objekt nicht gefunden wird!

Wenn 'foo'gefunden wird, wird das oidin seiner textDarstellung zurückgegeben. Dies ist nur der Tabellenname, der gemäß dem aktuellen Suchpfad schemaqualifiziert und bei Bedarf in doppelte Anführungszeichen gesetzt ist.

Wenn das Objekt nicht gefunden wird, können Sie sicher sein, dass es nirgendwo im Suchpfad vorhanden ist - oder überhaupt nicht für einen schemaqualifizierten Namen ( schema.foo).

Wenn es gefunden wird, gibt es zwei Mängel :

  1. Die Suche enthält implizite Schemata des Suchpfads , nämlich pg_catalogundpg_temp . Möglicherweise möchten Sie jedoch temporäre und Systemtabellen für Ihren Zweck ausschließen. (?)

  2. Eine Umwandlung regclassfür alle Objekte im Systemkatalog pg_class: Indizes, Ansichten, Sequenzen usw. Nicht nur Tabellen. Sie scheinen ausschließlich nach einem normalen Tisch zu suchen. Wahrscheinlich haben Sie jedoch auch Probleme mit anderen gleichnamigen Objekten. Einzelheiten:

Langsam und sicher

Wir sind wieder bei Ihrer Anfrage, verwenden diese jedoch nicht current_setting('search_path'), wodurch die nackte Einstellung zurückgegeben wird. Verwenden Sie die dedizierte Systeminformationsfunktion current_schemas(). Pro Dokumentation:

current_schemas(boolean) name[]
Namen von Schemas im Suchpfad, optional einschließlich impliziter Schemas

"$user"im Suchpfad wird intelligent aufgelöst. Wenn kein Schema mit dem Namen von SESSION_USERvorhanden ist, wird das Schema zunächst nicht zurückgegeben. Abhängig davon, was Sie genau möchten, können Sie auch implizite Schemata ( pg_catalogund möglicherweise pg_temp) zusätzlich ausgeben - aber ich gehe davon aus, dass Sie diese für den vorliegenden Fall nicht möchten. Verwenden Sie also:

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 , die alle außer der letztenDOAnweisungdemonstriert.
SQL Fiddle (JDBC) hat Probleme mit DOAnweisungen, die Abschlusszeichen enthalten.

Erwin Brandstetter
quelle
1

Sie können den Konfigurationswert in ein Array konvertieren und den $userdurch den aktuellen Benutzernamen ersetzen . Das Array kann dann in der where-Bedingung verwendet werden:

where n.nspname = any(string_to_array(replace(current_setting('search_path'), '$user', current_user), ','))
ein Pferd ohne Name
quelle
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
quelle