触发器中的动态函数调用
Dynamic function invocation in trigger
在我的用例中,我需要能够在插入 table 后使用静态参数执行动态(预定义)函数。
逻辑上我在想:
- 在它们自己的 table.
中定义函数调用(即名称和静态参数)
- 将这些静态函数调用定义与另一个 table 中的记录相关联(插入其中将触发动态函数调用)。
- 插入后使用触发器查询静态函数定义table并使用获得的静态参数执行获得的函数。
这是我到目前为止的结果:
要动态调用的可用函数池
create function f1(num int) returns boolean as $$
-- ...
$$ language plpgsql;
create function f2(name text, age int) returns boolean as $$
-- ...
$$ language plpgsql;
create function f3(first_name text, last_name text) returns boolean as $$
-- ...
$$ language plpgsql;
函数调用
create table function_invocations(
id integer not null,
name text not null,
args text not null, -- (not sure if this should be an array)
primary key(id)
);
create function verify_function_exists() returns trigger as $$
-- query information_schema to verify there is
-- a function with specified name and that
-- specified args satisfy function's
-- signature.
$$ language plpgsql;
create trigger function_exists_trig
before insert on function_invocations
for each row
execute procedure verify_function_exists();
Table 其插入导致动态函数调用
create table my_data(
id integer not null,
function_invocation_id integer not null,
-- etc.
primary key(id),
foreign key(function_invocation_id) references function_invocations(id)
);
create function exec_dynamic_function() returns trigger as $$
-- retrieve the function name and args from
-- function_definitions and execute the
-- function specified by `name` with the
-- provided `args`.
$$ language plpgsql;
create trigger function_invocations_trig
after update on my_data
for each row
execute procedure exec_dynamic_function();
这是完成任务的正确方法吗?来自 JS 背景的我可能会以错误的方式思考它,即
var api = {
my_func: function (age, name) {
console.log('%d %s', age, name);
}
};
var fn = 'my_func';
var args = [50, 'john'];
api[fn].apply(api, args);
我主要关心的是如何确保 function_invocations
table 中的行引用的函数实际存在并且定义的参数有效(或者至少可以强制为有效)。
我正在使用 PostgreSQL 9.4.1。
这里是使用简单 CHECK
约束而不是触发器的解决方案:
CREATE TABLE func (
func_id serial PRIMARY KEY
, func text NOT NULL
, args text NOT NULL
, <b>CHECK ((func || '(' || args || ')')::regprocedure IS NOT NULL)</b>
);
CHECK
约束比任何可能的触发器解决方案都更简单、更快速、更可靠。此变体适用于任何现代 Postgres 版本。
在约束可以完成评估之前,由于无效函数签名而转换为 regprocedure
失败 - 这同样可靠。这反映在相应的错误消息中。
在 Postgres 9.4+ 中,宁愿使用新的 to_regprocedure()
而不是强制转换,这不会引发异常。相反,您会从 CHECK
约束中获得异常。更多(最后一章):
How to check if a table exists in a given schema
DROP FUNCTION without knowing the number/type of parameters?
作品:
INSERT INTO func(func, args) VALUES ('substring','text, int');
异常失败:
INSERT INTO func(func, args) VALUES ('nonexistant','text, int');
我还会考虑对 (func, args)
进行 UNIQUE
约束。请注意,同一个 args
可以有多个有效文本表示。这是一个快速检查以发现隐藏的重复项:
SELECT func, string_to_array(args, ', ')::regtype[], count(*)
FROM func
GROUP BY 1, 2
HAVING count(*) > 1;
您不能在唯一索引中使用此表达式,因为转换为 regtype
不是 IMMUTABLE
。你将不得不玩把戏......
在我的用例中,我需要能够在插入 table 后使用静态参数执行动态(预定义)函数。
逻辑上我在想:
- 在它们自己的 table. 中定义函数调用(即名称和静态参数)
- 将这些静态函数调用定义与另一个 table 中的记录相关联(插入其中将触发动态函数调用)。
- 插入后使用触发器查询静态函数定义table并使用获得的静态参数执行获得的函数。
这是我到目前为止的结果:
要动态调用的可用函数池
create function f1(num int) returns boolean as $$
-- ...
$$ language plpgsql;
create function f2(name text, age int) returns boolean as $$
-- ...
$$ language plpgsql;
create function f3(first_name text, last_name text) returns boolean as $$
-- ...
$$ language plpgsql;
函数调用
create table function_invocations(
id integer not null,
name text not null,
args text not null, -- (not sure if this should be an array)
primary key(id)
);
create function verify_function_exists() returns trigger as $$
-- query information_schema to verify there is
-- a function with specified name and that
-- specified args satisfy function's
-- signature.
$$ language plpgsql;
create trigger function_exists_trig
before insert on function_invocations
for each row
execute procedure verify_function_exists();
Table 其插入导致动态函数调用
create table my_data(
id integer not null,
function_invocation_id integer not null,
-- etc.
primary key(id),
foreign key(function_invocation_id) references function_invocations(id)
);
create function exec_dynamic_function() returns trigger as $$
-- retrieve the function name and args from
-- function_definitions and execute the
-- function specified by `name` with the
-- provided `args`.
$$ language plpgsql;
create trigger function_invocations_trig
after update on my_data
for each row
execute procedure exec_dynamic_function();
这是完成任务的正确方法吗?来自 JS 背景的我可能会以错误的方式思考它,即
var api = {
my_func: function (age, name) {
console.log('%d %s', age, name);
}
};
var fn = 'my_func';
var args = [50, 'john'];
api[fn].apply(api, args);
我主要关心的是如何确保 function_invocations
table 中的行引用的函数实际存在并且定义的参数有效(或者至少可以强制为有效)。
我正在使用 PostgreSQL 9.4.1。
这里是使用简单 CHECK
约束而不是触发器的解决方案:
CREATE TABLE func (
func_id serial PRIMARY KEY
, func text NOT NULL
, args text NOT NULL
, <b>CHECK ((func || '(' || args || ')')::regprocedure IS NOT NULL)</b>
);
CHECK
约束比任何可能的触发器解决方案都更简单、更快速、更可靠。此变体适用于任何现代 Postgres 版本。
在约束可以完成评估之前,由于无效函数签名而转换为 regprocedure
失败 - 这同样可靠。这反映在相应的错误消息中。
在 Postgres 9.4+ 中,宁愿使用新的 to_regprocedure()
而不是强制转换,这不会引发异常。相反,您会从 CHECK
约束中获得异常。更多(最后一章):
How to check if a table exists in a given schema
DROP FUNCTION without knowing the number/type of parameters?
作品:
INSERT INTO func(func, args) VALUES ('substring','text, int');
异常失败:
INSERT INTO func(func, args) VALUES ('nonexistant','text, int');
我还会考虑对 (func, args)
进行 UNIQUE
约束。请注意,同一个 args
可以有多个有效文本表示。这是一个快速检查以发现隐藏的重复项:
SELECT func, string_to_array(args, ', ')::regtype[], count(*)
FROM func
GROUP BY 1, 2
HAVING count(*) > 1;
您不能在唯一索引中使用此表达式,因为转换为 regtype
不是 IMMUTABLE
。你将不得不玩把戏......