problem with multiple invocations of a deferred constraint trigger - Mailing list pgsql-general

From Karsten Hilbert
Subject problem with multiple invocations of a deferred constraint trigger
Date
Msg-id 20041202192719.D673@hermes.hilbert.loc
Whole thread Raw
List pgsql-general
Hello all,

I have two tables (simplified):

create table clin_episode (
    pk serial primary key,
    started timestamp with time zone,
    fk_clin_narrative integer
    unique
    default null
);

create table clin_narrative (
    pk serial primary key,
    fk_episode integer
    not null
    references clin_episode(pk),
    narrative text
    not null
);

The idea behind this is that clin_narrative stores ALL
narrative of a medical record. Clin_episode stores episodes
during which care was received. Such episodes do have a name
(such as "knee pain Nov/04") but such episode names are part
of the medical record narrative and should be stored in
clin_narrative, too.

Now, obviously I got a cyclic referential integrity problem
here, eg. narrative pointing to episodes which point back to
narrative. No problem, I added a deferred constraint trigger
like this (don't worry about the health issue stuff, it isn't
related to the problem):

*-----------------------------------------
-- an episode not linked to a health issue must have a
-- name (at least when the transaction ends ...)
\unset ON_ERROR_STOP
drop trigger tr_standalone_epi_needs_name on clin_episode;
drop function trf_standalone_epi_needs_name();
\set ON_ERROR_STOP 1

create function trf_standalone_epi_needs_name() returns opaque as '
declare
    msg text;
    narr_pk integer;
    narr_fk_episode integer;
begin
    -- debug ...
    raise notice ''%'', TG_OP;
    -- *if* we have a name it must belong to us ...
    -- (eg. check for cyclic referential integrity violations)
    if NEW.fk_clin_narrative is not null then
        select into narr_pk, narr_fk_episode cn.pk, cn.fk_episode
            from clin_narrative cn
            where cn.pk = NEW.fk_clin_narrative
            limit 1;
        if narr_fk_episode <> NEW.pk then
            msg := ''trf_standalone_epi_needs_name: clin_narrative row [''
                || narr_pk || ''] does not belong to episode [''
                || NEW.pk  || ''] and cannot thus name that episode'';
            raise exception ''%'', msg;
        end if;
        return NULL;
    end if;
    -- if linked to a health issue we do not have to have a name of our own ...
    if NEW.fk_health_issue is not null then
        return NULL;
    end if;
    msg := ''trf_standalone_epi_needs_name: episodes not linked to a health issue must point to a clin_narrative row at
theend of the transaction''; 
    raise exception ''%'', msg;
end;
' language 'plpgsql';

-- the trick is to defer the trigger ...
create constraint trigger tr_standalone_epi_needs_name
    after insert or update
    on clin_episode
    initially deferred
    for each row
        execute procedure trf_standalone_epi_needs_name()
;
*-----------------------------------------

This all works. However, to actually insert data I do the
following:

- insert into clin_episode with fk_clin_narrative=NULL
- insert into clin_narrative with fk_encounter = currval('clin_episode_pk_seq')
- update clin_episode with set fk_clin_narrative = currval('clin_narrative_pk_seq')

What I end up with is the trigger being queued up for checking
at the end of transaction TWICE. This is quite logical since I
did an INSERT and then an UPDATE and the trigger fires on
both.

However, the first trigger invocation (on INSERT) correctly
raises an exception since at that time the NEW row did not yet
have a suitable fk_clin_narrative.

What is the proper solution here ?

- Try to deal with INSERT and UPDATE somehow separately ? The
  problem I see with this is that *actually* I want to check the
  state of the row at the *latest* UDPATE in a transaction
  *only* and there might possibly be several UPDATEs in a tx.

- Don't check whether *NEW* has a fk_clin_narrative at trigger
  execution time (eg. at the end of the transaction) but
  rather explicitely SELECT fk_clin_narrative from the table
  where pk = NEW.pk ? IOW check the actual state of the row
  (which may have undergone several UPDATEs) at the end of the
  transaction instead of the NEW row at trigger queue up
  time. Would I then not run into trouble when
  trying to insert more than one episode within the same
  transaction ?

I'd be happy if someone had some help on this.

(Will post full schema details if needed.)


Karsten

GnuMed i18n coordinator
--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

pgsql-general by date:

Previous
From: Michael Fuhr
Date:
Subject: List archives not being updated?
Next
From: "Vincent Hikida"
Date:
Subject: Re: Indexes?