我想为数据库中的许多表生成代码,并在我准备编写第三个“获取表X的代码”的第三个实现时停止重构我的解决方案。
我的代码是:
-- Tenants receive a code that's composed of a portion of their subdomain and a unique number.
-- This number comes from this sequence.
CREATE SEQUENCE tenant_codes_seq MAXVALUE 9999 NO CYCLE;
CREATE TABLE tenants (
subdomain varchar(36) NOT NULL UNIQUE
, tenant_code char(8) NOT NULL UNIQUE
, PRIMARY KEY (tenant_code)
);
-- This function expects four parameters:
-- 1. The column that's receiving the generated code (RECEIVING_COLUMN_NAME)
-- 2. The column that's used to salt the code (SALT_COLUMN_NAME)
-- 3. The number of characters to use from the salt column (SALT_LENGTH)
-- 4. The sequence name, but defaults to RECEIVING_COLUMN_NAME || 's'
CREATE OR REPLACE FUNCTION generate_table_code() RETURNS trigger AS $$
DECLARE
receiving_column_name text;
salt_column_name text;
salt_length text;
sequence_name text;
BEGIN
receiving_column_name := TG_ARGV[0];
salt_column_name := TG_ARGV[1];
salt_length := TG_ARGV[2];
CASE
WHEN TG_NARGS = 3 THEN
sequence_name := receiving_column_name || 's';
WHEN TG_NARGS = 4 THEN
sequence_name := TG_ARGV[3];
ELSE
RAISE EXCEPTION '3 or 4 arguments expected, received %', TG_NARGS;
END CASE;
-- The intent is to return ABC-0001 when salt_column contains 'ABC'
EXECUTE 'rpad(substr(' ||
quote_ident(salt_column_name) ||
', 1, 4), 4, ' ||
quote_literal('-') ||
') || lpad(nextval(' ||
quote_literal(sequence_name) ||
')::text, ' ||
quote_literal(salt_length) ||
', ' ||
quote_literal('0') ||
')'
INTO STRICT NEW;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER generate_tenant_code_trig
BEFORE INSERT ON tenants FOR EACH ROW
EXECUTE PROCEDURE generate_table_code('tenant_code', 'subdomain', 4);
如何分配到NEW.tenant_code,NEW.user_code或NEW.table_whatever_code? p>
运行一些测试会产生正确的“语句”,但我似乎无法正确分配:
INSERT INTO tenants(subdomain) VALUES ('abc')
CREATE TABLE
ERROR: syntax error at or near "NEW"
LINE 1: NEW.tenant_code := rpad(substr(subdomain, 1, 4), 4, '-') || ...
^
QUERY: NEW.tenant_code := rpad(substr(subdomain, 1, 4), 4, '-') || lpad(nextval('tenant_codes')::text, '4', '0'::text)
CONTEXT: PL/pgSQL function "generate_table_code" line 20 at EXECUTE statement
答案 0 :(得分:4)
我非常热衷于被证明是错误的(我偶尔也需要这个),但最好我知道,使用变量引用列名是你真正需要使用PL / C触发器的情况之一比PL / PgSQL触发器。您可以在contrib / spi和PGXN上找到此类触发器的示例。
或者,一致地命名您的列,以便能够直接引用它们,例如NEW.tenant_code
。
就个人而言,我通常最终会编写一个创建触发器的函数:
create function create_tg_stuff(_table regclass, _args[] text[])
returns void as $$
begin
-- explore pg_catalog a bit
execute $x$
create function $x$ || quote_ident(_table || '_tg_stuff') || $x$()
returns trigger as $t$
begin
-- more stuff
return new;
end;
$t$ language plpgsql;
$x$;
end;
$$ language plpgsql;
答案 1 :(得分:0)
NEW
是RECORD
类型,因此您无法分配给该AFAIK。
要设置列的值,请指定为NEW.column
,例如:
NEW.tenant_code := (SELECT some_calculation);
也许你的设计过于复杂; PL / SQL是一种非常有限的语言 - 尽量使代码尽可能简单