在 Postgres 函数体的 psql 中访问 \set 变量

Access \set variables in psql for Postgres function bodies

我正在创建一个通用的自动化 Postgres 脚本,其中包括 DDL、DML、函数和触发器。在大多数操作中,我已经实现了在 psql 中使用 \set 参数化模式名称,但是在 函数体 的情况下它不起作用。我努力搜索并找到了 dynamic SQL 但这似乎也不起作用。

script.sql

\set schema_name myschema

CREATE SCHEMA IF NOT EXISTS :schema_name;

CREATE table :schema_name.employee (
 id serial,
 name text ,
 dob timestamp,
 leaves_count int,
 CONSTRAINT employee_pk PRIMARY KEY (id)
);

SELECT create_distributed_table(concat(:'schema_name', '.employee'), 'id');


CREATE table :schema_name.leaves (
 id serial,
 reason text,
 ts timestamp,
 employee_id int,
 CONSTRAINT leaves_pk PRIMARY KEY (id)
);

-- STORED PROCEDURE
CREATE OR REPLACE
FUNCTION :schema_name.deduct_leave_count() RETURNS TRIGGER AS $BODY$ BEGIN
UPDATE
    :schema_name.employee s
SET
    leaves_count = s.leaves_count - 1
WHERE
    s.id = NEW.employee_id;
RETURN NEW;
END $BODY$ LANGUAGE plpgsql;

-- TRIGGER
CREATE TRIGGER trigger_deduct_leave_count BEFORE
INSERT
    ON
    :schema_name.leaves FOR EACH ROW EXECUTE PROCEDURE :schema_name.deduct_leave_count();

psql -p 5432 -f script.sql

我在函数中遇到错误,因为它无法读取全局变量集。有没有办法在函数内部访问全局变量或 psql 的 -v 标志?

执行日志:

函数体是一个带引号的文字。美元报价,但都一样。 The manual:

Variable interpolation will not be performed within quoted SQL literals and identifiers.

有多种解决方法。由于您是 运行 psql 中的脚本,我建议您让 Postgres 为您使用 format() 进行连接并使用 \gexec:

执行
-- TRIGGER FUNCTION
SELECT format($$
CREATE OR REPLACE FUNCTION %1$I.deduct_leave_count()
  RETURNS trigger LANGUAGE plpgsql AS
$func$
BEGIN
   UPDATE %1$I.employee s
   SET    leaves_count = s.leaves_count - 1
   WHERE  s.id = NEW.employee_id;

   RETURN NEW;
END
$func$
$$, :'schema_name')\gexec

详细解释:

旁白 1:(触发)函数 不是 过程。相关:

旁白 2:由于模式名称是标识符,您可能需要使用双引号。 psql 支持:

CREATE table :"schema_name".leaves

保留给定的字符串(包括大写)并防止 SQL 注入。

format()%I 更聪明一些,只在有区别的地方添加双引号。这避免了函数定义中的噪声。参见:

  • Are PostgreSQL column names case-sensitive?
  • Define table and column names as arguments in a plpgsql function?