"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这一点的补充,这里注意两个重点
- 您正在使用 sql 查询(危险!)和
进行字符串插值
- 你必须使用 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 名称的便捷替代方法,但连接新标识符可能很棘手 - 正如最近的相关案例所示:
我在 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;
仍然没有运气。
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这一点的补充,这里注意两个重点
- 您正在使用 sql 查询(危险!)和 进行字符串插值
- 你必须使用 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
。
并且
此外,我建议对除了最琐碎的查询字符串之外的任何内容都使用 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 名称的便捷替代方法,但连接新标识符可能很棘手 - 正如最近的相关案例所示: