Merge near-duplicate code in RI triggers
authorPeter Eisentraut <peter@eisentraut.org>
Thu, 28 Feb 2019 18:08:55 +0000 (19:08 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Thu, 28 Feb 2019 19:35:55 +0000 (20:35 +0100)
Merge ri_setnull and ri_setdefault into one function ri_set.  These
functions were to a large part identical.

This is a continuation in spirit of
4797f9b519995ceca5d6b8550b5caa2ff6d19347.

Author: Corey Huinker <corey.huinker@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/0ccdd3e1-10b0-dd05-d8a7-183507c11eb1%402ndquadrant.com

src/backend/utils/adt/ri_triggers.c

index 6afaa67b09cc55f4329dfa110bb4557142beae53..ef04fa5009b0984e52450c36e0f230b5ae237ed9 100644 (file)
@@ -176,8 +176,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_setnull(TriggerData *trigdata);
-static Datum ri_setdefault(TriggerData *trigdata);
+static Datum ri_set(TriggerData *trigdata, bool is_set_null);
 static void quoteOneName(char *buffer, const char *name);
 static void quoteRelationName(char *buffer, Relation rel);
 static void ri_GenerateQual(StringInfo buf,
@@ -960,7 +959,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_setnull((TriggerData *) fcinfo->context);
+       return ri_set((TriggerData *) fcinfo->context, true);
 }
 
 /*
@@ -975,119 +974,9 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
        ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
 
        /* Share code with DELETE case */
-       return ri_setnull((TriggerData *) fcinfo->context);
+       return ri_set((TriggerData *) fcinfo->context, true);
 }
 
-/*
- * ri_setnull -
- *
- * Common code for ON DELETE SET NULL and ON UPDATE SET NULL
- */
-static Datum
-ri_setnull(TriggerData *trigdata)
-{
-       const RI_ConstraintInfo *riinfo;
-       Relation        fk_rel;
-       Relation        pk_rel;
-       TupleTableSlot *oldslot;
-       RI_QueryKey qkey;
-       SPIPlanPtr      qplan;
-
-       riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
-                                                                       trigdata->tg_relation, true);
-
-       /*
-        * Get the relation descriptors of the FK and PK tables and the old tuple.
-        *
-        * fk_rel is opened in RowExclusiveLock mode since that's what our
-        * eventual UPDATE will get on it.
-        */
-       fk_rel = table_open(riinfo->fk_relid, RowExclusiveLock);
-       pk_rel = trigdata->tg_relation;
-       oldslot = trigdata->tg_trigslot;
-
-       if (SPI_connect() != SPI_OK_CONNECT)
-               elog(ERROR, "SPI_connect failed");
-
-       /*
-        * Fetch or prepare a saved plan for the set null operation (it's
-        * the same query for delete and update cases)
-        */
-       ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DOUPDATE);
-
-       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];
-               const char *querysep;
-               const char *qualsep;
-               const char *fk_only;
-               Oid                     queryoids[RI_MAX_NUMKEYS];
-
-               /* ----------
-                * The query string built is
-                *      UPDATE [ONLY] <fktable> SET fkatt1 = NULL [, ...]
-                *                      WHERE $1 = fkatt1 [AND ...]
-                * The type id's for the $ parameters are those of the
-                * corresponding PK attributes.
-                * ----------
-                */
-               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);
-               querysep = "";
-               qualsep = "WHERE";
-               for (int i = 0; i < riinfo->nkeys; i++)
-               {
-                       Oid                     pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
-                       Oid                     fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
-
-                       quoteOneName(attname,
-                                                RIAttName(fk_rel, riinfo->fk_attnums[i]));
-                       appendStringInfo(&querybuf,
-                                                        "%s %s = NULL",
-                                                        querysep, attname);
-                       sprintf(paramname, "$%d", i + 1);
-                       ri_GenerateQual(&qualbuf, qualsep,
-                                                       paramname, pk_type,
-                                                       riinfo->pf_eq_oprs[i],
-                                                       attname, fk_type);
-                       querysep = ",";
-                       qualsep = "AND";
-                       queryoids[i] = pk_type;
-               }
-               appendStringInfoString(&querybuf, qualbuf.data);
-
-               /* Prepare and save the plan */
-               qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
-                                                        &qkey, fk_rel, pk_rel, true);
-       }
-
-       /*
-        * We have a plan now. Run it to update the existing references.
-        */
-       ri_PerformCheck(riinfo, &qkey, qplan,
-                                       fk_rel, pk_rel,
-                                       oldslot, NULL,
-                                       true,   /* must detect new rows */
-                                       SPI_OK_UPDATE);
-
-       if (SPI_finish() != SPI_OK_FINISH)
-               elog(ERROR, "SPI_finish failed");
-
-       table_close(fk_rel, RowExclusiveLock);
-
-       return PointerGetDatum(NULL);
-}
-
-
 /*
  * RI_FKey_setdefault_del -
  *
@@ -1100,7 +989,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_setdefault((TriggerData *) fcinfo->context);
+       return ri_set((TriggerData *) fcinfo->context, false);
 }
 
 /*
@@ -1115,16 +1004,17 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
        ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
 
        /* Share code with DELETE case */
-       return ri_setdefault((TriggerData *) fcinfo->context);
+       return ri_set((TriggerData *) fcinfo->context, false);
 }
 
 /*
- * ri_setdefault -
+ * ri_set -
  *
- * Common code for ON DELETE SET DEFAULT and ON UPDATE SET DEFAULT
+ * Common code for ON DELETE SET NULL, ON DELETE SET DEFAULT, ON UPDATE SET
+ * NULL, and ON UPDATE SET DEFAULT.
  */
 static Datum
-ri_setdefault(TriggerData *trigdata)
+ri_set(TriggerData *trigdata, bool is_set_null)
 {
        const RI_ConstraintInfo *riinfo;
        Relation        fk_rel;
@@ -1150,10 +1040,13 @@ ri_setdefault(TriggerData *trigdata)
                elog(ERROR, "SPI_connect failed");
 
        /*
-        * Fetch or prepare a saved plan for the set default operation
-        * (it's the same query for delete and update cases)
+        * Fetch or prepare a saved plan for the set null/default operation (it's
+        * the same query for delete and update cases)
         */
-       ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DOUPDATE);
+       ri_BuildQueryKey(&qkey, riinfo,
+                                        (is_set_null
+                                         ? RI_PLAN_SETNULL_DOUPDATE
+                                         : RI_PLAN_SETDEFAULT_DOUPDATE));
 
        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
        {
@@ -1169,7 +1062,7 @@ ri_setdefault(TriggerData *trigdata)
 
                /* ----------
                 * The query string built is
-                *      UPDATE [ONLY] <fktable> SET fkatt1 = DEFAULT [, ...]
+                *      UPDATE [ONLY] <fktable> SET fkatt1 = {NULL|DEFAULT} [, ...]
                 *                      WHERE $1 = fkatt1 [AND ...]
                 * The type id's for the $ parameters are those of the
                 * corresponding PK attributes.
@@ -1177,9 +1070,9 @@ ri_setdefault(TriggerData *trigdata)
                 */
                initStringInfo(&querybuf);
                initStringInfo(&qualbuf);
-               quoteRelationName(fkrelname, fk_rel);
                fk_only = fk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
                        "" : "ONLY ";
+               quoteRelationName(fkrelname, fk_rel);
                appendStringInfo(&querybuf, "UPDATE %s%s SET",
                                                 fk_only, fkrelname);
                querysep = "";
@@ -1192,8 +1085,9 @@ ri_setdefault(TriggerData *trigdata)
                        quoteOneName(attname,
                                                 RIAttName(fk_rel, riinfo->fk_attnums[i]));
                        appendStringInfo(&querybuf,
-                                                        "%s %s = DEFAULT",
-                                                        querysep, attname);
+                                                        "%s %s = %s",
+                                                        querysep, attname,
+                                                        is_set_null ? "NULL" : "DEFAULT");
                        sprintf(paramname, "$%d", i + 1);
                        ri_GenerateQual(&qualbuf, qualsep,
                                                        paramname, pk_type,
@@ -1224,21 +1118,26 @@ ri_setdefault(TriggerData *trigdata)
 
        table_close(fk_rel, RowExclusiveLock);
 
-       /*
-        * If we just deleted or updated the PK row whose key was equal to
-        * the FK columns' default values, and a referencing row exists in
-        * the FK table, we would have updated that row to the same values
-        * it already had --- and RI_FKey_fk_upd_check_required would
-        * hence believe no check is necessary.  So we need to do another
-        * lookup now and in case a reference still exists, abort the
-        * operation.  That is already implemented in the NO ACTION
-        * trigger, so just run it.  (This recheck is only needed in the
-        * SET DEFAULT case, since CASCADE would remove such rows in case
-        * of a DELETE operation or would change the FK key values in case
-        * of an UPDATE, while SET NULL is certain to result in rows that
-        * satisfy the FK constraint.)
-        */
-       return ri_restrict(trigdata, true);
+       if (is_set_null)
+               return PointerGetDatum(NULL);
+       else
+       {
+               /*
+                * If we just deleted or updated the PK row whose key was equal to
+                * the FK columns' default values, and a referencing row exists in
+                * the FK table, we would have updated that row to the same values
+                * it already had --- and RI_FKey_fk_upd_check_required would
+                * hence believe no check is necessary.  So we need to do another
+                * lookup now and in case a reference still exists, abort the
+                * operation.  That is already implemented in the NO ACTION
+                * trigger, so just run it.  (This recheck is only needed in the
+                * SET DEFAULT case, since CASCADE would remove such rows in case
+                * of a DELETE operation or would change the FK key values in case
+                * of an UPDATE, while SET NULL is certain to result in rows that
+                * satisfy the FK constraint.)
+                */
+               return ri_restrict(trigdata, true);
+       }
 }