仅给出字段名称,如何访问NEW或OLD字段?


8

我正在写一个验证触发器。触发器必须验证数组的总和等于另一个字段。由于我有很多这种验证的实例,因此我想编写一个过程并创建多个触发器,每个触发器都有一组不同的要检查的字段。

例如,我具有以下架构:

CREATE TABLE daily_reports(
     start_on date
   , show_id uuid
   , primary key(start_on, show_id)

     -- _graph are hourly values, while _count is total for the report
   , impressions_count bigint not null
   , impressions_graph bigint[] not null

   -- interactions_count, interactions_graph
   -- twitter_interactions_count, twitter_interactions_graph
);

验证必须确认impressions_count = sum(impressions_graph)

我被卡住是因为我不知道如何NEW从plpgsql中动态访问字段:

CREATE FUNCTION validate_sum_of_array_equals_other() RETURNS TRIGGER AS $$
DECLARE
  total bigint;
  array_sum bigint;
BEGIN
  -- TG_NARGS = 2
  -- TG_ARGV[0] = 'impressions_count'
  -- TG_ARGV[1] = 'impressions_graph'

  -- How to access impressions_count and impressions_graph from NEW?
  RETURN NEW;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER validate_daily_reports_impressions
ON daily_reports BEFORE INSERT OR UPDATE
FOR EACH ROW EXECUTE
  validate_sum_of_array_equals_other('impressions_count', 'impressions_graph');

我试图执行动态命令这样做EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0],但是PL / pgSQL的抱怨,新的是一个未知的关系。

我专门针对PostgreSQL 9.1。


跨发布到PostgreSQL的普通邮件列表postgresql.org/message-id/...
弗朗索瓦博索莱伊

AFAIK目前动态访问字段的唯一方法NEW是使用hstore(NEW)然后hstore以列名作为键的值访问字段。太烂了,因为然后它们都被投射到了text,如果您想以它们的原始类型来使用它们,则必须将它们抛回去。或者,您可以使用另一种过程语言(如PL / Python)编写触发器,该触发器对动态记录访问具有更好的支持。
Craig Ringer 2014年

@CraigRinger:嗯,不。你今天太悲观了。动态SQL有一种方法。
Erwin Brandstetter 2014年

Answers:


14

实际上,由于NEW是定义良好的复合类型,因此您可以使用简单和简单的属性表示法访问任何列。SQL本身不允许动态标识符(表或列名等)。但是您可以在PL / pgSQL函数中使用动态SQLEXECUTE

演示版

CREATE OR REPLACE FUNCTION trg_demo1()
  RETURNS TRIGGER AS
$func$
DECLARE
   _col_value text;
   _col_name  text := quote_ident(TG_ARGV[0]);  -- escape identifier
BEGIN
   EXECUTE format('SELECT ($1).%s::text', _col_name)
   USING NEW
   INTO  _col_value;

   -- do something with _col_value ...

   RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', _col_name, _col_value;

   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

强制转换text为可选。使用它,因为它可以通用。如果您知道类型,则可以不进行强制转换而工作...

format()与一起使用%s,因为此时标识符已被转义。
否则,请使用format()with %I防止SQL注入。

另外,在Postgres 9.3或更高版本中,您可以NEW使用to_json()和将列作为键访问来转换为JSON :

CREATE OR REPLACE FUNCTION trg_demo2()
  RETURNS TRIGGER AS
$func$
DECLARE
   _col_value text := to_json(NEW) ->> TG_ARGV[0];  -- no need to escape identifier
BEGIN
   RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', TG_ARGV[0], _col_value;
   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

由于列名未连接到SQL字符串中,因此无法进行SQL注入,并且无需转义该名称。

db <> 在这里摆弄(用EXCEPTION而不是NOTICE使效果可见)。

有关:

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.