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.