PostgreSQL 函数中的 psql 变量是不是有任何转义语法?

Posted

技术标签:

【中文标题】PostgreSQL 函数中的 psql 变量是不是有任何转义语法?【英文标题】:Are there any escaping syntax for psql variable inside PostgreSQL functions?PostgreSQL 函数中的 psql 变量是否有任何转义语法? 【发布时间】:2011-03-16 15:21:33 【问题描述】:

我编写 PSQL 脚本并使用变量(对于 psql --variable key=value 命令行语法)。

这对于像 select * from :key 这样的***范围非常有效,但我使用脚本创建函数并且需要在其中的变量值。

所以,语法像

create function foo() returns void as
$$
declare
begin
    grant select on my_table to group :user;
end;
$$
language plpgsql;

:user 处失败。

据我了解 psql 变量是一个普通的宏替换功能,但它不处理函数体。 这种情况是否有任何转义语法?用 $$ 包围 :user 可以解决替换问题,但 psql 在 $$ 处失败。

除了独立的宏处理(sed、awk 等)之外,还有其他方法吗?

【问题讨论】:

你能让函数 foo 接受一个参数并使用它吗? 【参考方案1】:

PSQL SET 变量不会插入美元引用的字符串中。我不确定这一点,但我认为在那里打开SET 变量插值没有任何逃避或其他诡计。

有人可能会认为您可以将未引用的:user 插入两个以美元引用的 PL/pgSQL 段之间以获得所需的效果。但这似乎不起作用......我认为语法需要单个字符串而不是连接字符串的表达式。可能是误会了。

无论如何,这并不重要。还有另一种方法(如 Pasco 所述):编写存储过程以接受 PL/pgSQL 参数。这就是它的样子。

CREATE OR REPLACE FUNCTION foo("user" TEXT) RETURNS void AS
$$
BEGIN
        EXECUTE 'GRANT SELECT ON my_table TO GROUP ' || quote_ident(user);
END;    
$$ LANGUAGE plpgsql;

关于此功能的说明:

    EXECUTE 在每次调用时使用我们的过程参数生成一个适当的GRANT。名为“Executing Dynamic Commands”的 PG 手册部分详细解释了EXECUTE。 过程参数user 的声明必须用双引号引起来。双引号强制将其解释为标识符。

一旦定义了这样的函数,就可以使用插值的 PSQL 变量来调用它。这是一个大纲。

    运行psql --variable user="'whoever'" --file=myscript.sql。用户名周围需要单引号! 在 myscript.sql 中,定义类似上面的函数。 在 myscript.sql 中,输入 select foo(:user);。这就是我们依赖那些在user 值中添加的单引号的地方。

虽然这似乎可行,但它让我觉得相当松鼠。我认为SET 变量用于运行时配置。在SET 中携带数据似乎很奇怪。

编辑:这是使用SET 变量的具体原因。来自手册页:“这些分配是在启动的早期阶段完成的,因此为内部目的保留的变量可能会在以后被覆盖。”如果 Postgres 决定使用名为 user 的变量(或任何你选择的变量),它可能会用你从未想过的东西覆盖你的脚本参数。事实上,psql 已经为自己使用了USER——这只是因为SET 是区分大小写的。这几乎从一开始就破坏了一切!

【讨论】:

你应该使用 EXECUTE 'GRANT SELECT ON my_table TO GROUP ' ||报价标识(用户);我相信。 @rfusca 是的,消毒它不会有什么坏处!【参考方案2】:

你可以使用 Dan LaRocque 所描述的方法让它变得有点像 hack'ishly 的工作(而不是敲击 Dan)——但是 psql 并不是你做这种工作的真正朋友(假设这种工作是脚本和不是一次性的东西)。如果您有一个函数,请将其重写为采用如下参数:

create function foo(v_user text) returns void as
$$
begin
    execute 'grant select on my_table to group '||quote_ident(v_user);
end;
$$
language plpgsql;

(quote_ident() 使您不必保证双引号,它可以处理所有这些。)

然后使用具有 Postgres 驱动程序的真正脚本语言(如 Perl、Python、Ruby 等)来使用参数调用您的函数。

Psql 有它的位置,我只是不确定是不是它。

【讨论】:

我同意像这样使用 psql SET 可能存在维护风险。当我写我的答案时,我也分享了你的疑虑。最后有一小段给出了一个具体的原因为什么它是有风险的。 @Dan LaRocque:是的,这就是为什么我觉得没有必要重申这一点,以及为什么我什至赞成你的回答。 :)【参考方案3】:

实际上,在更现代的psql 版本中有一种方法。

由于函数体 has to be a string constant 和 psql does not perform variable interpolation within string constants 我们不得不在单独的第一步中组装函数体:

select format($gexec$
create function foo() returns void as 
$$
begin
    grant select on my_table to group %I;
end;
$$
language plpgsql;
$gexec$, :'user') \gexec

结尾的\gexec 命令

使用格式函数执行“外部”查询以生成所需的查询字符串,然后 将生成的查询字符串作为查询执行。

【讨论】:

以上是关于PostgreSQL 函数中的 psql 变量是不是有任何转义语法?的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL中怎么把数值型转化成时间

PostgreSQL。日志文件中的慢查询在 psql 中很快

postgresql help

PostgreSQL,plpython3u函数。 os.getenv找不到环境变量

PostgreSQL操作-psql基本命令

psql工具使用