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)
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
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
所以我开始考虑从 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
?
您有一个未解决的命名冲突。
您必须在未声明的情况下使用旧版本的 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)