Трикът с PREPARE
не работи, тъй като не приема * текстов низ* (стойност) като CREATE FUNCTION
прави, но валидно изявление (код).
За да конвертирате данни в изпълним код трябва да използвате динамичен SQL, т.е. EXECUTE
във функция plpgsql или DO
изявление. Това работи без проблем, стига типът връщане да не зависи от резултата от първата функция myresult()
. В противен случай се връщате, за да хванете 22, както е посочено в предишния ми отговор:
- Как да се изпълни низ резултат от съхранена процедура в postgres
Решаващата част е да декларирате типа на връщането (тип ред в този случай) по някакъв начин. Можете да създадете TABLE
, TEMP TABLE
или TYPE
за целта. Или можете да използвате подготвен оператор или рекурсор.
Решение с подготвено изявление
Били сте много близки. Липсващото парче от пъзела е да подготвите генерираната заявка с динамичен SQL .
Функция за динамично изготвяне на изявление
Създайте тази функция веднъж . Това е оптимизирана и безопасна версия на вашата функция 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;
Използвам regclass
за параметъра на името на таблицата _tbl
за да го направи недвусмислено и безопасно срещу SQLi. Подробности:
- Име на таблица като параметър на функцията на PostgreSQL
Информационната схема не включва колоната oid на системните каталози, затова преминах към pg_catalog.pg_attribute
вместо information_schema.columns
. Това също е по-бързо. Има плюсове и минуси за това:
- Как да проверите дали таблица съществува в дадена схема
Ако подготвено изявление с името stmt_dyn
вече съществува, PREPARE
би направило изключение. Ако това е приемливо, премахнете отметката в системния изглед pg_prepared_statements
и следното DEALLOCATE
.
Възможни са по-сложни алгоритми за управление на множество подготвени оператори на сесия или приемане на името на подготвеното изявление като допълнителен параметър или дори използване на MD5 хеш на низа на заявката като име, но това е извън обхват на този въпрос.
Имайте предвид, че PREPARE
работи извън обхвата на транзакциите , веднъж PREPARE
успее, подготвеното изявление съществува за целия живот на сесията. Ако транзакцията за опаковане е прекъсната, PREPARE
е незасегната. ROLLBACK
не може премахнете подготвените изявления.
Динамично изпълнение на заявка
Две заявки, но само една обаждане до сървъра. И също много ефективен.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
По-просто и много по-ефективно за повечето прости случаи на използване, отколкото да създавате временна таблица или курсор и да избирате/извличате от тях (което би било други опции).
SQL Fiddle.