2016-09-03 1 views
2

Bitte siehe Antwort unten und Edit # 1. Der Trigger schlägt jedoch immer noch unter PetaPoco/Npgsql fehl.Wie erzwingen neue Datensätze, um Spaltenwerte nur aus einer Sequenz in PostgreSQL zu haben?

Ich vermisse etwas Grundlegendes hier. Ich muss dafür sorgen, dass jeder Datensatz hinzugefügt, um die Tabelle hat eine Bestellung nur durch die Sequenz. Dies sollte auch dann gelten, wenn der Befehl nicht von der INSERT-Anweisung selbst geliefert wird. Das heißt,

insert into returntooffice (chart_recid, returndate, torder, **orderno**) values (14982,'2016-11-09','2017-12-4 00:21:42.553508', **0**); 

und

insert into returntooffice (chart_recid, returndate, torder) values (14982,'2016-11-09','2017-12-4 00:21:42.553508'); 

sollte sowohl Ergebnis in dem nächsten OrderNo von der Sequenz und nicht einen OrderNo von 0. Das heißt, was tatsächlich geschieht, ist, dass die gelieferte orderno (von 0) wird eingefügt - nicht der nächste Sequenzwert (von 8000). Ich benutze hier einen Trigger, da das tatsächlich zusammengesetzte Insert von einem ORM stammt, das die postgreSQL DEFAULT-Klauseln für Spalten nicht berücksichtigt.

Hier sind die Details:

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; 

Edit # 1: Umbenennen der Auslöser, wie unten vorgeschlagen, ermöglicht alles richtig unter pgAdmin zu arbeiten, aber immer noch nicht auf PetaPoco einfügen. Irgendwelche Ideen warum?

CREATE TRIGGER zzz_get_next_order_number_trigger 
    BEFORE INSERT 
    ON returntooffice 
    FOR EACH ROW 
    EXECUTE PROCEDURE getnextorderno(); 
+2

Wenn Sie den Insert-Trigger tatsächlich so erzeugt haben, wird der gespeicherte Wert ** immer aus der Sequenz genommen. Es muss etwas anderes passieren. –

+0

@a_horse_with_no_name Einverstanden. Aber der Auslöser scheint ignoriert zu werden, selbst wenn es direkt mit PgAdmin gemacht wird. Ich verstehe es nicht. :(Wenn ich mich richtig erinnere, funktionierte das in PostgreSQL 9.3 –

Antwort

1

können Sie ein anderes Problem haben wie ein anderer Trigger auf die Tabelle, die auch das Verhalten von BEFORE INSERT und führt nach diesem Trigger hat. Denken Sie daran, dass die Ausführungsreihenfolge der Trigger für das gleiche Verhalten alphabetisch nach ihren Namen ausgewählt wird.


Ich habe Ihren Fall getestet.(Ja, auf Postgres 9.5.4, da Sie behaupten, dass es in dieser Version nicht funktioniert.) Alles funktioniert gut. Der Eingabewert wird jedes Mal mit einem nächsten Wert von order_number_seq überschrieben.

Wir mit einem Wert von 6558 der Sequenz fangen, und so:

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 

und die Ausgabe wie erwartet:

postgres=# select recid, orderno, chart_recid from returntooffice; 
recid | orderno | chart_recid 
-------+---------+------------- 
    1 | 6558 |   1 
    2 | 6559 |   2 
    3 | 6561 |   3 

das "Problem" zu reproduzieren Ich musste Create Scripts modifizieren, indem ich Constraints fallen ließ, die Ausführungsreihenfolge für verschiedene Anweisungen ersetzte und unnötige Teile entfernte. Hier ist es:

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; 
+0

Das Ändern des Auslösernamens funktioniert mit PgAdmin, aber das scheitert immer noch unter PetaPoco - aber das ist eine andere Frage. Danke. –

1

Zwei Alternativen zu unordentlichen Auslösern. Die erste besteht darin, Rollenprivilegien zu manipulieren.

create table t (i serial, s text); 

Entziehen alle Privilegien auf dem Tisch von den zugehörigen Rollen:

revoke all on t from test; 

Grants wählen nur

grant select on t to test; 

Grants alle für alle Spalten, aber die Sequenz ein:

grant all (s) on t to test; 

Nutzungsberechnung o NLY auf der Sequenz:

revoke all on t_i_seq from test; 
grant usage on t_i_seq to test; 

Jetzt kann diese Rolle nicht einfügen in die Sequenz Spalte:

insert into t (s) values ('a'); 
INSERT 0 1 
insert into t (i,s) values (10,'a'); 
ERROR: permission denied for relation t 

Die zweite Alternative ist einfacher. Gewähren Sie nur auf die Rolle auswählen:

revoke all on t from test; 
grant select on t to test; 

Als Tabelleneigentümer eine Insert-Funktion mit ‚Sicherheit definer‘ erstellen:

create function f(_s text) 
returns t as $$ 

    insert into t (s) values (_s) 
    returning *; 

$$ language sql security definer; 

Die Rolle wird nur in der Lage sein, die Funktion zum Einfügen verwenden.

Verwandte Themen