PostgreSQL函数中是否存在psql变量的转义语法?

时间:2010-07-15 20:22:27

标签: sql security scripting postgresql

我编写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;

:用户

失败

据我所知,psql变量是一个普通的宏替换功能,但它不处理函数体。 这种情况有没有逃避语法?围绕:user $$ 一起使用有关替换,但psql在 $$ 处失败。

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

3 个答案:

答案 0 :(得分:8)

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

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

无论如何,这没关系。还有另一种方法(如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;

关于此功能的说明:

  1. EXECUTE使用我们的过程参数在每次调用时生成一个适当的GRANT。名为“Executing Dynamic Commands”的PG手册部分详细解释了EXECUTE
  2. 过程声明user的声明必须加双引号。双引号强制它被解释为标识符。
  3. 一旦定义了这样的函数,就可以使用插值的PSQL变量来调用它。这是一个大纲。

    1. 运行psql --variable user="'whoever'" --file=myscript.sql。用户名周围需要单引号!
    2. 在myscript.sql中,定义如上所述的函数。
    3. 在myscript.sql中,放置select foo(:user);。这是我们依赖于user值的单引号的地方。
    4. 虽然这似乎有效,但它让我感到非常沮丧。我认为SET变量是用于运行时配置的。在SET中携带数据似乎很奇怪。

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

答案 1 :(得分:4)

你可以使用Dan LaRocque所描述的方法来使它成为一种黑客工作(不是对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()使得你不必保证双引号,它处理所有这些。)

但是然后使用像Perl,Python,Ruby等真正的脚本语言,它有一个Postgres驱动程序来调用带参数的函数。

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

答案 2 :(得分:0)

实际上,在更现代的 psql 版本中,有一种方法可以在函数体中使用 psql 变量,但它很复杂。

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

select format($$
begin
    grant select on my_table to group %I;
end;
$$, :'user')
as function_body \gset

注意尾部的 \gset 命令,它将结果字符串存储在变量 function_body 中。 第二步,我们现在可以创建最终的函数:

create function foo() returns void as
:'function_body'
language plpgsql;