Fix error code for referential action RESTRICT
According to the SQL standard, if the referential action RESTRICT is triggered, it has its own error code. We previously didn't use that, we just used the error code for foreign key violation. But RESTRICT is not necessarily an actual foreign key violation. The foreign key might still be satisfied in theory afterwards, but the RESTRICT setting prevents the action even then. So it's a separate kind of error condition. Discussion: https://www.postgresql.org/message-id/ea5b2777-266a-46fa-852f-6fca6ec480ad@eisentraut.org
This commit is contained in:
parent
2f696453d2
commit
086c84b23d
@ -228,6 +228,7 @@ static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
|
||||
RI_QueryKey *qkey, SPIPlanPtr qplan,
|
||||
Relation fk_rel, Relation pk_rel,
|
||||
TupleTableSlot *oldslot, TupleTableSlot *newslot,
|
||||
bool is_restrict,
|
||||
bool detectNewRows, int expect_OK);
|
||||
static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
|
||||
const RI_ConstraintInfo *riinfo, bool rel_is_pk,
|
||||
@ -235,7 +236,7 @@ static void ri_ExtractValues(Relation rel, TupleTableSlot *slot,
|
||||
static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
|
||||
Relation pk_rel, Relation fk_rel,
|
||||
TupleTableSlot *violatorslot, TupleDesc tupdesc,
|
||||
int queryno, bool partgone) pg_attribute_noreturn();
|
||||
int queryno, bool is_restrict, bool partgone) pg_attribute_noreturn();
|
||||
|
||||
|
||||
/*
|
||||
@ -449,6 +450,7 @@ RI_FKey_check(TriggerData *trigdata)
|
||||
ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
NULL, newslot,
|
||||
false,
|
||||
pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE,
|
||||
SPI_OK_SELECT);
|
||||
|
||||
@ -613,6 +615,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
|
||||
result = ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
oldslot, NULL,
|
||||
false,
|
||||
true, /* treat like update */
|
||||
SPI_OK_SELECT);
|
||||
|
||||
@ -800,6 +803,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
|
||||
ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
oldslot, NULL,
|
||||
!is_no_action,
|
||||
true, /* must detect new rows */
|
||||
SPI_OK_SELECT);
|
||||
|
||||
@ -901,6 +905,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
|
||||
ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
oldslot, NULL,
|
||||
false,
|
||||
true, /* must detect new rows */
|
||||
SPI_OK_DELETE);
|
||||
|
||||
@ -1017,6 +1022,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
|
||||
ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
oldslot, newslot,
|
||||
false,
|
||||
true, /* must detect new rows */
|
||||
SPI_OK_UPDATE);
|
||||
|
||||
@ -1244,6 +1250,7 @@ ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
|
||||
ri_PerformCheck(riinfo, &qkey, qplan,
|
||||
fk_rel, pk_rel,
|
||||
oldslot, NULL,
|
||||
false,
|
||||
true, /* must detect new rows */
|
||||
SPI_OK_UPDATE);
|
||||
|
||||
@ -1690,7 +1697,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
||||
ri_ReportViolation(&fake_riinfo,
|
||||
pk_rel, fk_rel,
|
||||
slot, tupdesc,
|
||||
RI_PLAN_CHECK_LOOKUPPK, false);
|
||||
RI_PLAN_CHECK_LOOKUPPK, false, false);
|
||||
|
||||
ExecDropSingleTupleTableSlot(slot);
|
||||
}
|
||||
@ -1906,7 +1913,7 @@ RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
||||
fake_riinfo.pk_attnums[i] = i + 1;
|
||||
|
||||
ri_ReportViolation(&fake_riinfo, pk_rel, fk_rel,
|
||||
slot, tupdesc, 0, true);
|
||||
slot, tupdesc, 0, false, true);
|
||||
}
|
||||
|
||||
if (SPI_finish() != SPI_OK_FINISH)
|
||||
@ -2387,6 +2394,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
|
||||
RI_QueryKey *qkey, SPIPlanPtr qplan,
|
||||
Relation fk_rel, Relation pk_rel,
|
||||
TupleTableSlot *oldslot, TupleTableSlot *newslot,
|
||||
bool is_restrict,
|
||||
bool detectNewRows, int expect_OK)
|
||||
{
|
||||
Relation query_rel,
|
||||
@ -2511,7 +2519,7 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
|
||||
pk_rel, fk_rel,
|
||||
newslot ? newslot : oldslot,
|
||||
NULL,
|
||||
qkey->constr_queryno, false);
|
||||
qkey->constr_queryno, is_restrict, false);
|
||||
|
||||
return SPI_processed != 0;
|
||||
}
|
||||
@ -2552,7 +2560,7 @@ static void
|
||||
ri_ReportViolation(const RI_ConstraintInfo *riinfo,
|
||||
Relation pk_rel, Relation fk_rel,
|
||||
TupleTableSlot *violatorslot, TupleDesc tupdesc,
|
||||
int queryno, bool partgone)
|
||||
int queryno, bool is_restrict, bool partgone)
|
||||
{
|
||||
StringInfoData key_names;
|
||||
StringInfoData key_values;
|
||||
@ -2682,6 +2690,20 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
|
||||
errdetail("Key is not present in table \"%s\".",
|
||||
RelationGetRelationName(pk_rel)),
|
||||
errtableconstraint(fk_rel, NameStr(riinfo->conname))));
|
||||
else if (is_restrict)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_RESTRICT_VIOLATION),
|
||||
errmsg("update or delete on table \"%s\" violates RESTRICT setting of foreign key constraint \"%s\" on table \"%s\"",
|
||||
RelationGetRelationName(pk_rel),
|
||||
NameStr(riinfo->conname),
|
||||
RelationGetRelationName(fk_rel)),
|
||||
has_perm ?
|
||||
errdetail("Key (%s)=(%s) is referenced from table \"%s\".",
|
||||
key_names.data, key_values.data,
|
||||
RelationGetRelationName(fk_rel)) :
|
||||
errdetail("Key is referenced from table \"%s\".",
|
||||
RelationGetRelationName(fk_rel)),
|
||||
errtableconstraint(fk_rel, NameStr(riinfo->conname))));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
|
||||
|
@ -2051,8 +2051,8 @@ CREATE TABLE test12fk (a int, b text COLLATE case_insensitive REFERENCES test12p
|
||||
INSERT INTO test12pk VALUES ('abc');
|
||||
INSERT INTO test12fk VALUES (1, 'abc'), (2, 'ABC');
|
||||
UPDATE test12pk SET x = 'ABC' WHERE x = 'abc'; -- restrict violation
|
||||
ERROR: update or delete on table "test12pk" violates foreign key constraint "test12fk_b_fkey" on table "test12fk"
|
||||
DETAIL: Key (x)=(abc) is still referenced from table "test12fk".
|
||||
ERROR: update or delete on table "test12pk" violates RESTRICT setting of foreign key constraint "test12fk_b_fkey" on table "test12fk"
|
||||
DETAIL: Key (x)=(abc) is referenced from table "test12fk".
|
||||
SELECT * FROM test12pk;
|
||||
x
|
||||
-----
|
||||
|
@ -1439,11 +1439,11 @@ insert into pp values(11);
|
||||
update pp set f1=f1+1;
|
||||
insert into cc values(13);
|
||||
update pp set f1=f1+1; -- fail
|
||||
ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc"
|
||||
DETAIL: Key (f1)=(13) is still referenced from table "cc".
|
||||
ERROR: update or delete on table "pp" violates RESTRICT setting of foreign key constraint "cc_f1_fkey" on table "cc"
|
||||
DETAIL: Key (f1)=(13) is referenced from table "cc".
|
||||
delete from pp where f1 = 13; -- fail
|
||||
ERROR: update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc"
|
||||
DETAIL: Key (f1)=(13) is still referenced from table "cc".
|
||||
ERROR: update or delete on table "pp" violates RESTRICT setting of foreign key constraint "cc_f1_fkey" on table "cc"
|
||||
DETAIL: Key (f1)=(13) is referenced from table "cc".
|
||||
drop table pp, cc;
|
||||
--
|
||||
-- Test interaction of foreign-key optimization with rules (bug #14219)
|
||||
@ -2664,11 +2664,11 @@ ALTER TABLE fk ADD FOREIGN KEY (a) REFERENCES pk ON UPDATE RESTRICT ON DELETE RE
|
||||
CREATE TABLE fk_d PARTITION OF fk DEFAULT;
|
||||
INSERT INTO fk VALUES (20), (30);
|
||||
DELETE FROM pk WHERE a = 20;
|
||||
ERROR: update or delete on table "pk11" violates foreign key constraint "fk_a_fkey2" on table "fk"
|
||||
DETAIL: Key (a)=(20) is still referenced from table "fk".
|
||||
ERROR: update or delete on table "pk11" violates RESTRICT setting of foreign key constraint "fk_a_fkey2" on table "fk"
|
||||
DETAIL: Key (a)=(20) is referenced from table "fk".
|
||||
UPDATE pk SET a = 90 WHERE a = 30;
|
||||
ERROR: update or delete on table "pk" violates foreign key constraint "fk_a_fkey" on table "fk"
|
||||
DETAIL: Key (a)=(30) is still referenced from table "fk".
|
||||
ERROR: update or delete on table "pk" violates RESTRICT setting of foreign key constraint "fk_a_fkey" on table "fk"
|
||||
DETAIL: Key (a)=(30) is referenced from table "fk".
|
||||
SELECT tableoid::regclass, * FROM fk;
|
||||
tableoid | a
|
||||
----------+----
|
||||
|
@ -1759,8 +1759,8 @@ 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)';
|
||||
ERROR: update or delete on table "temporal_rng" violates foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([6,7), [2018-01-01,2018-02-01)) is still referenced from table "temporal_fk_rng2rng".
|
||||
ERROR: update or delete on table "temporal_rng" violates RESTRICT setting of foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([6,7), [2018-01-01,2018-02-01)) is referenced from table "temporal_fk_rng2rng".
|
||||
-- a PK update that fails because both are referenced (even before commit):
|
||||
BEGIN;
|
||||
ALTER TABLE temporal_fk_rng2rng
|
||||
@ -1768,14 +1768,14 @@ BEGIN;
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
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');
|
||||
ERROR: update or delete on table "temporal_rng" violates foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is still referenced from table "temporal_fk_rng2rng".
|
||||
ERROR: update or delete on table "temporal_rng" violates RESTRICT setting of foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is referenced from table "temporal_fk_rng2rng".
|
||||
ROLLBACK;
|
||||
-- changing the scalar part fails:
|
||||
UPDATE temporal_rng SET id = '[7,8)'
|
||||
WHERE id = '[5,6)' AND valid_at = daterange('2018-01-01', '2018-02-01');
|
||||
ERROR: update or delete on table "temporal_rng" violates foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is still referenced from table "temporal_fk_rng2rng".
|
||||
ERROR: update or delete on table "temporal_rng" violates RESTRICT setting of foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is referenced from table "temporal_fk_rng2rng".
|
||||
-- then delete the objecting FK record and the same PK update succeeds:
|
||||
DELETE FROM temporal_fk_rng2rng WHERE id = '[3,4)';
|
||||
UPDATE temporal_rng SET valid_at = daterange('2016-01-01', '2016-02-01')
|
||||
@ -1840,8 +1840,8 @@ BEGIN;
|
||||
ALTER CONSTRAINT temporal_fk_rng2rng_fk
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
DELETE FROM temporal_rng WHERE id = '[5,6)' AND valid_at = daterange('2018-01-01', '2018-02-01');
|
||||
ERROR: update or delete on table "temporal_rng" violates foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is still referenced from table "temporal_fk_rng2rng".
|
||||
ERROR: update or delete on table "temporal_rng" violates RESTRICT setting of foreign key constraint "temporal_fk_rng2rng_fk" on table "temporal_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is referenced from table "temporal_fk_rng2rng".
|
||||
ROLLBACK;
|
||||
-- then delete the objecting FK record and the same PK delete succeeds:
|
||||
DELETE FROM temporal_fk_rng2rng WHERE id = '[3,4)';
|
||||
@ -2243,8 +2243,8 @@ 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)';
|
||||
ERROR: update or delete on table "temporal_mltrng" violates foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([6,7), {[2018-01-01,2018-02-01)}) is still referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ERROR: update or delete on table "temporal_mltrng" violates RESTRICT setting of foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([6,7), {[2018-01-01,2018-02-01)}) is referenced from table "temporal_fk_mltrng2mltrng".
|
||||
-- a PK update that fails because both are referenced (even before commit):
|
||||
BEGIN;
|
||||
ALTER TABLE temporal_fk_mltrng2mltrng
|
||||
@ -2252,14 +2252,14 @@ BEGIN;
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
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'));
|
||||
ERROR: update or delete on table "temporal_mltrng" violates foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is still referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ERROR: update or delete on table "temporal_mltrng" violates RESTRICT setting of foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ROLLBACK;
|
||||
-- changing the scalar part fails:
|
||||
UPDATE temporal_mltrng SET id = '[7,8)'
|
||||
WHERE id = '[5,6)' AND valid_at = datemultirange(daterange('2018-01-01', '2018-02-01'));
|
||||
ERROR: update or delete on table "temporal_mltrng" violates foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is still referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ERROR: update or delete on table "temporal_mltrng" violates RESTRICT setting of foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is referenced from table "temporal_fk_mltrng2mltrng".
|
||||
--
|
||||
-- test FK referenced deletes NO ACTION
|
||||
--
|
||||
@ -2317,8 +2317,8 @@ BEGIN;
|
||||
ALTER CONSTRAINT temporal_fk_mltrng2mltrng_fk
|
||||
DEFERRABLE INITIALLY DEFERRED;
|
||||
DELETE FROM temporal_mltrng WHERE id = '[5,6)' AND valid_at = datemultirange(daterange('2018-01-01', '2018-02-01'));
|
||||
ERROR: update or delete on table "temporal_mltrng" violates foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is still referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ERROR: update or delete on table "temporal_mltrng" violates RESTRICT setting of foreign key constraint "temporal_fk_mltrng2mltrng_fk" on table "temporal_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is referenced from table "temporal_fk_mltrng2mltrng".
|
||||
ROLLBACK;
|
||||
--
|
||||
-- FK between partitioned tables: ranges
|
||||
@ -2432,8 +2432,8 @@ INSERT INTO temporal_partitioned_fk_rng2rng (id, valid_at, parent_id) VALUES ('[
|
||||
DELETE FROM temporal_partitioned_rng WHERE id = '[5,6)' AND valid_at = daterange('2018-02-01', '2018-03-01');
|
||||
-- should fail:
|
||||
DELETE FROM temporal_partitioned_rng WHERE id = '[5,6)' AND valid_at = daterange('2018-01-01', '2018-02-01');
|
||||
ERROR: update or delete on table "tp1" violates foreign key constraint "temporal_partitioned_fk_rng2rng_parent_id_valid_at_fkey" on table "temporal_partitioned_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is still referenced from table "temporal_partitioned_fk_rng2rng".
|
||||
ERROR: update or delete on table "tp1" violates RESTRICT setting of foreign key constraint "temporal_partitioned_fk_rng2rng_parent_id_valid_at_fkey" on table "temporal_partitioned_fk_rng2rng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), [2018-01-01,2018-02-01)) is referenced from table "temporal_partitioned_fk_rng2rng".
|
||||
--
|
||||
-- partitioned FK referenced updates CASCADE
|
||||
--
|
||||
@ -2588,8 +2588,8 @@ INSERT INTO temporal_partitioned_fk_mltrng2mltrng (id, valid_at, parent_id) VALU
|
||||
DELETE FROM temporal_partitioned_mltrng WHERE id = '[5,6)' AND valid_at = datemultirange(daterange('2018-02-01', '2018-03-01'));
|
||||
-- should fail:
|
||||
DELETE FROM temporal_partitioned_mltrng WHERE id = '[5,6)' AND valid_at = datemultirange(daterange('2018-01-01', '2018-02-01'));
|
||||
ERROR: update or delete on table "tp1" violates foreign key constraint "temporal_partitioned_fk_mltrng2mltrng_parent_id_valid_at_fkey1" on table "temporal_partitioned_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is still referenced from table "temporal_partitioned_fk_mltrng2mltrng".
|
||||
ERROR: update or delete on table "tp1" violates RESTRICT setting of foreign key constraint "temporal_partitioned_fk_mltrng2mltrng_parent_id_valid_at_fkey1" on table "temporal_partitioned_fk_mltrng2mltrng"
|
||||
DETAIL: Key (id, valid_at)=([5,6), {[2018-01-01,2018-02-01)}) is referenced from table "temporal_partitioned_fk_mltrng2mltrng".
|
||||
--
|
||||
-- partitioned FK referenced updates CASCADE
|
||||
--
|
||||
|
Loading…
x
Reference in New Issue
Block a user