触发器函数中的动态SELECT给出语法错误

时间:2015-05-28 06:02:01

标签: postgresql

在下面的代码中调试一些语法错误需要帮助。代码如下所示,在Insert,Select等关键字附近只有几个语法错误

CREATE OR REPLACE FUNCTION audit_temp() RETURNS TRIGGER LANGUAGE plpgsql AS $BODY$
DECLARE
  ri RECORD; 
  oldValue TEXT;
  newValue TEXT;
  isColumnSignificant BOOLEAN;
  isValueModified BOOLEAN;
BEGIN
  IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
    NEW.record_modified_ = clock_timestamp(); 

    FOR ri IN
      -- Fetch a ResultSet listing columns defined for this trigger's table.
      SELECT ordinal_position, column_name, data_type
      FROM information_schema.columns
      WHERE table_schema = quote_ident(TG_TABLE_SCHEMA)
        AND table_name = quote_ident(TG_TABLE_NAME)
      ORDER BY ordinal_position
    LOOP
      -- For each column in this trigger's table, copy the OLD & NEW values into respective variables.
      -- NEW value
      EXECUTE 'SELECT ($1).' || ri.column_name || '::text' INTO newValue USING NEW;
      -- OLD value
      IF TG_OP = 'INSERT' THEN   -- If operation is an INSERT, we have no OLD value, so use an empty string.
        oldValue := ''::varchar;
      ELSE   -- Else operation is an UPDATE, so capture the OLD value.
        EXECUTE 'SELECT ($1).' || ri.column_name || '::text' INTO oldValue USING OLD;
      END IF;

      isColumnSignificant := (position( '_x_' in ri.column_name ) < 1) AND
                             (ri.column_name <> 'pkey_') AND
                             (ri.column_name <> 'record_modified_');
      IF isColumnSignificant THEN
        isValueModified := oldValue <> newValue;  -- If this nthField in the table was modified, make history.
        IF isValueModified THEN
          /*RAISE NOTICE E'Inserting history_ row for INSERT or UPDATE.\n';*/
          INSERT INTO audit_temp( operation_, table_oid_, table_name_, uuid_, column_name_, ordinal_position_of_column_, old_value_, new_value_ )
          VALUES ( TG_OP, TG_RELID, TG_TABLE_NAME, NEW.pkey_, ri.column_name::VARCHAR, ri.ordinal_position, oldValue::VARCHAR, newValue::VARCHAR);
        END IF;
      END IF;
    END LOOP;

    RETURN NEW;
  ELSIF TG_OP = 'DELETE' THEN
    /*RAISE NOTICE E'Inserting history_ row for DELETE.\n';*/
    --    Similar to INSERT above, but refers to OLD instead of NEW, and passes empty values for last 4 fields.
    INSERT INTO audit_temp ( operation_, table_oid_, table_name_, uuid_, column_name_, ordinal_position_of_column_, old_value_, new_value_ )
    VALUES ( TG_OP, TG_RELID, TG_TABLE_NAME, OLD.pkey_, ''::VARCHAR, 0, ''::VARCHAR, ''::VARCHAR );
    RETURN OLD;
  END IF;
  /* Should never reach this point. Branching in code above should always reach a call to RETURN. */
  RAISE EXCEPTION 'Unexpectedly reached the bottom of this function without calling RETURN.';
END; $BODY$;

错误如下&amp;主要围绕选择插入关键字:

   >[Error] Script lines: 1-42 -------------------------
     ERROR: syntax error at or near "SELECT"

任何建议?????

1 个答案:

答案 0 :(得分:0)

语法错误

违规陈述就是这个(和其他人一样):

EXECUTE 'SELECT ($1).' || ri.column_name || '::text' INTO newValue USING NEW;

根据documentation 参数,符号只能用于数据值 - 如果要使用动态确定的表名或列名,则必须将它们以文本形式插入命令字符串。所以解决方案是:

EXECUTE 'SELECT (' || NEW || ').' || ri.column_name || '::text' INTO newValue;

改进

您可以对触发功能进行一些改进,以使其更快更有效:

  • 在填充isColumnSignficantoldValue之前,您应该先检查newValue
  • 更新时,您不必记录OLD值:首次插入或稍后更新数据时,它们已在审计表中。
  • 删除时,不要存储空字符串和0,只需将列保留为NULL。