Il trucco con PREPARE
non funziona, poiché non richiede una *stringa di testo* (un valore) come CREATE FUNCTION
lo fa, ma una dichiarazione valida (codice).
Per convertire dati nel codice eseguibile devi usare SQL dinamico, ad esempio EXECUTE
in una funzione plpgsql o DO
dichiarazione. Funziona senza problemi fintanto che il tipo restituito non dipende dal risultato della prima funzione myresult()
. Altrimenti sei tornato a prendere 22 come descritto nella mia risposta precedente:
- Come eseguire un risultato stringa di una procedura memorizzata in postgres
La parte cruciale è dichiarare il tipo di reso (tipo di riga in questo caso) in qualche modo. Puoi creare una TABLE
, TEMP TABLE
o TYPE
allo scopo. Oppure puoi usare una dichiarazione preparata o un recursor.
Soluzione con dichiarazione preparata
Sei stato molto vicino. Il pezzo mancante del puzzle è preparare la query generata con SQL dinamico .
Funzione per preparare la dichiarazione in modo dinamico
Crea questa funzione una volta . È una versione ottimizzata e sicura della tua funzione 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;
Uso regclass
per il parametro nome tabella _tbl
per renderlo univoco e sicuro contro SQLi. Dettagli:
- Nome tabella come parametro di funzione PostgreSQL
Lo schema delle informazioni non include la colonna oid dei cataloghi di sistema, quindi sono passato a pg_catalog.pg_attribute
invece di information_schema.columns
. Anche quello è più veloce. Ci sono pro e contro per questo:
- Come verificare se esiste una tabella in un determinato schema
Se un'istruzione preparata con il nome stmt_dyn
già esistente, PREPARE
solleverebbe un'eccezione. Se ciò è accettabile, rimuovere il segno di spunta dalla vista di sistema pg_prepared_statements
e il seguente DEALLOCATE
.
Algoritmi più sofisticati sono possibili per gestire più istruzioni preparate per sessione, o prendere il nome dell'istruzione preparata come parametro aggiuntivo, o anche usare un hash MD5 della stringa di query come nome, ma questo va oltre portata di questa domanda.
Tieni presente che PREPARE
opera al di fuori dell'ambito delle transazioni , una volta PREPARE
riesce, l'istruzione preparata esiste per la durata della sessione. Se la transazione di wrapping viene interrotta, PREPARE
è inalterato. ROLLBACK
non posso rimuovere le dichiarazioni preparate.
Esecuzione dinamica della query
Due query, ma solo una chiamare il server. E anche molto efficiente.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Più semplice e molto più efficiente per i casi d'uso più semplici rispetto alla creazione di una tabella temporanea o di un cursore e alla selezione / recupero da quello (che sarebbero altre opzioni).
SQL Fiddle.