【问题标题】:Dynamic function invocation in trigger触发器中的动态函数调用
【发布时间】:2015-04-30 10:01:12
【问题描述】:

在我的用例中,我需要能够在插入表后使用静态参数执行动态(预定义)函数。

逻辑上我在想:

  1. 在自己的表中定义函数调用(即名称和静态参数)。
  2. 将这些静态函数调用定义与另一个表中的记录相关联(插入其中将触发动态函数调用)。
  3. 插入时使用触发器查询静态函数定义表,并使用获取的静态参数执行获取的函数。

这是我目前的成果:

动态调用的可用函数池

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();

插入导致动态函数调用的表

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 表中的行引用的函数确实存在并且定义的参数是有效的(或者至少可以被强制为有效)。

我正在使用 PostgreSQL 9.4.1

【问题讨论】:

  • 一如既往请提供您的 Postgres 版本。
  • 对不起,我的错误-已添加。

标签: postgresql function database-design plpgsql check-constraints


【解决方案1】:

这里是一个简单的CHECK 约束而不是触发器的解决方案:

CREATE TABLE func (
  func_id serial PRIMARY KEY
, func text NOT NULL
, args text NOT NULL
, CHECK ((func || '(' || args || ')')::regprocedure IS NOT NULL)
);

CHECK 约束比任何可能的触发解决方案都更简单、更快且更可靠。此变体适用于任何现代 Postgres 版本。

在约束可以完成评估之前,由于无效函数签名,转换为 regprocedure 失败 - 这同样可靠。这反映在相应的错误消息中。

在 Postgres 9.4+ 中,请使用新的 to_regprocedure() 而不是强制转换,这不会引发异常。相反,您会从 CHECK 约束中获得异常。更多(最后一章):

作品:

INSERT INTO func(func, args) VALUES ('substring','text, int');

异常失败:

INSERT INTO func(func, args) VALUES ('nonexistant','text, int');

SQL Fiddle.

我还会考虑在(func, args) 上设置UNIQUE 约束。请注意,同一个args 可以有多个有效的文本表示。这是发现隐藏重复项的快速检查:

SELECT func, string_to_array(args, ', ')::regtype[], count(*)
FROM   func
GROUP  BY 1, 2
HAVING count(*) > 1;

您不能在唯一索引中使用此表达式,因为转换为 regtype 的不是 IMMUTABLE。你将不得不玩花样......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-19
    • 2013-09-19
    • 1970-01-01
    • 2021-06-09
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多