Partitioning constraints vs before-trigger

From: Nikolay <nitronick2005(at)gmail(dot)com>
To: pgsql-general(at)postgresql(dot)org
Subject: Partitioning constraints vs before-trigger
Date: 2018-01-18 12:39:55
Message-ID: efa0022e-ccbf-8d96-fca9-245f0065b4e7@gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general

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.

Browse pgsql-general by date

  From Date Subject
Next Message Enrico Pirozzi 2018-01-18 16:06:57 Connection type
Previous Message Michael Paquier 2018-01-18 01:39:57 Re: ERROR: unexpected chunk number 0 (expected 1) for toast value 76753264 in pg_toast_10920100