aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ri_triggers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ri_triggers.c')
-rw-r--r--src/backend/utils/adt/ri_triggers.c115
1 files changed, 85 insertions, 30 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 96269fc2adb..8ebb2a50a11 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -73,11 +73,14 @@
#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK 2
#define RI_PLAN_LAST_ON_PK RI_PLAN_CHECK_LOOKUPPK_FROM_PK
/* these queries are executed against the FK (referencing) table: */
-#define RI_PLAN_CASCADE_DEL_DODELETE 3
-#define RI_PLAN_CASCADE_UPD_DOUPDATE 4
-#define RI_PLAN_RESTRICT_CHECKREF 5
-#define RI_PLAN_SETNULL_DOUPDATE 6
-#define RI_PLAN_SETDEFAULT_DOUPDATE 7
+#define RI_PLAN_CASCADE_ONDELETE 3
+#define RI_PLAN_CASCADE_ONUPDATE 4
+/* For RESTRICT, the same plan can be used for both ON DELETE and ON UPDATE triggers. */
+#define RI_PLAN_RESTRICT 5
+#define RI_PLAN_SETNULL_ONDELETE 6
+#define RI_PLAN_SETNULL_ONUPDATE 7
+#define RI_PLAN_SETDEFAULT_ONDELETE 8
+#define RI_PLAN_SETDEFAULT_ONUPDATE 9
#define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3)
#define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2)
@@ -110,6 +113,8 @@ typedef struct RI_ConstraintInfo
Oid fk_relid; /* referencing relation */
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
+ int ndelsetcols; /* number of columns referenced in ON DELETE SET clause */
+ int16 confdelsetcols[RI_MAX_NUMKEYS]; /* attnums of cols to set on delete */
char confmatchtype; /* foreign key's match type */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
@@ -180,7 +185,7 @@ static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
TupleTableSlot *oldslot,
const RI_ConstraintInfo *riinfo);
static Datum ri_restrict(TriggerData *trigdata, bool is_no_action);
-static Datum ri_set(TriggerData *trigdata, bool is_set_null);
+static Datum ri_set(TriggerData *trigdata, bool is_set_null, int tgkind);
static void quoteOneName(char *buffer, const char *name);
static void quoteRelationName(char *buffer, Relation rel);
static void ri_GenerateQual(StringInfo buf,
@@ -660,7 +665,7 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
* Fetch or prepare a saved plan for the restrict lookup (it's the same
* query for delete and update cases)
*/
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_CHECKREF);
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT);
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
@@ -767,7 +772,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
elog(ERROR, "SPI_connect failed");
/* Fetch or prepare a saved plan for the cascaded delete */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_ONDELETE);
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
@@ -876,7 +881,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
elog(ERROR, "SPI_connect failed");
/* Fetch or prepare a saved plan for the cascaded update */
- ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
+ ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_ONUPDATE);
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
@@ -970,7 +975,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
/* Share code with UPDATE case */
- return ri_set((TriggerData *) fcinfo->context, true);
+ return ri_set((TriggerData *) fcinfo->context, true, RI_TRIGTYPE_DELETE);
}
/*
@@ -985,7 +990,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
/* Share code with DELETE case */
- return ri_set((TriggerData *) fcinfo->context, true);
+ return ri_set((TriggerData *) fcinfo->context, true, RI_TRIGTYPE_UPDATE);
}
/*
@@ -1000,7 +1005,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
/* Share code with UPDATE case */
- return ri_set((TriggerData *) fcinfo->context, false);
+ return ri_set((TriggerData *) fcinfo->context, false, RI_TRIGTYPE_DELETE);
}
/*
@@ -1015,7 +1020,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
/* Share code with DELETE case */
- return ri_set((TriggerData *) fcinfo->context, false);
+ return ri_set((TriggerData *) fcinfo->context, false, RI_TRIGTYPE_UPDATE);
}
/*
@@ -1025,7 +1030,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
* NULL, and ON UPDATE SET DEFAULT.
*/
static Datum
-ri_set(TriggerData *trigdata, bool is_set_null)
+ri_set(TriggerData *trigdata, bool is_set_null, int tgkind)
{
const RI_ConstraintInfo *riinfo;
Relation fk_rel;
@@ -1033,6 +1038,7 @@ ri_set(TriggerData *trigdata, bool is_set_null)
TupleTableSlot *oldslot;
RI_QueryKey qkey;
SPIPlanPtr qplan;
+ int32 queryno;
riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
trigdata->tg_relation, true);
@@ -1051,18 +1057,28 @@ ri_set(TriggerData *trigdata, bool is_set_null)
elog(ERROR, "SPI_connect failed");
/*
- * Fetch or prepare a saved plan for the set null/default operation (it's
- * the same query for delete and update cases)
+ * Fetch or prepare a saved plan for the trigger.
*/
- ri_BuildQueryKey(&qkey, riinfo,
- (is_set_null
- ? RI_PLAN_SETNULL_DOUPDATE
- : RI_PLAN_SETDEFAULT_DOUPDATE));
+ switch (tgkind) {
+ case RI_TRIGTYPE_UPDATE:
+ queryno = is_set_null
+ ? RI_PLAN_SETNULL_ONUPDATE
+ : RI_PLAN_SETDEFAULT_ONUPDATE;
+ break;
+ case RI_TRIGTYPE_DELETE:
+ queryno = is_set_null
+ ? RI_PLAN_SETNULL_ONDELETE
+ : RI_PLAN_SETDEFAULT_ONDELETE;
+ break;
+ default:
+ elog(ERROR, "invalid tgkind passed to ri_set");
+ }
+
+ ri_BuildQueryKey(&qkey, riinfo, queryno);
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
- StringInfoData qualbuf;
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -1070,6 +1086,32 @@ ri_set(TriggerData *trigdata, bool is_set_null)
const char *qualsep;
Oid queryoids[RI_MAX_NUMKEYS];
const char *fk_only;
+ int num_cols_to_set;
+ const int16 *set_cols;
+
+ switch (tgkind) {
+ case RI_TRIGTYPE_UPDATE:
+ num_cols_to_set = riinfo->nkeys;
+ set_cols = riinfo->fk_attnums;
+ break;
+ case RI_TRIGTYPE_DELETE:
+ /*
+ * If confdelsetcols are present, then we only update
+ * the columns specified in that array, otherwise we
+ * update all the referencing columns.
+ */
+ if (riinfo->ndelsetcols != 0) {
+ num_cols_to_set = riinfo->ndelsetcols;
+ set_cols = riinfo->confdelsetcols;
+ }
+ else {
+ num_cols_to_set = riinfo->nkeys;
+ set_cols = riinfo->fk_attnums;
+ }
+ break;
+ default:
+ elog(ERROR, "invalid tgkind passed to ri_set");
+ }
/* ----------
* The query string built is
@@ -1080,13 +1122,29 @@ ri_set(TriggerData *trigdata, bool is_set_null)
* ----------
*/
initStringInfo(&querybuf);
- initStringInfo(&qualbuf);
fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
"" : "ONLY ";
quoteRelationName(fkrelname, fk_rel);
appendStringInfo(&querybuf, "UPDATE %s%s SET",
fk_only, fkrelname);
+
+ /*
+ * Add assignment clauses
+ */
querysep = "";
+ for (int i = 0; i < num_cols_to_set; i++)
+ {
+ quoteOneName(attname, RIAttName(fk_rel, set_cols[i]));
+ appendStringInfo(&querybuf,
+ "%s %s = %s",
+ querysep, attname,
+ is_set_null ? "NULL" : "DEFAULT");
+ querysep = ",";
+ }
+
+ /*
+ * Add WHERE clause
+ */
qualsep = "WHERE";
for (int i = 0; i < riinfo->nkeys; i++)
{
@@ -1097,22 +1155,17 @@ ri_set(TriggerData *trigdata, bool is_set_null)
quoteOneName(attname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf,
- "%s %s = %s",
- querysep, attname,
- is_set_null ? "NULL" : "DEFAULT");
+
sprintf(paramname, "$%d", i + 1);
- ri_GenerateQual(&qualbuf, qualsep,
+ ri_GenerateQual(&querybuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
attname, fk_type);
if (pk_coll != fk_coll && !get_collation_isdeterministic(pk_coll))
ri_GenerateQualCollation(&querybuf, pk_coll);
- querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
}
- appendBinaryStringInfo(&querybuf, qualbuf.data, qualbuf.len);
/* Prepare and save the plan */
qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
@@ -2098,7 +2151,9 @@ ri_LoadConstraintInfo(Oid constraintOid)
riinfo->pk_attnums,
riinfo->pf_eq_oprs,
riinfo->pp_eq_oprs,
- riinfo->ff_eq_oprs);
+ riinfo->ff_eq_oprs,
+ &riinfo->ndelsetcols,
+ riinfo->confdelsetcols);
ReleaseSysCache(tup);