如何强制新记录的列值仅来自 PostgreSQL 中的序列?
How to force new records to have column values ONLY from a sequence in PostgreSQL?
请参阅下面的答案和编辑#1。但是在PetaPoco/Npgsql.
下仍然触发失败
我在这里遗漏了一些基本的东西。我需要确保添加到 table 的 any 记录的顺序为 only。即使 orderno 由插入语句本身提供,这也应该包括在内。也就是说,
insert into returntooffice (chart_recid, returndate, torder, **orderno**) values (14982,'2016-11-09','2017-12-4 00:21:42.553508', **0**);
和
insert into returntooffice (chart_recid, returndate, torder) values (14982,'2016-11-09','2017-12-4 00:21:42.553508');
都应该从 序列中产生下一个 orderno 而不是 0 的 orderno。 也就是说,实际发生的是正在插入提供的 orderno(0)——而不是下一个序列值(8000)。我在这里使用触发器,因为实际的组合插入是由不遵守列上的 postgreSQL DEFAULT 子句的 ORM。
详情如下:
CREATE TABLE returntooffice
(
recid serial NOT NULL,
orderno integer NOT NULL,
chart_recid integer NOT NULL,
returndate date,
torder timestamp without time zone NOT NULL DEFAULT now(),
modified timestamp without time zone DEFAULT now(),
return_as_needed boolean,
is_deferred boolean,
CONSTRAINT returntooffice_pk PRIMARY KEY (recid),
CONSTRAINT returntooffice_chart_fk FOREIGN KEY (chart_recid)
REFERENCES charts (recid) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT returntooffice_order_unqiue UNIQUE (orderno),
CONSTRAINT returntooffice_unqiue UNIQUE (chart_recid, torder)
)
WITH (
OIDS=FALSE
);
ALTER TABLE returntooffice
OWNER TO postgres;
CREATE TRIGGER get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
CREATE TRIGGER update_modified
BEFORE UPDATE
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE update_modified();
CREATE SEQUENCE order_number_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 6558
CACHE 1;
ALTER TABLE order_number_seq
OWNER TO postgres;
CREATE OR REPLACE FUNCTION getnextorderno()
RETURNS trigger AS
$BODY$
BEGIN
NEW.orderno := nextval('order_number_seq');
Return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION getnextorderno()
OWNER TO postgres;
编辑#1:
按照下面的建议重命名触发器,允许一切在 pgAdmin 下正常工作,但在 PetaPoco Insert 上仍然失败。有什么想法吗?
CREATE TRIGGER zzz_get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
您可能遇到其他问题,例如 table 上的另一个触发器也具有 BEFORE INSERT
的行为并在此触发器之后执行。请记住,相同行为的触发器执行顺序是按名称的字母顺序选择的。
我已经测试了你的情况。 (是的,在 Postgres 9.5.4 上,因为你声称它在这个版本中不起作用。) 一切正常。输入值每次都会被 order_number_seq
.
中的下一个值覆盖
我们从序列的值 6558
开始,因此:
postgres=# insert into returntooffice (orderno, chart_recid) values (0, 1);
INSERT 0 1
postgres=# insert into returntooffice (chart_recid) values (2);
INSERT 0 1
postgres=# insert into returntooffice (orderno, chart_recid) values
(nextval('order_number_seq'::regclass), 3);
INSERT 0 1
预期的输出是:
postgres=# select recid, orderno, chart_recid from returntooffice;
recid | orderno | chart_recid
-------+---------+-------------
1 | 6558 | 1
2 | 6559 | 2
3 | 6561 | 3
为了重现 "issue" 我必须通过删除约束、替换不同语句的执行顺序并删除不必要的部分来修改创建脚本。这是:
CREATE TABLE returntooffice
(
recid serial NOT NULL,
orderno integer NOT NULL,
chart_recid integer NOT NULL,
returndate date,
torder timestamp without time zone NOT NULL DEFAULT now(),
modified timestamp without time zone DEFAULT now(),
return_as_needed boolean,
is_deferred boolean
)
WITH (
OIDS=FALSE
);
CREATE OR REPLACE FUNCTION getnextorderno()
RETURNS trigger AS
$BODY$
BEGIN
NEW.orderno := nextval('order_number_seq');
Return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
-- Trigger: get_next_order_number_trigger on returntooffice
-- DROP TRIGGER get_next_order_number_trigger ON returntooffice;
CREATE TRIGGER get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
CREATE SEQUENCE order_number_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 6558
CACHE 1;
杂乱触发器的两种替代方法。首先是操纵角色权限。
create table t (i serial, s text);
撤消相关角色对 table 的所有特权:
revoke all on t from test;
仅授予 select
grant select on t to test;
在除序列一之外的所有列上授予所有权限:
grant all (s) on t to test;
仅在序列上授予使用权:
revoke all on t_i_seq from test;
grant usage on t_i_seq to test;
现在该角色无法插入到序列列中:
insert into t (s) values ('a');
INSERT 0 1
insert into t (i,s) values (10,'a');
ERROR: permission denied for relation t
第二种选择更简单。仅授予 select 角色:
revoke all on t from test;
grant select on t to test;
作为 table 所有者使用 'security definer' 创建一个插入函数:
create function f(_s text)
returns t as $$
insert into t (s) values (_s)
returning *;
$$ language sql security definer;
角色将只能使用函数插入。
请参阅下面的答案和编辑#1。但是在PetaPoco/Npgsql.
下仍然触发失败我在这里遗漏了一些基本的东西。我需要确保添加到 table 的 any 记录的顺序为 only。即使 orderno 由插入语句本身提供,这也应该包括在内。也就是说,
insert into returntooffice (chart_recid, returndate, torder, **orderno**) values (14982,'2016-11-09','2017-12-4 00:21:42.553508', **0**);
和
insert into returntooffice (chart_recid, returndate, torder) values (14982,'2016-11-09','2017-12-4 00:21:42.553508');
都应该从 序列中产生下一个 orderno 而不是 0 的 orderno。 也就是说,实际发生的是正在插入提供的 orderno(0)——而不是下一个序列值(8000)。我在这里使用触发器,因为实际的组合插入是由不遵守列上的 postgreSQL DEFAULT 子句的 ORM。
详情如下:
CREATE TABLE returntooffice
(
recid serial NOT NULL,
orderno integer NOT NULL,
chart_recid integer NOT NULL,
returndate date,
torder timestamp without time zone NOT NULL DEFAULT now(),
modified timestamp without time zone DEFAULT now(),
return_as_needed boolean,
is_deferred boolean,
CONSTRAINT returntooffice_pk PRIMARY KEY (recid),
CONSTRAINT returntooffice_chart_fk FOREIGN KEY (chart_recid)
REFERENCES charts (recid) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT returntooffice_order_unqiue UNIQUE (orderno),
CONSTRAINT returntooffice_unqiue UNIQUE (chart_recid, torder)
)
WITH (
OIDS=FALSE
);
ALTER TABLE returntooffice
OWNER TO postgres;
CREATE TRIGGER get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
CREATE TRIGGER update_modified
BEFORE UPDATE
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE update_modified();
CREATE SEQUENCE order_number_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 6558
CACHE 1;
ALTER TABLE order_number_seq
OWNER TO postgres;
CREATE OR REPLACE FUNCTION getnextorderno()
RETURNS trigger AS
$BODY$
BEGIN
NEW.orderno := nextval('order_number_seq');
Return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION getnextorderno()
OWNER TO postgres;
编辑#1: 按照下面的建议重命名触发器,允许一切在 pgAdmin 下正常工作,但在 PetaPoco Insert 上仍然失败。有什么想法吗?
CREATE TRIGGER zzz_get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
您可能遇到其他问题,例如 table 上的另一个触发器也具有 BEFORE INSERT
的行为并在此触发器之后执行。请记住,相同行为的触发器执行顺序是按名称的字母顺序选择的。
我已经测试了你的情况。 (是的,在 Postgres 9.5.4 上,因为你声称它在这个版本中不起作用。) 一切正常。输入值每次都会被 order_number_seq
.
我们从序列的值 6558
开始,因此:
postgres=# insert into returntooffice (orderno, chart_recid) values (0, 1);
INSERT 0 1
postgres=# insert into returntooffice (chart_recid) values (2);
INSERT 0 1
postgres=# insert into returntooffice (orderno, chart_recid) values
(nextval('order_number_seq'::regclass), 3);
INSERT 0 1
预期的输出是:
postgres=# select recid, orderno, chart_recid from returntooffice;
recid | orderno | chart_recid
-------+---------+-------------
1 | 6558 | 1
2 | 6559 | 2
3 | 6561 | 3
为了重现 "issue" 我必须通过删除约束、替换不同语句的执行顺序并删除不必要的部分来修改创建脚本。这是:
CREATE TABLE returntooffice
(
recid serial NOT NULL,
orderno integer NOT NULL,
chart_recid integer NOT NULL,
returndate date,
torder timestamp without time zone NOT NULL DEFAULT now(),
modified timestamp without time zone DEFAULT now(),
return_as_needed boolean,
is_deferred boolean
)
WITH (
OIDS=FALSE
);
CREATE OR REPLACE FUNCTION getnextorderno()
RETURNS trigger AS
$BODY$
BEGIN
NEW.orderno := nextval('order_number_seq');
Return NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
-- Trigger: get_next_order_number_trigger on returntooffice
-- DROP TRIGGER get_next_order_number_trigger ON returntooffice;
CREATE TRIGGER get_next_order_number_trigger
BEFORE INSERT
ON returntooffice
FOR EACH ROW
EXECUTE PROCEDURE getnextorderno();
CREATE SEQUENCE order_number_seq
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 6558
CACHE 1;
杂乱触发器的两种替代方法。首先是操纵角色权限。
create table t (i serial, s text);
撤消相关角色对 table 的所有特权:
revoke all on t from test;
仅授予 select
grant select on t to test;
在除序列一之外的所有列上授予所有权限:
grant all (s) on t to test;
仅在序列上授予使用权:
revoke all on t_i_seq from test;
grant usage on t_i_seq to test;
现在该角色无法插入到序列列中:
insert into t (s) values ('a');
INSERT 0 1
insert into t (i,s) values (10,'a');
ERROR: permission denied for relation t
第二种选择更简单。仅授予 select 角色:
revoke all on t from test;
grant select on t to test;
作为 table 所有者使用 'security definer' 创建一个插入函数:
create function f(_s text)
returns t as $$
insert into t (s) values (_s)
returning *;
$$ language sql security definer;
角色将只能使用函数插入。