1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-03 09:13:20 +03:00

Fix NO ACTION temporal foreign keys when the referenced endpoints change

If a referenced UPDATE changes the temporal start/end times, shrinking
the span the row is valid, we get a false return from
ri_Check_Pk_Match(), but overlapping references may still be valid, if
their reference didn't overlap with the removed span.

We need to consider what span(s) are still provided in the referenced
table.  Instead of returning that from ri_Check_Pk_Match(), we can
just look it up in the main SQL query.

Reported-by: Sam Gabrielsson <sam@movsom.se>
Author: Paul Jungwirth <pj@illuminatedcomputing.com>
Discussion: https://www.postgresql.org/message-id/flat/CA+renyUApHgSZF9-nd-a0+OPGharLQLO=mDHcY4_qQ0+noCUVg@mail.gmail.com
This commit is contained in:
Peter Eisentraut
2025-01-21 14:34:44 +01:00
parent 888d4523f0
commit 1772d554b0
7 changed files with 233 additions and 7 deletions

View File

@@ -1249,6 +1249,33 @@ UPDATE temporal_rng
SET valid_at = CASE WHEN lower(valid_at) = '2018-01-01' THEN daterange('2018-01-01', '2018-01-05')
WHEN lower(valid_at) = '2018-02-01' THEN daterange('2018-01-05', '2018-03-01') END
WHERE id = '[6,7)';
-- a PK update shrinking the referenced range but still valid:
-- There are two references: one fulfilled by the first pk row,
-- the other fulfilled by both pk rows combined.
INSERT INTO temporal_rng (id, valid_at) VALUES
('[1,2)', daterange('2018-01-01', '2018-03-01')),
('[1,2)', daterange('2018-03-01', '2018-06-01'));
INSERT INTO temporal_fk_rng2rng (id, valid_at, parent_id) VALUES
('[1,2)', daterange('2018-01-15', '2018-02-01'), '[1,2)'),
('[2,3)', daterange('2018-01-15', '2018-05-01'), '[1,2)');
UPDATE temporal_rng SET valid_at = daterange('2018-01-15', '2018-03-01')
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update growing the referenced range is fine:
UPDATE temporal_rng SET valid_at = daterange('2018-01-01', '2018-03-01')
WHERE id = '[1,2)' AND valid_at @> '2018-01-25'::date;
-- a PK update shrinking the referenced range and changing the id invalidates the whole range (error):
UPDATE temporal_rng SET id = '[2,3)', valid_at = daterange('2018-01-15', '2018-03-01')
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update changing only the id invalidates the whole range (error):
UPDATE temporal_rng SET id = '[2,3)'
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update that loses time from both ends, but is still valid:
INSERT INTO temporal_rng (id, valid_at) VALUES
('[2,3)', daterange('2018-01-01', '2018-03-01'));
INSERT INTO temporal_fk_rng2rng (id, valid_at, parent_id) VALUES
('[5,6)', daterange('2018-01-15', '2018-02-01'), '[2,3)');
UPDATE temporal_rng SET valid_at = daterange('2018-01-15', '2018-02-15')
WHERE id = '[2,3)';
-- a PK update that fails because both are referenced:
UPDATE temporal_rng SET valid_at = daterange('2016-01-01', '2016-02-01')
WHERE id = '[5,6)' AND valid_at = daterange('2018-01-01', '2018-02-01');
@@ -1710,6 +1737,33 @@ UPDATE temporal_mltrng
SET valid_at = CASE WHEN lower(valid_at) = '2018-01-01' THEN datemultirange(daterange('2018-01-01', '2018-01-05'))
WHEN lower(valid_at) = '2018-02-01' THEN datemultirange(daterange('2018-01-05', '2018-03-01')) END
WHERE id = '[6,7)';
-- a PK update shrinking the referenced multirange but still valid:
-- There are two references: one fulfilled by the first pk row,
-- the other fulfilled by both pk rows combined.
INSERT INTO temporal_mltrng (id, valid_at) VALUES
('[1,2)', datemultirange(daterange('2018-01-01', '2018-03-01'))),
('[1,2)', datemultirange(daterange('2018-03-01', '2018-06-01')));
INSERT INTO temporal_fk_mltrng2mltrng (id, valid_at, parent_id) VALUES
('[1,2)', datemultirange(daterange('2018-01-15', '2018-02-01')), '[1,2)'),
('[2,3)', datemultirange(daterange('2018-01-15', '2018-05-01')), '[1,2)');
UPDATE temporal_mltrng SET valid_at = datemultirange(daterange('2018-01-15', '2018-03-01'))
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update growing the referenced multirange is fine:
UPDATE temporal_mltrng SET valid_at = datemultirange(daterange('2018-01-01', '2018-03-01'))
WHERE id = '[1,2)' AND valid_at @> '2018-01-25'::date;
-- a PK update shrinking the referenced multirange and changing the id invalidates the whole multirange (error):
UPDATE temporal_mltrng SET id = '[2,3)', valid_at = datemultirange(daterange('2018-01-15', '2018-03-01'))
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update changing only the id invalidates the whole multirange (error):
UPDATE temporal_mltrng SET id = '[2,3)'
WHERE id = '[1,2)' AND valid_at @> '2018-01-15'::date;
-- a PK update that loses time from both ends, but is still valid:
INSERT INTO temporal_mltrng (id, valid_at) VALUES
('[2,3)', datemultirange(daterange('2018-01-01', '2018-03-01')));
INSERT INTO temporal_fk_mltrng2mltrng (id, valid_at, parent_id) VALUES
('[5,6)', datemultirange(daterange('2018-01-15', '2018-02-01')), '[2,3)');
UPDATE temporal_mltrng SET valid_at = datemultirange(daterange('2018-01-15', '2018-02-15'))
WHERE id = '[2,3)';
-- a PK update that fails because both are referenced:
UPDATE temporal_mltrng SET valid_at = datemultirange(daterange('2016-01-01', '2016-02-01'))
WHERE id = '[5,6)' AND valid_at = datemultirange(daterange('2018-01-01', '2018-02-01'));