3

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

脚本.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 标志?

执行日志:

执行日志

4

1 回答 1

2

函数的主体是带引号的文字。美元报价,但都一样。手册:

变量插值不会在引用的 SQL 文字和标识符中执行。

有多种解决方法。由于您在 psql 中运行脚本,我建议您让 Postgres 为您执行连接format()并执行 with \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()with %I更聪明一点,只在有影响的地方添加双引号。这避免了函数定义中的噪音。看:

于 2020-05-14T15:16:54.633 回答