mirror of
https://github.com/postgres/postgres.git
synced 2025-11-24 00:23:06 +03:00
Allow specifying column list for foreign key ON DELETE SET actions
Extend the foreign key ON DELETE actions SET NULL and SET DEFAULT by
allowing the specification of a column list, like
CREATE TABLE posts (
...
FOREIGN KEY (tenant_id, author_id) REFERENCES users ON DELETE SET NULL (author_id)
);
If a column list is specified, only those columns are set to
null/default, instead of all the columns in the foreign-key
constraint.
This is useful for multitenant or sharded schemas, where the tenant or
shard ID is included in the primary key of all tables but shouldn't be
set to null.
Author: Paul Martinez <paulmtz@google.com>
Discussion: https://www.postgresql.org/message-id/flat/CACqFVBZQyMYJV=njbSMxf+rbDHpx=W=B7AEaMKn8dWn9OZJY7w@mail.gmail.com
This commit is contained in:
@@ -68,6 +68,8 @@ CreateConstraintEntry(const char *constraintName,
|
||||
int foreignNKeys,
|
||||
char foreignUpdateType,
|
||||
char foreignDeleteType,
|
||||
const int16 *fkDeleteSetCols,
|
||||
int numFkDeleteSetCols,
|
||||
char foreignMatchType,
|
||||
const Oid *exclOp,
|
||||
Node *conExpr,
|
||||
@@ -88,6 +90,7 @@ CreateConstraintEntry(const char *constraintName,
|
||||
ArrayType *conppeqopArray;
|
||||
ArrayType *conffeqopArray;
|
||||
ArrayType *conexclopArray;
|
||||
ArrayType *confdelsetcolsArray;
|
||||
NameData cname;
|
||||
int i;
|
||||
ObjectAddress conobject;
|
||||
@@ -136,6 +139,16 @@ CreateConstraintEntry(const char *constraintName,
|
||||
fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
|
||||
conffeqopArray = construct_array(fkdatums, foreignNKeys,
|
||||
OIDOID, sizeof(Oid), true, TYPALIGN_INT);
|
||||
|
||||
if (numFkDeleteSetCols > 0)
|
||||
{
|
||||
for (i = 0; i < numFkDeleteSetCols; i++)
|
||||
fkdatums[i] = Int16GetDatum(fkDeleteSetCols[i]);
|
||||
confdelsetcolsArray = construct_array(fkdatums, numFkDeleteSetCols,
|
||||
INT2OID, 2, true, TYPALIGN_SHORT);
|
||||
}
|
||||
else
|
||||
confdelsetcolsArray = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -143,6 +156,7 @@ CreateConstraintEntry(const char *constraintName,
|
||||
conpfeqopArray = NULL;
|
||||
conppeqopArray = NULL;
|
||||
conffeqopArray = NULL;
|
||||
confdelsetcolsArray = NULL;
|
||||
}
|
||||
|
||||
if (exclOp != NULL)
|
||||
@@ -211,6 +225,11 @@ CreateConstraintEntry(const char *constraintName,
|
||||
else
|
||||
nulls[Anum_pg_constraint_conffeqop - 1] = true;
|
||||
|
||||
if (confdelsetcolsArray)
|
||||
values[Anum_pg_constraint_confdelsetcols - 1] = PointerGetDatum(confdelsetcolsArray);
|
||||
else
|
||||
nulls[Anum_pg_constraint_confdelsetcols - 1] = true;
|
||||
|
||||
if (conexclopArray)
|
||||
values[Anum_pg_constraint_conexclop - 1] = PointerGetDatum(conexclopArray);
|
||||
else
|
||||
@@ -1157,13 +1176,15 @@ get_primary_key_attnos(Oid relid, bool deferrableOk, Oid *constraintOid)
|
||||
/*
|
||||
* Extract data from the pg_constraint tuple of a foreign-key constraint.
|
||||
*
|
||||
* All arguments save the first are output arguments; the last three of them
|
||||
* can be passed as NULL if caller doesn't need them.
|
||||
* All arguments save the first are output arguments. All output arguments
|
||||
* other than numfks, conkey and confkey can be passed as NULL if caller
|
||||
* doesn't need them.
|
||||
*/
|
||||
void
|
||||
DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
|
||||
AttrNumber *conkey, AttrNumber *confkey,
|
||||
Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs)
|
||||
Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs,
|
||||
int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
|
||||
{
|
||||
Oid constrId;
|
||||
Datum adatum;
|
||||
@@ -1260,6 +1281,32 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
|
||||
pfree(arr); /* free de-toasted copy, if any */
|
||||
}
|
||||
|
||||
if (fk_del_set_cols)
|
||||
{
|
||||
adatum = SysCacheGetAttr(CONSTROID, tuple,
|
||||
Anum_pg_constraint_confdelsetcols, &isNull);
|
||||
if (isNull)
|
||||
{
|
||||
*num_fk_del_set_cols = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int num_delete_cols;
|
||||
|
||||
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
|
||||
if (ARR_NDIM(arr) != 1 ||
|
||||
ARR_HASNULL(arr) ||
|
||||
ARR_ELEMTYPE(arr) != INT2OID)
|
||||
elog(ERROR, "confdelsetcols is not a 1-D smallint array");
|
||||
num_delete_cols = ARR_DIMS(arr)[0];
|
||||
memcpy(fk_del_set_cols, ARR_DATA_PTR(arr), num_delete_cols * sizeof(int16));
|
||||
if ((Pointer) arr != DatumGetPointer(adatum))
|
||||
pfree(arr); /* free de-toasted copy, if any */
|
||||
|
||||
*num_fk_del_set_cols = num_delete_cols;
|
||||
}
|
||||
}
|
||||
|
||||
*numfks = numkeys;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user