"perform create index" 在 plpgsql 中没有 运行

"perform create index" in plpgsql doesn't run

我在 plgpsql 函数 (postgres 9.4) 中执行 "perform create index" 时遇到问题。例如:

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;

仍然没有运气。

PLPGSQL 中的

PERFORM 语句用于执行没有 return 结果或结果无用的查询。从技术上讲,PLPGSQL 块中的 PERFORM ... 等于普通 SQL 中的 SELECT ...。因此,在您的示例中,您正在尝试执行类似

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; $$;

作为使用execute这一点的补充,这里注意两个重点

  1. 您正在使用 sql 查询(危险!)和
  2. 进行字符串插值
  3. 你必须使用 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; $$;

关于 PERFORM
并且 关于 SQL 注入。

此外,我建议对除了最琐碎的查询字符串之外的任何内容都使用 format(),这样可以让您使用动态 SQL 更轻松。 And the manual does, too:

A cleaner approach is to use format()'s %I specification for table or column names.

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 参数是传递 table 名称的便捷替代方法,但连接新标识符可能很棘手 - 正如最近的相关案例所示: