2016-06-24 2 views
0

У меня возникают проблемы с выполнением «выполнить создание индекса» внутри функции plgpsql (postgres 9.4). Например:«выполнить создание индекса» в plpgsql не запускается

create or replace function foo() returns void language plpgsql as $$ 
begin 
    perform 'create unique index patients_row_id_key on patients(row_id)'; 
end; $$; 

Кажется работать нормально:

select foo(); 

Однако индекс не создается. Любой диагноз и обходной путь? Я пробовал:

alter function foo() VOLATILE; 

и все еще не повезло.

+0

Ах, здорово: я думал, что «выполнить» было исключено, потому что нет никакой ценности результата. Войдите как ответ, и я соглашусь .... – shaunc

ответ

2

PERFORM заявление в PLPGSQL, используемое для выполнения запросов, которые не возвращают результат или какой результат не полезен. Технически PERFORM ... внутри блока PLPGSQL равен SELECT ... в простом SQL. Поэтому в вашем примере вы пытаетесь выполнить что-то вроде

select 'create unique index patients_row_id_key on patients(row_id)'; 

и просто проигнорировать результат.

Подробнее: Executing a Command With No Result

Вы не должны обернуть заявления DDL внутри PLPGSQL и может использовать его как:

create or replace function foo() returns void language plpgsql as $$ 
begin 
    create unique index patients_row_id_key on patients(row_id); 
end; $$; 

Или, если вы хотите построить его во время выполнения, то используйте EXECUTE заявление: Executing Dynamic Commands например:

create or replace function foo(p_tablename text) returns void language plpgsql as $$ 
begin 
    execute 'create unique index ' || p_tablename || '_row_id_key on ' || p_tablename || '(row_id)'; 
end; $$; 
+0

[Как объяснил @Chris] (http://stackoverflow.com/a/38021245/939860), ваша предлагаемая функция небезопасна для SQL-инъекции. –

+0

@ErwinBrandstetter Это был просто быстрый пример использования 'execute'. В реальной жизни такие сервисные функции должны быть ограничены для выполнения обычными пользователями. ИМО. – Abelisto

+0

Ограничение функций для обычных пользователей вряд ли является решением проблемы. Названия таблиц (и другие идентификаторы) должны обрабатываться как пользовательский ввод в любое время. Кроме того, если доверенный пользователь запускает таблицы схемы, то наличие такой таблицы является тиковой бомбой. –

3

В качестве дополнения к пункту выполнения, обратите внимание на два важных момента. (! Опасная)

  1. Вы делаете интерполяцию строки с SQL запросами, и
  2. Вы должны использовать quote_ident, не quote_literal

Если вы используете функцию Abelisto по выше, и вызвать его:

SELECT foo('test_idx on test; drop table foo; --'); 

SQL-инъекция в хранимой процедуре. Хуже, если это определение безопасности. Исправленная версия будет:

create or replace function foo(p_tablename text) returns void language plpgsql as $$ 
begin 
    execute 'create unique index ' || quote_ident(p_tablename || '_row_id_key') || ' on ' || quote_ident(p_tablename) || '(row_id)'; 
end; $$; 
+1

NB Функция, которую я дал в качестве примера, вообще не принимала никаких параметров - просто нарисованный пример, конечно, потому что я хотел спросить о «выполнении». Реальный код (который использует quote_ident для идентификаторов) гораздо более активен. – shaunc

+2

Я это признаю. Причина этого заключалась в том, что решение предлагало использовать параметры, и это опасно для тех, кто может читать его через год. –

+0

Правильно ... спасибо! – shaunc

4

What @Abelisto wrote о PERFORM.
И what @Chris added о внедрении SQL.

Плюс, я предлагаю использовать format() для чего угодно, кроме самых тривиальных строк запросов, чтобы упростить вашу жизнь с помощью динамического SQL. And the manual does, too:

Очиститель подход заключается в использовании %I спецификации format() «ы для таблиц и столбцов.

CREATE OR REPLACE FUNCTION foo(_tbl text) 
    RETURNS void AS 
$func$ 
BEGIN 
    EXECUTE format('CREATE UNIQUE INDEX %I ON %I(row_id)', _tbl || _row_id_key', _tbl); 
END 
$func$ LANGUAGE plpgsql; 

regclass параметра является удобной альтернативой для передачи имен таблиц, но конкатенаций новых идентификаторов может быть сложнее - как это недавно соответствующим случай идет, чтобы показать:

Смежные вопросы