I have a stored procedure which attempts to write to two tables, related via a foreign key constraint.
Slightly simplified table definitions:
CREATE TABLE station_event
(
station_code VARCHAR(3) NOT NULL,
user_id INTEGER NOT NULL,
event_dtm TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT station_event_pk
PRIMARY KEY (station_code, user_id, event_dtm)
);
CREATE TABLE location_station_event
(
station_code VARCHAR(3) NOT NULL,
user_id INTEGER NOT NULL,
event_dtm TIMESTAMPTZ(0) NOT NULL DEFAULT now(),
location_code VARCHAR(8) NOT NULL,
location_no INTEGER NOT NULL,
CONSTRAINT location_station_event_pk
PRIMARY KEY (station_code, user_id, event_dtm),
CONSTRAINT location_station_event_station_event_fk
FOREIGN KEY (station_code, user_id, event_dtm)
REFERENCES station_event (station_code, user_id, event_dtm)
);
Slightly simplified stored procedure definition:
CREATE FUNCTION location_station_apply (
p_site_code VARCHAR,
p_location_no INTEGER,
p_station_code VARCHAR
)
RETURNS VOID AS $$
BEGIN
INSERT INTO station_event (
station_code,
user_id
)
VALUES (
p_station_code,
user_id()
);
INSERT INTO location_station_event (
station_code,
user_id,
location_no
)
VALUES (
p_station_code,
user_id(),
p_location_no
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
The error I receive is:
ERROR: insert or update on table "location_station_event" violates
foreign key constraint "location_station_event_station_event_fk"
DETAIL: Key (station_code, user_id, event_dtm)=(CE, 1, 2024-05-24
10:21:56+01) is not present in table "station_event".
The function is unchanged between DEV and PROD environments, although DEV is running PostgreSQL v14 and PROD is still running v11. It is failing, as above, on DEV but running successfully on PROD.
Is there something I am missing which might be PostgreSQL version specific? Maybe there’s a new config parameter (introduced after v11)?
2
Answers
Your
primary key
of the first table isPRIMARY KEY (station_code, user_id, event_dtm)
and you refer it from the second table viaYou never specify it, so they both refer to the default values. In order to fix the issue, make sure that you pass the timestamp to the procedure, so it will enforce the same value for both inserts instead of allowing them to diverge (which is the reason for your problem):
The reason for your problem is that the result of
now()
is different when you execute the second insert from what it was when you executed the first insert.Furthermore, please make sure that
event_dtm
has the same type for both. Finally, please make sure thatuser_id()
is returning the same value at both inserts. Because if that differs, then you will need to pass a user_id too and use that for both inserts.Made minor changers in your table definitions, and function. Find below code:
Made event_dtm consistent in both tables, but changed its default value in location_station_event table
As this was causing to round up your micro-seconds in location_station_event
and values were not matching, and was violating foreign key constraint.
Secondly made a minor change in your function to make it fool proof, added new variable which gets event_dtm value on inserting in station_event and insert that exact value in location_station_event