PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Verwenden Sie die Textausgabe einer Funktion als neue Abfrage

Der Trick mit PREPARE funktioniert nicht, da es keine * Textzeichenfolge * (einen Wert) wie CREATE FUNCTION akzeptiert tut, aber eine gültige Anweisung (Code).

Um Daten umzuwandeln in ausführbaren Code Sie müssen dynamisches SQL verwenden, d. h. EXECUTE in einer plpgsql-Funktion oder DO Erklärung. Dies funktioniert problemlos, solange der Rückgabetyp nicht vom Ergebnis der ersten Funktion myresult() abhängt . Andernfalls sind Sie zurück, um 22 zu fangen, wie in meiner vorherigen Antwort beschrieben:

  • Wie man ein String-Ergebnis einer gespeicherten Prozedur in Postgres ausführt

Der entscheidende Teil ist die Deklaration des Rückgabetyps (Zeilentyp in diesem Fall) irgendwie. Sie können eine TABLE erstellen , TEMP TABLE oder TYPE für den Zweck. Oder Sie können eine vorbereitete Anweisung oder einen Refcursor verwenden.

Lösung mit vorbereiteter Anweisung

Du warst sehr nah dran. Das fehlende Puzzleteil besteht darin, die generierte Abfrage mit dynamischem SQL vorzubereiten .

Funktion zur dynamischen Erstellung von Auszügen

Erstellen Sie diese Funktion einmal . Es ist eine optimierte und sichere Version Ihrer Funktion myresult() :

CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
  RETURNS void AS 
$func$
BEGIN
   IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
      DEALLOCATE stmt_dyn;
   END IF;                 -- you my or may not need this safety check 

   EXECUTE (
     SELECT 'PREPARE stmt_dyn AS SELECT '
         || string_agg(quote_ident(attname), ',' ORDER BY attname)
         || ' FROM ' || _tbl
      FROM   pg_catalog.pg_attribute
      WHERE  attrelid = _tbl
      AND    attname LIKE _prefix || '%'
      AND    attnum > 0
      AND    NOT attisdropped
     );
END
$func$  LANGUAGE plpgsql;

Ich verwende regclass für den Tabellennamenparameter _tbl um es eindeutig und sicher gegen SQLi zu machen. Einzelheiten:

  • Tabellenname als PostgreSQL-Funktionsparameter

Das Informationsschema enthält nicht die oid-Spalte von Systemkatalogen, also bin ich zu pg_catalog.pg_attribute gewechselt anstelle von information_schema.columns . Das geht auch schneller. Dafür gibt es Vor- und Nachteile:

  • Überprüfen, ob eine Tabelle in einem bestimmten Schema existiert

Wenn eine vorbereitete Anweisung mit dem Namen stmt_dyn bereits vorhanden, PREPARE würde eine Ausnahme auslösen. Wenn dies akzeptabel ist, entfernen Sie das Häkchen in der Systemansicht pg_prepared_statements und das folgende DEALLOCATE .
Es sind ausgefeiltere Algorithmen möglich, um mehrere vorbereitete Anweisungen pro Sitzung zu verwalten oder den Namen der vorbereiteten Anweisung als zusätzlichen Parameter zu verwenden oder sogar einen MD5-Hash der Abfragezeichenfolge als Namen zu verwenden, aber das geht zu weit Umfang dieser Frage.

Beachten Sie, dass PREPARE arbeitet außerhalb des Bereichs von Transaktionen , einmal PREPARE erfolgreich ist, existiert die vorbereitete Anweisung für die Lebensdauer der Sitzung. Wenn die Verpackungstransaktion abgebrochen wird, PREPARE ist unbeeinflusst. ROLLBACK kann nicht vorbereitete Anweisungen entfernen.

Dynamische Abfrageausführung

Zwei Abfragen, aber nur eine Aufruf an den Server. Und sehr effizient.

SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;

Einfacher und viel effizienter für die meisten einfachen Anwendungsfälle, als eine temporäre Tabelle oder einen Cursor zu erstellen und daraus auszuwählen / abzurufen (was andere Optionen wären).

SQL-Geige.