I have a ‘test’ PostgreSQL 12 schema, defined with initial tables:
begin;
drop schema if exists test cascade;
create schema test;
set search_path = test,public;
create table jobs (
job_id serial primary key,
job_type_lu text);
create table phases (
phase_id serial primary key,
job_id_fk int references jobs(job_id),
phase_type_lu text);
create table phase_steps (
phase_step_id serial primary key,
phase_id_fk int references phases(phase_id),
step_type_lu text);
commit;
‘jobs’ are composed of one or more ‘phases’, which in turn have multiple ‘phase_steps’
What I want to accomplish is to fire two triggers when a new job is inserted into the ‘jobs’ table. I have these two triggers defined:
create or replace function test.setup_phases () returns trigger as $$
begin
insert into test.phase_steps (job_id_fk,phase_type_lu)
values
(new.jobs_id,'RESEARCH: JOB_INPUT'),
(new.jobs_id,'RESEARCH: WATER'),
(new.jobs_id,'RESEARCH: OTHER'),
(new.jobs_id,'RESEARCH: REVISION_ATEAM'),
(new.jobs_id,'RESEARCH: REVISION_CLIENT');
return new;
end;
$$ language plpgsql;
create trigger setup_test_phases after insert on test.jobs
for each row execute procedure test.setup_phases();
create or replace function test.setup_phase_steps() returns trigger as $$
begin
if (new.phase_type_lu = 'RESEARCH')
then
insert into test.phase_steps (phase_id_fk,step_type_lu)
values
(new.phase_id,'RESEARCH: JOB_INPUT'),
(new.phase_id,'RESEARCH: WATER'),
(new.phase_id,'RESEARCH: OTHER'),
(new.phase_id,'RESEARCH: REVISION_ATEAM'),
(new.phase_id,'RESEARCH: REVISION_CLIENT');
return new;
end if;
if (new.phase_type_lu = 'SURVEY')
then
insert into test.phase_steps (phase_id_fk,step_type_lu)
values
(new.phase_id,'SURVEY: SCHEDULING'),
(new.phase_id,'SURVEY: FIELDING'),
(new.phase_id,'SURVEY: REVISION_ATEAM'),
(new.phase_id,'SURVEY: REVISION_CLIENT');
return new;
end if;
end;
$$ language plpgsql;
create trigger setup_test_phase_steps after insert on test.phases
for each row execute procedure test.setup_phase_steps();
If I input data directly for just the phases (therefore invoking only the ‘setup_test_phase_steps’ trigger, e.g.,
insert into test.phases (phase_type_lu)
values
('RESEARCH'),
('SURVEY');
The correct results are obtained:
However, if I enter input directly into the ‘jobs’ table (which then will invoke the ‘setup_test_phases’ AND the ‘setup_test_phase_steps’ triggers):
insert into test.jobs (job_type_lu)
values
('NT-TYP');
I get this error:
I’m obviously misunderstanding cascading triggers, but what?
Best Answer
To resolve your problem, I did the following (all of the code below can be found on the fiddle here):
I changed the formatting somewhat - it helps me think - I try to base my SQL coding standards on this, but that's, as Fr. Jack would say, an ecumenical matter (if you're not a Fr. Ted fan, don't worry...).
The crux of your problem is that you have a cascading chain of
FOREIGN KEY
s fromjob
tophase
tophase_step
- which is good - helps keep track of what work goes with what contract &c.The problem is with your code here:
You have
setup_phases
, BUT underneath that, you haveinsert into test.phase_steps (job_id_fk,phase_type_lu)
- you "skip" theINSERT INTO phases
step - so your chain ofFOREIGN KEY
s is broken.Now, I imagine that your work flow is something like this:
You receive a job - the first
phase
of which is normallyRESEARCH
, so I created thephase
table as follows (job
remains unchanged):So, then I did this:
and:
So, now, when I create a job, it sets up a
RESEARCH
phase. You missed this in your code. What happens now when IINSERT
a job is the following:and:
Result:
Note: no phase steps because I haven't done that yet.
So then I copied your code - with the notable exception (pardon the pun...) of the lines (see fiddle):
Not having this provokes the error message you are receiving...
So, now, when a job is inserted, the job cascades to (
RESEARCH
) phase and the phase cascades to the appropriate steps - as follows:And:
Result:
So, we have our cascade.
Now (again, making assumptions about your workflow) - if the client is happy with the research phase, they'll want to progress to the
SURVEY
phase and so you will wish to enter that into the system but not as a new job (hope I'm OK so far... :-) )So, now we do this:
BUT, there's no associated job_id, so, thanks to the
EXCEPTION
, this fails as follows:But, if we do this:
we get:
Result:
Our
SURVEY
phase steps have been propagated with the correctFOREIGN KEY
s. So, all is rosy in the garden? I ran a final test at the bottom of the fiddle (not shown for brevity), and all appears OK!For future reference, it would be great if you could provide appropriate fiddles with your questions and also if you could give us the version of PostgreSQL that you are running - help us to help you!
p.s. welcome to the forum!