Tricket med PREPARE
virker ikke, da det ikke tager en * tekststreng* (en værdi) som CREATE FUNCTION
gør, men en gyldig erklæring (kode).
At konvertere data i eksekverbar kode du skal bruge dynamisk SQL, dvs. EXECUTE
i en plpgsql-funktion eller DO
udmelding. Dette fungerer uden problemer, så længe returtypen ikke afhænger af resultatet af den første funktion myresult()
. Ellers er du tilbage for at fange 22 som skitseret i mit tidligere svar:
- Sådan udføres et strengresultat af en lagret procedure i postgres
Den afgørende del er at erklære returtypen (rækketype i dette tilfælde) på en eller anden måde. Du kan oprette en TABLE
, TEMP TABLE
eller TYPE
til formålet. Eller du kan bruge en forberedt erklæring eller en refcursor.
Løsning med forberedt redegørelse
Du har været meget tæt på. Den manglende brik i puslespillet er at forberede den genererede forespørgsel med dynamisk SQL .
Funktion til at forberede sætning dynamisk
Opret denne funktion én gang . Det er en optimeret og sikker version af din 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;
Jeg bruger regclass
for tabelnavnsparameteren _tbl
at gøre det entydigt og sikkert mod SQLi. Detaljer:
- Tabelnavn som en PostgreSQL-funktionsparameter
Informationsskemaet inkluderer ikke oid-kolonnen i systemkataloger, så jeg skiftede til pg_catalog.pg_attribute
i stedet for information_schema.columns
. Det er også hurtigere. Der er fordele og ulemper ved dette:
- Sådan kontrollerer du, om en tabel findes i et givet skema
Hvis en forberedt sætning med navnet stmt_dyn
eksisterede allerede, PREPARE
ville rejse en undtagelse. Hvis det er acceptabelt, skal du fjerne markeringen på systemvisningen pg_prepared_statements
og følgende DEALLOCATE
.
Mere sofistikerede algoritmer er mulige til at administrere flere forberedte sætninger pr. session, eller tage navnet på den forberedte sætning som en ekstra parameter, eller endda bruge en MD5-hash af forespørgselsstrengen som navn, men det er ud over omfanget af dette spørgsmål.
Vær opmærksom på, at PREPARE
opererer uden for rammerne af transaktioner , én gang PREPARE
lykkes, eksisterer den forberedte erklæring i hele sessionens levetid. Hvis indpakningstransaktionen afbrydes, PREPARE
er upåvirket. ROLLBACK
kan ikke fjerne udarbejdede erklæringer.
Dynamisk udførelse af forespørgsler
To forespørgsler, men kun én opkald til serveren. Og også meget effektiv.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Enklere og meget mere effektivt til de fleste simple brugstilfælde end at oprette en midlertidig tabel eller en markør og vælge/hente fra den (hvilket ville være andre muligheder).
SQL Fiddle.