Skip to content

Commit e723de1

Browse files
pjungwirCommitfest Bot
authored andcommitted
Add CASCADE/SET NULL/SET DEFAULT for temporal foreign keys
Previously we raised an error for these options, because their implementations require FOR PORTION OF. Now that we have temporal UPDATE/DELETE, we can implement foreign keys that use it. Author: Paul A. Jungwirth <[email protected]>
1 parent 44cdb88 commit e723de1

File tree

8 files changed

+3184
-52
lines changed

8 files changed

+3184
-52
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,9 +1850,9 @@ ALTER TABLE variants
18501850

18511851
<para>
18521852
<productname>PostgreSQL</productname> supports <literal>NO ACTION</literal>
1853-
temporal foreign keys, but not <literal>RESTRICT</literal>,
1854-
<literal>CASCADE</literal>, <literal>SET NULL</literal>, or
1855-
<literal>SET DEFAULT</literal>.
1853+
<literal>CASCADE</literal>, <literal>SET NULL</literal>, and
1854+
<literal>SET DEFAULT</literal> temporal foreign keys,
1855+
but not <literal>RESTRICT</literal>.
18561856
</para>
18571857
</sect3>
18581858
</sect2>

doc/src/sgml/ref/create_table.sgml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,7 +1315,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13151315
</para>
13161316

13171317
<para>
1318-
In a temporal foreign key, this option is not supported.
1318+
In a temporal foreign key, the delete/update will use
1319+
<literal>FOR PORTION OF</literal> semantics to constrain the
1320+
effect to the bounds being deleted/updated in the referenced row.
13191321
</para>
13201322
</listitem>
13211323
</varlistentry>
@@ -1330,7 +1332,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13301332
</para>
13311333

13321334
<para>
1333-
In a temporal foreign key, this option is not supported.
1335+
In a temporal foreign key, the change will use <literal>FOR PORTION
1336+
OF</literal> semantics to constrain the effect to the bounds being
1337+
deleted/updated in the referenced row. The column maked with
1338+
<literal>PERIOD</literal> will not be set to null.
13341339
</para>
13351340
</listitem>
13361341
</varlistentry>
@@ -1347,7 +1352,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13471352
</para>
13481353

13491354
<para>
1350-
In a temporal foreign key, this option is not supported.
1355+
In a temporal foreign key, the change will use <literal>FOR PORTION
1356+
OF</literal> semantics to constrain the effect to the bounds being
1357+
deleted/updated in the referenced row. The column marked with
1358+
<literal>PERIOD</literal> with not be set to a default value.
13511359
</para>
13521360
</listitem>
13531361
</varlistentry>

src/backend/commands/tablecmds.c

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *
562562
Relation rel, Constraint *fkconstraint,
563563
bool recurse, bool recursing,
564564
LOCKMODE lockmode);
565-
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
565+
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, const int16 fkperiodattnum,
566566
int numfksetcols, int16 *fksetcolsattnums,
567567
List *fksetcols);
568568
static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
@@ -10074,6 +10074,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1007410074
int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
1007510075
bool with_period;
1007610076
bool pk_has_without_overlaps;
10077+
int16 fkperiodattnum = InvalidAttrNumber;
1007710078
int i;
1007810079
int numfks,
1007910080
numpks,
@@ -10159,15 +10160,20 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1015910160
fkconstraint->fk_attrs,
1016010161
fkattnum, fktypoid, fkcolloid);
1016110162
with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10162-
if (with_period && !fkconstraint->fk_with_period)
10163-
ereport(ERROR,
10164-
errcode(ERRCODE_INVALID_FOREIGN_KEY),
10165-
errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10163+
if (with_period)
10164+
{
10165+
if (!fkconstraint->fk_with_period)
10166+
ereport(ERROR,
10167+
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
10168+
errmsg("foreign key uses PERIOD on the referenced table but not the referencing table")));
10169+
fkperiodattnum = fkattnum[numfks - 1];
10170+
}
1016610171

1016710172
numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
1016810173
fkconstraint->fk_del_set_cols,
1016910174
fkdelsetcols, NULL, NULL);
1017010175
numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10176+
fkperiodattnum,
1017110177
numfkdelsetcols,
1017210178
fkdelsetcols,
1017310179
fkconstraint->fk_del_set_cols);
@@ -10269,19 +10275,13 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1026910275
*/
1027010276
if (fkconstraint->fk_with_period)
1027110277
{
10272-
if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10273-
fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10274-
fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10275-
fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10278+
if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT)
1027610279
ereport(ERROR,
1027710280
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1027810281
errmsg("unsupported %s action for foreign key constraint using PERIOD",
1027910282
"ON UPDATE"));
1028010283

10281-
if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10282-
fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10283-
fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10284-
fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10284+
if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT)
1028510285
ereport(ERROR,
1028610286
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1028710287
errmsg("unsupported %s action for foreign key constraint using PERIOD",
@@ -10638,6 +10638,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1063810638
*/
1063910639
static int
1064010640
validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10641+
const int16 fkperiodattnum,
1064110642
int numfksetcols, int16 *fksetcolsattnums,
1064210643
List *fksetcols)
1064310644
{
@@ -10651,6 +10652,14 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
1065110652
/* Make sure it's in fkattnums[] */
1065210653
for (int j = 0; j < numfks; j++)
1065310654
{
10655+
if (fkperiodattnum == setcol_attnum)
10656+
{
10657+
char *col = strVal(list_nth(fksetcols, i));
10658+
10659+
ereport(ERROR,
10660+
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10661+
errmsg("column \"%s\" referenced in ON DELETE SET action cannot be PERIOD", col)));
10662+
}
1065410663
if (fkattnums[j] == setcol_attnum)
1065510664
{
1065610665
seen = true;
@@ -13890,17 +13899,26 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
1389013899
case FKCONSTR_ACTION_CASCADE:
1389113900
fk_trigger->deferrable = false;
1389213901
fk_trigger->initdeferred = false;
13893-
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13902+
if (fkconstraint->fk_with_period)
13903+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_cascade_del");
13904+
else
13905+
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
1389413906
break;
1389513907
case FKCONSTR_ACTION_SETNULL:
1389613908
fk_trigger->deferrable = false;
1389713909
fk_trigger->initdeferred = false;
13898-
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13910+
if (fkconstraint->fk_with_period)
13911+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_setnull_del");
13912+
else
13913+
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
1389913914
break;
1390013915
case FKCONSTR_ACTION_SETDEFAULT:
1390113916
fk_trigger->deferrable = false;
1390213917
fk_trigger->initdeferred = false;
13903-
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13918+
if (fkconstraint->fk_with_period)
13919+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_setdefault_del");
13920+
else
13921+
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
1390413922
break;
1390513923
default:
1390613924
elog(ERROR, "unrecognized FK action type: %d",
@@ -13950,17 +13968,26 @@ createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstr
1395013968
case FKCONSTR_ACTION_CASCADE:
1395113969
fk_trigger->deferrable = false;
1395213970
fk_trigger->initdeferred = false;
13953-
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13971+
if (fkconstraint->fk_with_period)
13972+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_cascade_upd");
13973+
else
13974+
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
1395413975
break;
1395513976
case FKCONSTR_ACTION_SETNULL:
1395613977
fk_trigger->deferrable = false;
1395713978
fk_trigger->initdeferred = false;
13958-
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13979+
if (fkconstraint->fk_with_period)
13980+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_setnull_upd");
13981+
else
13982+
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
1395913983
break;
1396013984
case FKCONSTR_ACTION_SETDEFAULT:
1396113985
fk_trigger->deferrable = false;
1396213986
fk_trigger->initdeferred = false;
13963-
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13987+
if (fkconstraint->fk_with_period)
13988+
fk_trigger->funcname = SystemFuncName("RI_FKey_period_setdefault_upd");
13989+
else
13990+
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
1396413991
break;
1396513992
default:
1396613993
elog(ERROR, "unrecognized FK action type: %d",

0 commit comments

Comments
 (0)