Hi all,
Could anybody explain, what happens first: constraint check or
before-trigger execution?
I have a table, partitioned by date:
CREATE TABLE foo
(
unid text NOT NULL,
logtime timestamp with time zone NOT NULL,
size integer,
CONSTRAINT foo_pkey PRIMARY KEY (unid)
);
-- There is an before-insert trigger which works perfectly, creates a
new monthly partition if neccessary and inserts new record into the
partition.
-- Here is how partitions look like:
CREATE TABLE foo_2018_01
(
CONSTRAINT foo_2018_01_pkey PRIMARY KEY (unid),
CONSTRAINT foo_2018_01_logtime_check CHECK (logtime >= '2018-01-01
00:00:00+00'::timestamp with time zone AND logtime < '2018-02-01
00:00:00+00'::timestamp with time zone)
)
INHERITS (foo);
I cannot change anything in the application, as it's proprietary. So I
had to do partitioning myself with a trigger.
Now there's a new problem. It looks like the application sometimes do
UPDATEs to the "logtime" column, which I use for partitioning.
So the application can do something like UPDATE foo SET
logtime='2017-12-01 00:00:00+00', size=5 WHERE unid='blahblablah',
althrough this record had logtime='2018-01-18 00:00:00+00' and was in
different partition.
In such case, I can see the error (and transaction aborts):
ERROR: new row for relation "foo_2018_01" violates check constraint
"foo_2018_01_logtime_check"
For business logic, it wouldn't be critical if I forbid/undo/replace
modification of logtime column. But other columns must be updated by the
application when neccessary.
Now I need to ignore new value for "logtime" column for every UPDATE to
table "foo".
Here is my idea:
CREATE OR REPLACE FUNCTION logtime_update_trigger() RETURNS trigger AS
$BODY$
BEGIN
IF (NEW.logtime != OLD.logtime) THEN
NEW.logtime := OLD.logtime;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE TRIGGER trg_foo_update BEFORE UPDATE ON foo
FOR EACH ROW EXECUTE PROCEDURE logtime_update_trigger();
Unfortunately, it seems like this trigger is not even being executed and
I still get the same error:
ERROR: new row for relation "foo_2018_01" violates check constraint
"foo_2018_01_logtime_check"
I suppose that's because contraint check is performed before the trigger
is fired? Is there any workarounds here?
I also tried to create a rule:
CREATE OR REPLACE RULE test_rule AS ON UPDATE TO foo
WHERE new.logtime <> old.logtime DO INSTEAD
UPDATE foo SET size = new.size WHERE foo.unid = old.unid AND foo.logtime
= old.logtime;
But then I get recursion error:
ERROR: infinite recursion detected in rules for relation "foo"
Possibly because the recursion analysis doesn't take WHERE condition
into account.
Any help would be greatly appreciated.
PostgreSQL version: 9.0.1 on CentOS 5 i686.
Best regards, Nikolay Karikh.