在两个表中的触发器函数中选择

Léo*_* 준영 6 postgresql trigger datatypes plpgsql serialization

我在 x86_64-unknown-linux-gnu 上的 PostgreSQL 9.4.3 中的表和触发器,由 gcc (Debian 4.9.2-10) 4.9.2,64 位编译:

CREATE TABLE measurements (
        measurement_id SERIAL PRIMARY KEY NOT NULL,
        measurement_size_in_bytes INTEGER NOT NULL
    );    

CREATE TABLE file_headers (
        header_id SERIAL PRIMARY KEY NOT NULL, 
        measurement_id INTEGER NOT NULL, 
        file_header_index_start INTEGER,
        file_header_index_end INTEGER
    );

CREATE TRIGGER measurement_ids AFTER INSERT 
        ON measurements FOR EACH ROW 
        EXECUTE PROCEDURE ins_function('SELECT measurement_id FROM measurements 
        ORDER BY measurement_id desc limit 1;', 1, 666 ); 
Run Code Online (Sandbox Code Playgroud)

在那里我假设 SELECT 的数据类型自 SERIAL 以来是 INTEGER 但它显然是错误的,因为我从这个启动触发器的命令中收到错误消息:

INSERT INTO measurements (measurement_size_in_bytes) VALUES (888);` 
Run Code Online (Sandbox Code Playgroud)
ERROR:  invalid input syntax for integer: "SELECT measurement_id FROM measurements ORDER BY measurement_id desc limit 1;"
CONTEXT:  PL/pgSQL function ins_function() line 10 at assignment
Run Code Online (Sandbox Code Playgroud)

编辑

ins_function() 并根据@a_horse_with_no_name 和@Joishi 的评论进行编辑:

CREATE OR REPLACE FUNCTION ins_function() RETURNS TRIGGER AS $$
    --
    -- Perform AFTER INSERT operation on file_header by creating rows with new.measurement_id, new.file_header_index_start and new.file_header_index_end.
    --
DECLARE
    measurement_id              INTEGER;
    file_header_index_start     INTEGER;
    file_header_index_end       INTEGER; 
BEGIN     

    SELECT a.measurement_id INTO measurement_id from measurements a ORDER BY measurement_id desc limit 1;
    file_header_index_start := TG_ARGV[0];
    file_header_index_end := TG_ARGV[1]; 

    IF TG_OP = 'INSERT' THEN
        INSERT INTO file_headers (measurement_id, file_header_index_start, file_header_index_end)
        VALUES (measurement_id, file_header_index_start, file_header_index_end); 
        RETURN NEW; 
    END IF;

    RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;

--
-- Function and trigger on INSERT. 
--
CREATE TRIGGER measurement_ids AFTER INSERT 
    ON measurements FOR EACH ROW EXECUTE PROCEDURE ins_function(1, 666); 
Run Code Online (Sandbox Code Playgroud)

我现在没有错误,但输出错误:在表测量中成功时没有INSERT在表中看到。file_headers

@ErwinBrandstetter 答案的输出

所以我开始考虑从 TEXT 转换为 INT 但这应该是非常基本的操作,因为它TG_ARGV[]是一种文本数据类型。一种不成功的尝试是format('SELECT $1.%I', TG_ARGV[0])。该regclass会的工作你描述这里insaft_function()

SELECT NEW.measurement_id, TG_ARGV[0]::regclass, TG_ARGV[1]::regclass;
Run Code Online (Sandbox Code Playgroud)

为什么没有成功的插入到表中file_headers

Erw*_*ter 7

您有一个未解决的命名冲突

您必须在未声明的情况下使用旧版本的 Postgres。或者您正在使用非默认配置设置进行操作。

在这里声明一个名为 的变量measurement_id

DECLARE
    measurement_id              INTEGER;
Run Code Online (Sandbox Code Playgroud)

一开始就使用不明确的变量名是一种愚蠢的做法。如果你无论如何都这样做,你必须知道你在做什么。我习惯于在变量名前面加上下划线,这与列名不同,比如_measurement_id.

后面的SELECT说法有歧义:

ORDER BY measurement_id
Run Code Online (Sandbox Code Playgroud)

这将在具有默认配置的现代 PostgreSQL 中引发错误消息。根据文档

默认情况下,如果 SQL 语句中的名称可以引用变量或表列,PL/pgSQL 将报告错误。

和:

要在系统范围内更改此行为,请将配置参数设置plpgsql.variable_conflict为 error、use_variable 或 use_column 之一(其中 error 是出厂默认值)。此参数影响 PL/pgSQL 函数中语句的后续编译,但不影响当前会话中已编译的语句。因为更改此设置会导致 PL/pgSQL 函数的行为发生意外更改,因此只能由超级用户更改。

在 9.0 之前的 Postgres 中,这将被解析为变量. 根据文档

在这种情况下,您可以指定 PL/pgSQL 应将不明确的引用解析为变量(这与PostgreSQL 9.0 之前的PL/pgSQL 的行为兼容)

大胆强调我的。
这将导致任意结果,因为排序顺序现在是不确定的。

审计职能

CREATE OR REPLACE FUNCTION insaft_function()
   RETURNS TRIGGER AS
$func$
DECLARE
   _measurement_id          integer;
   _file_header_index_start integer := TG_ARGV[0]::int;
   _file_header_index_end   integer := TG_ARGV[1]::int; 
BEGIN     

   SELECT a.measurement_id   INTO _measurement_id
   FROM   measurements a
   ORDER  BY a.measurement_id DESC  -- you had ambiguity here!
   LIMIT  1;

   IF TG_OP = 'INSERT' THEN  -- noise if only used in AFTER INSERT trigger
      INSERT INTO file_headers (measurement_id, file_header_index_start
                                              , file_header_index_end)
      VALUES (_measurement_id, _file_header_index_start, _file_header_index_end); 
   END IF;

   RETURN NULL; -- result is ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

请注意我如何命名它insaft_function(),因为这仅用于AFTER INSERT触发器中。

扳机:

CREATE TRIGGER insaft_measurement_ids
AFTER INSERT ON measurements
FOR EACH ROW EXECUTE PROCEDURE insaft_function(1, 666);
Run Code Online (Sandbox Code Playgroud)

但是对于提供的设置,您可以从根本上简化功能:

CREATE OR REPLACE FUNCTION insaft_function()
   RETURNS TRIGGER AS
$func$
BEGIN     
   INSERT INTO file_headers (measurement_id, file_header_index_start
                                           , file_header_index_end)
   VALUES (NEW.measurement_id, TG_ARGV[0]::int, TG_ARGV[1]::int);

   RETURN NULL;  -- result ignored since this is an AFTER trigger
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)