通过作为触发器arg传递的列名访问触发器中的记录字段

时间:2013-12-05 12:40:59

标签: sql postgresql triggers hstore

我有一组表,它们使用一个鉴别器列来区分实体类型。这些表中的某些表描述了已定义的实体组之间的关系,或者包含特定类型实体的更多信息。我想对表中的数据进行完整性检查。最初这些是作为支票编写的,但postgres不能很好地处理备份中的表排序,所以我想把检查重写为触发器,这样我就可以在恢复数据库时关闭触发器。

由于实体的实际列名将从表更改为表,并且我想以DRY方式执行,我尝试编写单个触发器函数,该实体将实体的id作为参数并验证它是正确的类型。

我正在尝试调用带参数的函数触发器

c.is_earth_based_measurement_tg(bigint).

实际触发器编写如下:

CREATE TRIGGER earth_based_measurement_locations_tg1
  BEFORE INSERT OR UPDATE
  ON measurements.earth_based_measurement_locations
  FOR EACH ROW
    EXECUTE PROCEDURE c.is_earth_based_measurement_tg(NEW.fk_measurement);

当我尝试保存触发器时,我得到'错误:语法错误在或接近“。”'指的是NEW.fk_measurement。编写此触发器的正确​​方法是什么?

感谢。

2 个答案:

答案 0 :(得分:5)

您不需要传递NEW.anything作为参数;它会自动作为变量NEW提供给触发器函数。因此,只要NEW.fk_measurementtg_opINSERTUPDATE未设置为NEW,您的触发器就可以从其中的任何位置访问DELETE }或TRUNCATE触发器。)

触发器可以带参数,但它们在TG_ARGV中出现(为零323个音符)。它们不需要用于列值,实际上不能引用列值;它们的目的是用于参数化触发器,具有可选功能的触发器等等。

在重新阅读您的问题时,您需要一个感兴趣的列名参数。不幸的是,在NEW元组中通过动态名称访问列非常尴尬。

您可以将触发器创建为:

CREATE TRIGGER earth_based_measurement_locations_tg1
  BEFORE INSERT OR UPDATE
  ON measurements.earth_based_measurement_locations
  FOR EACH ROW
    EXECUTE PROCEDURE c.is_earth_based_measurement_tg('fk_measurement');

即。将colname作为字符串文字传递。

触发程序将定义为:

CREATE OR REPLACE FUNCTION is_earth_based_measurement_tg() RETURNS trigger AS $$
BEGIN
  IF tg_op = 'INSERT' OR tg_op = 'UPDATE' THEN
    PERFORM my_func(hstore(NEW) -> TG_ARGV[0]);
  END IF;
END;
$$ LANGUAGE plpgsql;

...或者您要对从列中提取的值进行的任何其他操作。

无法通过当前PostgreSQL版本中的动态列名设置NEW中列的值(直到并包括9.4)。

请注意,我们将NEW元组转换为hstore,然后按列名取消引用它。

答案 1 :(得分:1)

不能使用参数声明触发器函数。 OLDNEW和几个触发器相关变量默认使用所谓的TG_ARGV传递,并且可以在触发器函数内访问,无需额外的步骤。您可以查看official documentation以获取详细信息,但通常看起来应与此类似:

CREATE OR REPLACE FUNCTION is_earth_based_measurement_tg()
RETURNS TRIGGER AS $earth_based_measurement_locations_tg1$
    BEGIN
       IF (NEW.fk_measurement = 'foo') THEN
           -- Do something
       END IF;
    END;
$earth_based_measurement_locations_tg1$ LANGUAGE plpgsql;