2016-06-24 17 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中的语句用于执行不返回结果或哪些结果无用的查询。技术上,PLPGSQL块内的PERFORM ...等于普通SQL中的SELECT ...。因此,在您的示例中,您尝试执行类似于

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

并且忽略结果。

了解更多:Executing a Command With No Result

你不应该来包装里面PLPGSQL DDL语句,并可以把它当作是:

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

[As @Chris解释](http://stackoverflow.com/a/38021245/939860),您的建议功能是不安全的SQL注入。 –

+0

@ErwinBrandstetter这只是'execute'语句用法的一个简单例子。在现实生活中,这些服务功能应该由常规用户限制执行。 IMO。 – Abelisto

+0

将功能限制于常规用户难以解决问题。表名(和其他标识符)必须始终像用户输入一样对待。否则,如果一个信任用户运行一个模式表,那么这个表的存在就是一个滴答作响的炸弹。 –

3

作为使用执行点的补充,请注意两点。 (!危险)

  1. 你正在做的SQL查询字符串插值和
  2. 你必须使用quote_ident,不是的quote_nullable

如果使用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

注意我作为例子给出的函数根本没有采用任何参数 - 只是一个当然的例子,因为我想问一下关于“执行”的问题。真正的代码(使用quote_ident作为身份)涉及更多。 – shaunc

+2

我意识到这一点。讨论这个问题的原因是,解决方案提供了使用参数,这对于一年中可能阅读的人来说部分是危险的。 –

+0

对......谢谢! – shaunc

4

What @Abelisto wrotePERFORM
what @Chris added关于SQL注入。

另外,我建议使用format()除了最琐碎的查询字符串之外的任何东西,以使您的生活更轻松。 And the manual does, too:

的清洁器的方法是使用format()的为表或列名称%I规范。

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参数是通过表名,但串联新的标识符可能会非常棘手一个方便的选择 - 因为这最近的相关情况正好说明: