如何从命令行将变量传递给 PL/pgSQL 代码?

Łuk*_*zka 5 postgresql psql plpgsql string-manipulation

我正在从命令行运行 psql 脚本,其中的变量类似于:

psql ...... -v now_utc=$NOW_UTC 
Run Code Online (Sandbox Code Playgroud)

然后我想在我的脚本中使用它,例如:

$$
DECLARE
   _now_date timestamp := :now_utc;
BEGIN
  -- do something
END
$$
Run Code Online (Sandbox Code Playgroud)

但我得到了一个错误,如:

syntax error at or near ':'
Run Code Online (Sandbox Code Playgroud)

一旦我从:now_utc变为now(),脚本就很好,它就像一个魅力。

问题是如何将变量从命令行传递给 PL/pgSQL 代码?

Erw*_*ter 10

psql的手册:

变量插值不会在带引号的 SQL 文字和标识符内执行。

DO语句(或函数)的主体带引号的文字 - 在本例中是美元引号,但都是一样的:

$$
DECLARE
   _now_date timestamp := :now_utc;
BEGIN
   -- do something
END
$$
Run Code Online (Sandbox Code Playgroud)

要启用 SQL 插值,请在字符串外部进行插值,然后连接 - 这很快就会变得乏味......

使其更简单的一种方法是让 Postgres 进行字符串处理,然后使用 执行结果\gexec。从 psql 调用:

SELECT format($$
DO
$do$
DECLARE
   _now_date timestamp := %L;
BEGIN
  RAISE NOTICE '%%', _now_date;
END
$do$;
$$, :'now_utc')\gexec
Run Code Online (Sandbox Code Playgroud)
$$
DECLARE
   _now_date timestamp := :now_utc;
BEGIN
   -- do something
END
$$
Run Code Online (Sandbox Code Playgroud)

%内部有特殊含义format(),用于%%获取%字符串中的单个值。)

:'now_utc'另请注意获取带引号的字符串的语法。

要仅使用当前 UTC 时间戳,您不需要所有这些复杂操作:

DO
$do$
DECLARE
   _now_date timestamp := now() AT TIME ZONE 'UTC';
BEGIN
  RAISE NOTICE '%', _now_date;
END
$do$;
Run Code Online (Sandbox Code Playgroud)

DO语句并不意味着带有参数。创建(临时)函数并在调用中将值作为函数参数传递会更简单:

CREATE FUNCTION pg_temp.foo(_now_date timestamp)
  RETURNS void
  LANGUAGE plpgsql AS
$func$
BEGIN
  RAISE NOTICE '%', _now_date;
END
$func$;
Run Code Online (Sandbox Code Playgroud)

在 psql 中使用 SQL 插值调用:

SELECT pg_temp.foo(:'now_utc');
Run Code Online (Sandbox Code Playgroud)

最后,为了传递,您还可以(ab)使用 Postgres “自定义选项”(作为会话变量):

SET myvars.now_date TO :'now_utc';

DO
$do$
BEGIN
  RAISE NOTICE '%', current_setting('myvars.now_date', true);
END
$do$;
Run Code Online (Sandbox Code Playgroud)

请注意,该值存储为text,您可能需要进行转换。

细节:

但是你可能会跳过 psql 变量并直接在 Postgres 中设置选项......