Обсуждение: Re: [GENERAL] postgres error reporting

Поиск
Список
Период
Сортировка

Re: [GENERAL] postgres error reporting

От
Dmitry Tkach
Дата:
Tom Lane wrote:

>Dima Tkach <dmitry@openratings.com> writes:
>
>
>>What drives me crazy though is the way it reports referential integrity
>>violations:
>>
>>
>
>
>
>>ERROR:  <unnamed> referential integrity violation - key referenced from
>>child not found in parent
>>
>>
>
>
>
>>Now, is it really that hard to tell *which* value for the key was not
>>found?
>>
>>
>
>Send a patch ;-)
>
>
Ok :-) Here it is.
There was also a bug, that I fixed in "no action" triggers - they were
setting the uid to the owner of the PK table, but actually looking at
the FK, so it would
barf if it was owned by a different user, with no 'select' permission to
the PK-owner....

There are actually *two* different patches - In ri_patch.txt I also took
the liberty to eliminate some code duplication by extracting the code,
common between all of the triggers into separate functions... if you
don't like that for some reason, there is a "lighter" version -
ri_msg_patch.txt, that only takes care about the error reporting, and
fixes that table owner problem, leaving everything else intact... Just
pick the one you like better...

These are both against REL7_3_STABLE. I compiled and tested them, and
did not see any problems...

If you need a separate version for the HEAD, let me know - I can send
that too...

Dima

*** ri_triggers.c    Fri Feb 28 13:48:56 2003
--- ri_triggers.old    Thu Feb 27 13:47:36 2003
***************
*** 64,87 ****
  #define RI_PLAN_NOACTION_DEL_CHECKREF    1
  #define RI_PLAN_NOACTION_UPD_CHECKREF    1
  #define RI_PLAN_RESTRICT_DEL_CHECKREF    1
  #define RI_PLAN_RESTRICT_UPD_CHECKREF    1
  #define RI_PLAN_SETNULL_DEL_DOUPDATE    1
  #define RI_PLAN_SETNULL_UPD_DOUPDATE    1

  #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
  #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)

- #define RI_TRIGTYPE_INSERT 1
- #define RI_TRIGTYPE_UPDATE 2
- #define RI_TRIGTYPE_INUP   3
- #define RI_TRIGTYPE_DELETE 4

  /* ----------
   * RI_QueryKey
   *
   *    The key identifying a prepared SPI plan in our private hashtable
   * ----------
   */
  typedef struct RI_QueryKey
  {
      int32        constr_type;
--- 64,83 ----
***************
*** 145,202 ****
  static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
                 HeapTuple newtup, RI_QueryKey *key, int pairidx);
  static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
  static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
                    Oid tgoid, int match_type, int tgnargs, char **tgargs);

  static void ri_InitHashTables(void);
  static void *ri_FetchPreparedPlan(RI_QueryKey *key);
  static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);

- static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind);
- static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel,
-                                       Relation pk_rel, HeapTuple old_tuple,
-                                       HeapTuple new_tuple, const char *constr);
- static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel,
-                                         HeapTuple check, HeapTuple upd,
-                                         Datum *vals, char *nulls);
- static void ri_ReportViolation (const char *constr, Relation pk_rel,
-                                           Relation fk_rel, RI_QueryKey *qkey,
-                                           HeapTuple violator);
  /* ----------
   * RI_FKey_check -
   *
   *    Check foreign key existence (combined for INSERT and UPDATE).
   * ----------
   */
  static Datum
  RI_FKey_check(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;
      int            match_type;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
--- 141,201 ----
  static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
                 HeapTuple newtup, RI_QueryKey *key, int pairidx);
  static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
  static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
                    Oid tgoid, int match_type, int tgnargs, char **tgargs);

  static void ri_InitHashTables(void);
  static void *ri_FetchPreparedPlan(RI_QueryKey *key);
  static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);

  /* ----------
   * RI_FKey_check -
   *
   *    Check foreign key existence (combined for INSERT and UPDATE).
   * ----------
   */
  static Datum
  RI_FKey_check(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        check_values[RI_MAX_NUMKEYS];
+     char        check_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
      int            match_type;
+     Oid            save_uid;
+
+     save_uid = GetUserId();

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_check() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_check() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
!         !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_check() must be fired for INSERT or UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
***************
*** 284,305 ****
              qplan = SPI_saveplan(qplan);
              ri_HashPreparedPlan(&qkey, qplan);
          }

          /*
           * Execute the plan
           */
          if (SPI_connect() != SPI_OK_CONNECT)
              elog(WARNING, "SPI_connect() failed in RI_FKey_check()");

!         ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,NULL,
!                               tgargs[RI_CONSTRAINT_NAME_ARGNO]);

          if (SPI_finish() != SPI_OK_FINISH)
              elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

--- 283,314 ----
              qplan = SPI_saveplan(qplan);
              ri_HashPreparedPlan(&qkey, qplan);
          }

          /*
           * Execute the plan
           */
          if (SPI_connect() != SPI_OK_CONNECT)
              elog(WARNING, "SPI_connect() failed in RI_FKey_check()");

!         SetUserId(RelationGetForm(pk_rel)->relowner);
!
!         if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
!             elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
!
!         SetUserId(save_uid);
!
!         if (SPI_processed == 0)
!             elog(ERROR, "%s referential integrity violation - "
!                  "no rows found in %s",
!                  tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                  RelationGetRelationName(pk_rel));

          if (SPI_finish() != SPI_OK_FINISH)
              elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

***************
*** 431,455 ****

          /*
           * Prepare, save and remember the new plan.
           */
          qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
          qplan = SPI_saveplan(qplan);
          ri_HashPreparedPlan(&qkey, qplan);
      }

      /*
       * Now check that foreign key exists in PK table
       */

!     ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,new_row,
!                           tgargs[RI_CONSTRAINT_NAME_ARGNO]);

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

      /*
       * Never reached
--- 440,499 ----

          /*
           * Prepare, save and remember the new plan.
           */
          qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
          qplan = SPI_saveplan(qplan);
          ri_HashPreparedPlan(&qkey, qplan);
      }

      /*
+      * We have a plan now. Build up the arguments for SPI_execp() from the
+      * key values in the new FK tuple.
+      */
+     for (i = 0; i < qkey.nkeypairs; i++)
+     {
+         /*
+          * We can implement MATCH PARTIAL by excluding this column from
+          * the query if it is null.  Simple!  Unfortunately, the
+          * referential actions aren't so I've not bothered to do so for
+          * the moment.
+          */
+
+         check_values[i] = SPI_getbinval(new_row,
+                                         fk_rel->rd_att,
+                                       qkey.keypair[i][RI_KEYPAIR_FK_IDX],
+                                         &isnull);
+         if (isnull)
+             check_nulls[i] = 'n';
+         else
+             check_nulls[i] = ' ';
+     }
+     check_nulls[i] = '\0';
+
+     /*
       * Now check that foreign key exists in PK table
       */

!     SetUserId(RelationGetForm(pk_rel)->relowner);
!
!     if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
!         elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
!
!     SetUserId(save_uid);
!
!     if (SPI_processed == 0)
!         elog(ERROR, "%s referential integrity violation - "
!              "key referenced from %s not found in %s",
!              tgargs[RI_CONSTRAINT_NAME_ARGNO],
!              RelationGetRelationName(fk_rel),
!              RelationGetRelationName(pk_rel));

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

      /*
       * Never reached
***************
*** 491,513 ****
--- 535,563 ----
   *    Check for matching value of old pk row in current state for
   * noaction triggers. Returns false if no row was found and a fk row
   * could potentially be referencing this row, true otherwise.
   * ----------
   */
  static bool
  ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, int tgnargs, char **tgargs)
  {
      void       *qplan;
      RI_QueryKey qkey;
+     bool        isnull;
+     Datum        check_values[RI_MAX_NUMKEYS];
+     char        check_nulls[RI_MAX_NUMKEYS + 1];
      int            i;
+     Oid            save_uid;
      bool        result;

+     save_uid = GetUserId();
+
      ri_BuildQueryKeyPkCheck(&qkey, tgoid,
                              RI_PLAN_CHECK_LOOKUPPK, pk_rel,
                              tgnargs, tgargs);

      switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
      {
          case RI_KEYS_ALL_NULL:

              /*
               * No check - nothing could have been referencing this row
***************
*** 595,617 ****

          /*
           * Prepare, save and remember the new plan.
           */
          qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
          qplan = SPI_saveplan(qplan);
          ri_HashPreparedPlan(&qkey, qplan);
      }

      /*
!      * We have a plan now. Run it.
       */
!     result = ri_PerformCheck (&qkey, qplan,NULL, pk_rel, old_row, NULL, NULL);

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()");

      return result;
  }


  /* ----------
   * RI_FKey_noaction_del -
--- 645,692 ----

          /*
           * Prepare, save and remember the new plan.
           */
          qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
          qplan = SPI_saveplan(qplan);
          ri_HashPreparedPlan(&qkey, qplan);
      }

      /*
!      * We have a plan now. Build up the arguments for SPI_execp() from the
!      * key values in the new FK tuple.
       */
!     for (i = 0; i < qkey.nkeypairs; i++)
!     {
!         check_values[i] = SPI_getbinval(old_row,
!                                         pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                         &isnull);
!         if (isnull)
!             check_nulls[i] = 'n';
!         else
!             check_nulls[i] = ' ';
!     }
!     check_nulls[i] = '\0';
!
!     /*
!      * Now check that foreign key exists in PK table
!      */
!
!     SetUserId(RelationGetForm(pk_rel)->relowner);
!
!     if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
!         elog(ERROR, "SPI_execp() failed in ri_Check_Pk_Match()");
!
!     SetUserId(save_uid);
!
!     result = (SPI_processed != 0);

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()");

      return result;
  }


  /* ----------
   * RI_FKey_noaction_del -
***************
*** 625,655 ****
  RI_FKey_noaction_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;
      int            match_type;


      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
--- 700,741 ----
  RI_FKey_noaction_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        del_values[RI_MAX_NUMKEYS];
+     char        del_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
      int            match_type;
+     Oid            save_uid;

+     save_uid = GetUserId();

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_noaction_del() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_noaction_del() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_noaction_del() must be fired for DELETE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
***************
*** 768,792 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */

!             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,
!                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
--- 854,905 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the deleted PK tuple.
!              */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 del_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     del_nulls[i] = 'n';
!                 else
!                     del_nulls[i] = ' ';
!             }
!             del_nulls[i] = '\0';
!
!             /*
!              * Now check for existing references
               */
+             SetUserId(RelationGetForm(pk_rel)->relowner);
+
+             if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
+                 elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");

!             SetUserId(save_uid);
!
!             if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
***************
*** 817,846 ****
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;
      int            match_type;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
--- 930,971 ----
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
      int            match_type;
+     Oid            save_uid;
+
+     save_uid = GetUserId();

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_noaction_upd() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_noaction_upd() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_noaction_upd() must be fired for UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
***************
*** 970,993 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */
!             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,
!                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL noaction update.
--- 1095,1146 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the updated PK tuple.
!              */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 upd_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!             }
!             upd_nulls[i] = '\0';
!
!             /*
!              * Now check for existing references
               */
!             SetUserId(RelationGetForm(pk_rel)->relowner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");
!
!             SetUserId(save_uid);
!
!             if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL noaction update.
***************
*** 1015,1043 ****
  RI_FKey_cascade_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
--- 1168,1207 ----
  RI_FKey_cascade_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        del_values[RI_MAX_NUMKEYS];
+     char        del_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_cascade_del() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_cascade_del() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_cascade_del() must be fired for DELETE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
***************
*** 1058,1077 ****
--- 1222,1242 ----
       */
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 6) a) i):
               *        MATCH <unspecified> or MATCH FULL
               *            ... ON DELETE CASCADE
               * ----------
               */
***************
*** 1142,1166 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments
!              * from the key values in the deleted PK tuple and delete the
!              * referencing rows
               */
!             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL cascaded delete.
--- 1307,1352 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the deleted PK tuple.
               */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 del_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     del_nulls[i] = 'n';
!                 else
!                     del_nulls[i] = ' ';
!             }
!             del_nulls[i] = '\0';
!
!             /*
!              * Now delete constraint
!              */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, del_values, del_nulls, 0) != SPI_OK_DELETE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_del()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL cascaded delete.
***************
*** 1189,1218 ****
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;
      int            j;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
--- 1375,1415 ----
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS * 2];
+     char        upd_nulls[RI_MAX_NUMKEYS * 2 + 1];
+     bool        isnull;
      int            i;
      int            j;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_cascade_upd() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_cascade_upd() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_cascade_upd() must be fired for UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
***************
*** 1234,1253 ****
--- 1431,1451 ----
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      new_row = trigdata->tg_newtuple;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 7) a) i):
               *        MATCH <unspecified> or MATCH FULL
               *            ... ON UPDATE CASCADE
               * ----------
               */
***************
*** 1339,1361 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */
!             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,new_row,NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL cascade update.
--- 1537,1591 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the updated PK tuple.
!              */
!             for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
!             {
!                 upd_values[i] = SPI_getbinval(new_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!
!                 upd_values[j] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[j] = 'n';
!                 else
!                     upd_nulls[j] = ' ';
!             }
!             upd_nulls[j] = '\0';
!
!             /*
!              * Now update the existing references
               */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_upd()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL cascade update.
***************
*** 1390,1418 ****
  RI_FKey_restrict_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
--- 1620,1659 ----
  RI_FKey_restrict_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        del_values[RI_MAX_NUMKEYS];
+     char        del_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_restrict_del() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_restrict_del() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_restrict_del() must be fired for DELETE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
***************
*** 1433,1452 ****
--- 1674,1694 ----
       */
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
      pk_rel = trigdata->tg_relation;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 6) a) iv):
               *        MATCH <unspecified> or MATCH FULL
               *            ... ON DELETE CASCADE
               * ----------
               */
***************
*** 1519,1543 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */

!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL,
!                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
--- 1761,1813 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the deleted PK tuple.
!              */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 del_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     del_nulls[i] = 'n';
!                 else
!                     del_nulls[i] = ' ';
!             }
!             del_nulls[i] = '\0';
!
!             /*
!              * Now check for existing references
               */
+             save_uid = GetUserId();
+             SetUserId(fk_owner);
+
+             if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
+                 elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");
+
+             SetUserId(save_uid);

!             if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
***************
*** 1573,1601 ****
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
--- 1843,1882 ----
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_restrict_upd() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_restrict_upd() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_restrict_upd() must be fired for UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
***************
*** 1617,1636 ****
--- 1898,1918 ----
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
      pk_rel = trigdata->tg_relation;
      new_row = trigdata->tg_newtuple;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 6) a) iv):
               *        MATCH <unspecified> or MATCH FULL
               *            ... ON DELETE CASCADE
               * ----------
               */
***************
*** 1713,1736 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */
!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL,
!                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict update.
--- 1995,2049 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the updated PK tuple.
               */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 upd_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!             }
!             upd_nulls[i] = '\0';
!
!             /*
!              * Now check for existing references
!              */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             SetUserId(RelationGetForm(pk_rel)->relowner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");
!
!             SetUserId(save_uid);
!
!             if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict update.
***************
*** 1758,1786 ****
  RI_FKey_setnull_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
--- 2071,2110 ----
  RI_FKey_setnull_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_setnull_del() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setnull_del() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setnull_del() must be fired for DELETE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
***************
*** 1801,1820 ****
--- 2125,2145 ----
       */
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 6) a) ii):
               *        MATCH <UNSPECIFIED> or MATCH FULL
               *            ... ON DELETE SET NULL
               * ----------
               */
***************
*** 1895,1917 ****

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Run it.
               */
!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
--- 2220,2265 ----

                  /*
                   * Prepare, save and remember the new plan.
                   */
                  qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
                  qplan = SPI_saveplan(qplan);
                  ri_HashPreparedPlan(&qkey, qplan);
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the updated PK tuple.
               */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 upd_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!             }
!             upd_nulls[i] = '\0';
!
!             /*
!              * Now update the existing references
!              */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_del()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
***************
*** 1940,1970 ****
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            i;
      int            match_type;
      bool        use_cached_query;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
--- 2288,2329 ----
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
      int            i;
      int            match_type;
      bool        use_cached_query;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_setnull_upd() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setnull_upd() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setnull_upd() must be fired for UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
***************
*** 1987,2006 ****
--- 2346,2366 ----
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      new_row = trigdata->tg_newtuple;
      old_row = trigdata->tg_trigtuple;
      match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (match_type)
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 7) a) ii) 2):
               *        MATCH FULL
               *            ... ON UPDATE SET NULL
               * ----------
               */
***************
*** 2128,2151 ****
                   * "standard" plan.
                   */
                  if (use_cached_query)
                  {
                      qplan = SPI_saveplan(qplan);
                      ri_HashPreparedPlan(&qkey, qplan);
                  }
              }

              /*
!              * We have a plan now.
               * Now update the existing references
               */
!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null update.
--- 2488,2533 ----
                   * "standard" plan.
                   */
                  if (use_cached_query)
                  {
                      qplan = SPI_saveplan(qplan);
                      ri_HashPreparedPlan(&qkey, qplan);
                  }
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the updated PK tuple.
!              */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 upd_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!             }
!             upd_nulls[i] = '\0';
!
!             /*
               * Now update the existing references
               */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_upd()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null update.
***************
*** 2173,2200 ****
  RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
--- 2555,2594 ----
  RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
+     int            i;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_setdefault_del() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setdefault_del() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setdefault_del() must be fired for DELETE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
***************
*** 2215,2234 ****
--- 2609,2629 ----
       */
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 6) a) iii):
               *        MATCH <UNSPECIFIED> or MATCH FULL
               *            ... ON DELETE SET DEFAULT
               * ----------
               */
***************
*** 2353,2376 ****
                                      spi_plan->targetlist);
                              spi_qptle->expr = stringToNode(defval[j].adbin);

                              break;
                          }
                      }
                  }
              }

              /*
!              * We have a plan now.
               * Now update the existing references
               */
!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
--- 2748,2793 ----
                                      spi_plan->targetlist);
                              spi_qptle->expr = stringToNode(defval[j].adbin);

                              break;
                          }
                      }
                  }
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the deleted PK tuple.
!              */
!             for (i = 0; i < qkey.nkeypairs; i++)
!             {
!                 upd_values[i] = SPI_getbinval(old_row,
!                                               pk_rel->rd_att,
!                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
!                                               &isnull);
!                 if (isnull)
!                     upd_nulls[i] = 'n';
!                 else
!                     upd_nulls[i] = ' ';
!             }
!             upd_nulls[i] = '\0';
!
!             /*
               * Now update the existing references
               */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_del()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
***************
*** 2399,2427 ****
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
      int            match_type;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
--- 2816,2856 ----
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
      int            tgnargs;
      char      **tgargs;
      Relation    fk_rel;
      Relation    pk_rel;
      HeapTuple    new_row;
      HeapTuple    old_row;
      RI_QueryKey qkey;
      void       *qplan;
+     Datum        upd_values[RI_MAX_NUMKEYS];
+     char        upd_nulls[RI_MAX_NUMKEYS + 1];
+     bool        isnull;
+     int            i;
      int            match_type;
+     Oid            save_uid;
+     Oid            fk_owner;

      ReferentialIntegritySnapshotOverride = true;

      /*
       * Check that this is a valid trigger call on the right time and
       * event.
       */
!     if (!CALLED_AS_TRIGGER(fcinfo))
!         elog(ERROR, "RI_FKey_setdefault_upd() not fired by trigger manager");
!     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
!         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setdefault_upd() must be fired AFTER ROW");
!     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
!         elog(ERROR, "RI_FKey_setdefault_upd() must be fired for UPDATE");

      /*
       * Check for the correct # of call arguments
       */
      tgnargs = trigdata->tg_trigger->tgnargs;
      tgargs = trigdata->tg_trigger->tgargs;
      if (tgnargs < 4 || (tgnargs % 2) != 0)
          elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
      if (tgnargs > RI_MAX_ARGUMENTS)
          elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
***************
*** 2443,2462 ****
--- 2872,2892 ----
      if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
          elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
               "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
               trigdata->tg_trigger->tgname,
               RelationGetRelationName(trigdata->tg_relation));

      fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
      pk_rel = trigdata->tg_relation;
      new_row = trigdata->tg_newtuple;
      old_row = trigdata->tg_trigtuple;
+     fk_owner = RelationGetForm(fk_rel)->relowner;

      match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);

      switch (match_type)
      {
              /* ----------
               * SQL3 11.9 <referential constraint definition>
               *    Gereral rules 7) a) iii):
               *        MATCH <UNSPECIFIED> or MATCH FULL
               *            ... ON UPDATE SET DEFAULT
***************
*** 2609,2632 ****
                                  spi_qptle->expr = stringToNode(defval[j].adbin);

                                  break;
                              }
                          }
                      }
                  }
              }

              /*
!              * We have a plan now. Run it.
               */

!             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
--- 3039,3084 ----
                                  spi_qptle->expr = stringToNode(defval[j].adbin);

                                  break;
                              }
                          }
                      }
                  }
              }

              /*
!              * We have a plan now. Build up the arguments for SPI_execp()
!              * from the key values in the deleted PK tuple.
               */
+             for (i = 0; i < qkey.nkeypairs; i++)
+             {
+                 upd_values[i] = SPI_getbinval(old_row,
+                                               pk_rel->rd_att,
+                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
+                                               &isnull);
+                 if (isnull)
+                     upd_nulls[i] = 'n';
+                 else
+                     upd_nulls[i] = ' ';
+             }
+             upd_nulls[i] = '\0';

!             /*
!              * Now update the existing references
!              */
!             save_uid = GetUserId();
!             SetUserId(fk_owner);
!
!             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
!                 elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_upd()");
!
!             SetUserId(save_uid);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()");

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL set null delete.
***************
*** 2853,3039 ****
          fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
          if (fno == SPI_ERROR_NOATTRIBUTE)
              elog(ERROR, "constraint %s: table %s does not have an attribute %s",
                   argv[RI_CONSTRAINT_NAME_ARGNO],
                   RelationGetRelationName(pk_rel),
                   argv[j + 1]);
          key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
      }
  }

- static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind)
- {
-    TriggerData *trigdata = (TriggerData *) fcinfo -> context;
-
-     if (!CALLED_AS_TRIGGER(fcinfo))
-         elog(ERROR, "%s() not fired by trigger manager", name);
-     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
-         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
-         elog(ERROR, "%s() must be fired AFTER ROW", name);
-
-     if (tgkind == RI_TRIGTYPE_INUP &&
-          !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
-          !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-         elog(ERROR, "%s() must be fired for INSERT or UPDATE", name);
-     else if (tgkind == RI_TRIGTYPE_INSERT &&
-                 !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
-        elog (ERROR, "%s() must be fired for INSERT", name);
-     else if (tgkind == RI_TRIGTYPE_UPDATE &&
-                 !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
-        elog (ERROR, "%s() must be fired for UPDATE", name);
-     else if (tgkind == RI_TRIGTYPE_DELETE &&
-                 !TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
-        elog (ERROR, "%s() must be fired for DELETE", name);
- }
-
-
- static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel,
-                                       Relation pk_rel, HeapTuple old_tuple,
-                                       HeapTuple new_tuple, const char *constr)
- {
-   /* The query is always run against the FK table except
-     * when this is an update/insert trigger on the FK table itself -
-     * either RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS, which
-     * is equivalent to nkeypairs == 0
-     */
-   Relation query_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
-                         qkey -> nkeypairs == 0 ? pk_rel : fk_rel;
-
-   /** The values for the query are taken from the table on which the trigger
-      * is called - it is normally the other one with respect to query_rel.
-      * An exception is ri_Check_Pk_Match(), which uses the PK table for both
-      * (the case when fk_rel == NULL)
-      */
-   Relation source_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK &&
-                          fk_rel ?  fk_rel : pk_rel;
-
-   /** If the vals are taken from fk_rel, then ..FK_IDX, otherwise PK_IDX */
-   int key_idx = source_rel == fk_rel ? RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
-
-   /** If constr is given, this is a 'noaction' trigger - we only want to check
-      * if there are any rows that satisfy the query, thus limit=1, also, if
-      * the query is LOOKUPPK, we are just checking if the row is there...
-      * Otherwise, we want to perform some action on the matching rows, so
-      * do not limit the number of results.
-      */
-   int limit = constr || qkey -> constr_queryno==RI_PLAN_CHECK_LOOKUPPK ? 1 : 0;
-
-   Oid save_uid = GetUserId ();
-
-   Datum        vals [RI_MAX_NUMKEYS * 2];
-   char        nulls[RI_MAX_NUMKEYS * 2 + 1];
-
-   ri_ExtractValues (qkey, key_idx, source_rel,
-                           new_tuple ? new_tuple : old_tuple,
-                           new_tuple ? old_tuple : NULL, vals, nulls);
-
-   SetUserId (RelationGetForm (query_rel) -> relowner);
-
-   if (SPI_execp(qplan, vals, nulls, limit) < 0)
-      elog(ERROR, "SPI_execp() failed in ri_PerformCheck()");
-
-   SetUserId(save_uid);
-
-   if (constr &&
-         (SPI_processed==0) == (qkey->constr_queryno==RI_PLAN_CHECK_LOOKUPPK))
-      ri_ReportViolation (constr, pk_rel, fk_rel, qkey,
-                                 new_tuple ? new_tuple : old_tuple);
-
-   return SPI_processed != 0;
- }
-
- static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel,
-                                         HeapTuple check, HeapTuple upd,
-                                         Datum *vals, char *nulls)
- {
-   int i,j;
-   bool isnull;
-
-   for (i = 0, j = qkey -> nkeypairs; i < qkey -> nkeypairs; i++, j++)
-   {
-      vals[i]=SPI_getbinval(check,rel->rd_att,qkey->keypair[i][key_idx],&isnull);
-      nulls[i]=isnull ? 'n' : ' ';
-
-      if (upd)
-      {
-         vals[j]=SPI_getbinval(upd,rel->rd_att,qkey->keypair[i][key_idx],&isnull);
-         nulls[j] = isnull ? 'n' : ' ';
-      }
-   }
-   nulls [upd ? j : i] = '\0';
- }
-
- static void ri_ReportViolation (const char *constr, Relation pk_rel,
-                                           Relation fk_rel, RI_QueryKey *qkey,
-                                           HeapTuple violator)
- {
-   static char *null_str = "null";
-
-   char  key_names  [512];
-   char  key_values [512];
-
-   char  *name_ptr = key_names;
-   char  *val_ptr  = key_values;
-   int  idx = 0;
-
-   /* If the failed constraint was on insert/update to the FK table,
-     * we want the key names and values extracted from there, and the error
-     * message to look like 'key blah referenced from FK not found in PK'
-     * Otherwise, the attr names and values come from the PK table and the
-     * message looks like 'key blah in PK still referenced in FK'.
-     * So, rel is set to where the tuple description is coming from
-     * (FK in the first case, and PK in the second case), and it also is
-     * the first relation mentioned in the message, other_rel is respectively
-     * the other relation.
-     */
-
-   bool onfk   = (qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
-
-   int  key_idx = onfk       ?  RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
-   Relation rel = onfk       ?  fk_rel : pk_rel;
-   Relation other_rel = onfk ?  pk_rel : fk_rel;
-
-   /* Special case - if there are no keys at all, this is a 'no column'
-     * constraint - no need to try to extract the values, and the message
-     * in this case looks differently
-     */
-   if (qkey -> nkeypairs == 0)
-      elog(ERROR, "%s referential integrity violation - no rows found in %s",
-             constr, RelationGetRelationName(pk_rel));
-
-   for (idx = 0; idx < qkey->nkeypairs; idx++)
-   {
-      int  fnum  = qkey->keypair[idx][key_idx];
-      char *name = SPI_fname (rel->rd_att, fnum);
-      char *val  = SPI_getvalue (violator, rel->rd_att, fnum);
-      if (!val)
-         val = null_str;
-
-      if (name_ptr - key_names  + strlen(name) + 5  >= 512 ||
-           val_ptr  - key_values + strlen (val) + 5  >= 512)
-      {
-         sprintf (name_ptr, "...");
-         sprintf (val_ptr,  "...");
-         break;
-      }
-
-      name_ptr += sprintf (name_ptr, "%s%s", idx > 0 ? "," : "", name);
-      val_ptr  += sprintf (val_ptr,  "%s%s", idx > 0 ? "," : "", val);
-   }
-
-   elog (ERROR, "%s referential integrity violation - key (%s)=(%s) "
-                  "%s %s %s in %s", constr, key_names, key_values,
-                     onfk ?  "referenced from" : "in", RelationGetRelationName (rel),
-                     onfk ?  "not found" : "still referenced",
-                     RelationGetRelationName (other_rel));
- }
-
  /* ----------
   * ri_BuildQueryKeyPkCheck -
   *
   *    Build up a new hashtable key for a prepared SPI plan of a
   *    check for PK rows in noaction triggers.
   *
   *        constr_type is FULL
   *        constr_id is the OID of the pg_trigger row that invoked us
   *        constr_queryno is an internal number of the query inside the proc
   *        pk_relid is the OID of referenced relation
--- 3305,3324 ----
***************
*** 3378,3402 ****
      /*
       * If not found, lookup the operator, then do the function manager
       * lookup, and remember that info.
       */
      if (!entry)
      {
          Oid            opr_proc;
          FmgrInfo    finfo;

          opr_proc = compatible_oper_funcid(makeList1(makeString("=")),
!                                                      typeid, typeid, true);
          if (!OidIsValid(opr_proc))
!           elog (ERROR,
!                   "ri_AttributesEqual(): cannot find '=' operator for type %u",
!                   typeid);

          /*
           * Since fmgr_info could fail, call it *before* creating the
           * hashtable entry --- otherwise we could elog leaving an
           * incomplete entry in the hashtable.  Also, because this will be
           * a permanent table entry, we must make sure any subsidiary
           * structures of the fmgr record are kept in TopMemoryContext.
           */
          fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);

--- 3663,3687 ----
      /*
       * If not found, lookup the operator, then do the function manager
       * lookup, and remember that info.
       */
      if (!entry)
      {
          Oid            opr_proc;
          FmgrInfo    finfo;

          opr_proc = compatible_oper_funcid(makeList1(makeString("=")),
!                                           typeid, typeid, true);
          if (!OidIsValid(opr_proc))
!             elog(ERROR,
!             "ri_AttributesEqual(): cannot find '=' operator for type %u",
!                  typeid);

          /*
           * Since fmgr_info could fail, call it *before* creating the
           * hashtable entry --- otherwise we could elog leaving an
           * incomplete entry in the hashtable.  Also, because this will be
           * a permanent table entry, we must make sure any subsidiary
           * structures of the fmgr record are kept in TopMemoryContext.
           */
          fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);

*** ri_triggers.msg    Thu Feb 27 13:53:22 2003
--- ri_triggers.old    Thu Feb 27 13:47:36 2003
***************
*** 141,163 ****
  static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
                 HeapTuple newtup, RI_QueryKey *key, int pairidx);
  static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
  static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
                    Oid tgoid, int match_type, int tgnargs, char **tgargs);

  static void ri_InitHashTables(void);
  static void *ri_FetchPreparedPlan(RI_QueryKey *key);
  static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);

- static void ri_ReportViolation (const char *constr, Relation pk_rel,
-                                           Relation fk_rel, RI_QueryKey *qkey,
-                                           HeapTuple violator);
  /* ----------
   * RI_FKey_check -
   *
   *    Check foreign key existence (combined for INSERT and UPDATE).
   * ----------
   */
  static Datum
  RI_FKey_check(PG_FUNCTION_ARGS)
  {
      TriggerData *trigdata = (TriggerData *) fcinfo->context;
--- 141,160 ----
***************
*** 294,315 ****
              elog(WARNING, "SPI_connect() failed in RI_FKey_check()");

          SetUserId(RelationGetForm(pk_rel)->relowner);

          if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
              elog(ERROR, "SPI_execp() failed in RI_FKey_check()");

          SetUserId(save_uid);

          if (SPI_processed == 0)
!           ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
!                                      &qkey, NULL);

          if (SPI_finish() != SPI_OK_FINISH)
              elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

--- 291,314 ----
              elog(WARNING, "SPI_connect() failed in RI_FKey_check()");

          SetUserId(RelationGetForm(pk_rel)->relowner);

          if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
              elog(ERROR, "SPI_execp() failed in RI_FKey_check()");

          SetUserId(save_uid);

          if (SPI_processed == 0)
!             elog(ERROR, "%s referential integrity violation - "
!                  "no rows found in %s",
!                  tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                  RelationGetRelationName(pk_rel));

          if (SPI_finish() != SPI_OK_FINISH)
              elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

***************
*** 476,497 ****
       */

      SetUserId(RelationGetForm(pk_rel)->relowner);

      if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
          elog(ERROR, "SPI_execp() failed in RI_FKey_check()");

      SetUserId(save_uid);

      if (SPI_processed == 0)
!       ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
!                                  &qkey, new_row);

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

      /*
       * Never reached
--- 475,499 ----
       */

      SetUserId(RelationGetForm(pk_rel)->relowner);

      if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
          elog(ERROR, "SPI_execp() failed in RI_FKey_check()");

      SetUserId(save_uid);

      if (SPI_processed == 0)
!         elog(ERROR, "%s referential integrity violation - "
!              "key referenced from %s not found in %s",
!              tgargs[RI_CONSTRAINT_NAME_ARGNO],
!              RelationGetRelationName(fk_rel),
!              RelationGetRelationName(pk_rel));

      if (SPI_finish() != SPI_OK_FINISH)
          elog(WARNING, "SPI_finish() failed in RI_FKey_check()");

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

      /*
       * Never reached
***************
*** 871,900 ****
                  if (isnull)
                      del_nulls[i] = 'n';
                  else
                      del_nulls[i] = ' ';
              }
              del_nulls[i] = '\0';

              /*
               * Now check for existing references
               */
!             SetUserId(RelationGetForm(fk_rel)->relowner);

              if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                                          pk_rel, fk_rel, &qkey, old_row);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
--- 873,905 ----
                  if (isnull)
                      del_nulls[i] = 'n';
                  else
                      del_nulls[i] = ' ';
              }
              del_nulls[i] = '\0';

              /*
               * Now check for existing references
               */
!             SetUserId(RelationGetForm(pk_rel)->relowner);

              if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
***************
*** 1109,1138 ****
                  if (isnull)
                      upd_nulls[i] = 'n';
                  else
                      upd_nulls[i] = ' ';
              }
              upd_nulls[i] = '\0';

              /*
               * Now check for existing references
               */
!             SetUserId(RelationGetForm(fk_rel)->relowner);

              if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                                          pk_rel, fk_rel, &qkey, old_row);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL noaction update.
--- 1114,1146 ----
                  if (isnull)
                      upd_nulls[i] = 'n';
                  else
                      upd_nulls[i] = ' ';
              }
              upd_nulls[i] = '\0';

              /*
               * Now check for existing references
               */
!             SetUserId(RelationGetForm(pk_rel)->relowner);

              if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL noaction update.
***************
*** 1781,1802 ****
               */
              save_uid = GetUserId();
              SetUserId(fk_owner);

              if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                                          pk_rel, fk_rel, &qkey, old_row);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
--- 1789,1813 ----
               */
              save_uid = GetUserId();
              SetUserId(fk_owner);

              if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict delete.
***************
*** 2014,2035 ****
              SetUserId(fk_owner);

              SetUserId(RelationGetForm(pk_rel)->relowner);

              if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                                          pk_rel, fk_rel, &qkey, old_row);

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict update.
--- 2025,2049 ----
              SetUserId(fk_owner);

              SetUserId(RelationGetForm(pk_rel)->relowner);

              if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
                  elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");

              SetUserId(save_uid);

              if (SPI_processed > 0)
!                 elog(ERROR, "%s referential integrity violation - "
!                      "key in %s still referenced from %s",
!                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
!                      RelationGetRelationName(pk_rel),
!                      RelationGetRelationName(fk_rel));

              if (SPI_finish() != SPI_OK_FINISH)
                  elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

              /*
               * Handle MATCH PARTIAL restrict update.
***************
*** 3289,3373 ****
          key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;

          fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
          if (fno == SPI_ERROR_NOATTRIBUTE)
              elog(ERROR, "constraint %s: table %s does not have an attribute %s",
                   argv[RI_CONSTRAINT_NAME_ARGNO],
                   RelationGetRelationName(pk_rel),
                   argv[j + 1]);
          key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
      }
- }
-
- static void ri_ReportViolation (const char *constr, Relation pk_rel,
-                                           Relation fk_rel, RI_QueryKey *qkey,
-                                           HeapTuple violator)
- {
-   static char *null_str = "null";
-
-   char  key_names  [512];
-   char  key_values [512];
-
-   char  *name_ptr = key_names;
-   char  *val_ptr  = key_values;
-   int  idx = 0;
-
-   /* If the failed constraint was on insert/update to the FK table,
-     * we want the key names and values extracted from there, and the error
-     * message to look like 'key blah referenced from FK not found in PK'
-     * Otherwise, the attr names and values come from the PK table and the
-     * message looks like 'key blah in PK still referenced in FK'.
-     * So, rel is set to where the tuple description is coming from
-     * (FK in the first case, and PK in the second case), and it also is
-     * the first relation mentioned in the message, other_rel is respectively
-     * the other relation.
-     */
-
-   bool onfk   = (qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
-
-   int  key_idx = onfk       ?  RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
-   Relation rel = onfk       ?  fk_rel : pk_rel;
-   Relation other_rel = onfk ?  pk_rel : fk_rel;
-
-   /* Special case - if there are no keys at all, this is a 'no column'
-     * constraint - no need to try to extract the values, and the message
-     * in this case looks differently
-     */
-   if (qkey -> nkeypairs == 0)
-      elog(ERROR, "%s referential integrity violation - no rows found in %s",
-             constr, RelationGetRelationName(pk_rel));
-
-   for (idx = 0; idx < qkey->nkeypairs; idx++)
-   {
-      int  fnum  = qkey->keypair[idx][key_idx];
-      char *name = SPI_fname (rel->rd_att, fnum);
-      char *val  = SPI_getvalue (violator, rel->rd_att, fnum);
-      if (!val)
-         val = null_str;
-
-      if (name_ptr - key_names  + strlen(name) + 5  >= 512 ||
-           val_ptr  - key_values + strlen (val) + 5  >= 512)
-      {
-         sprintf (name_ptr, "...");
-         sprintf (val_ptr,  "...");
-         break;
-      }
-
-      name_ptr += sprintf (name_ptr, "%s%s", idx > 0 ? "," : "", name);
-      val_ptr  += sprintf (val_ptr,  "%s%s", idx > 0 ? "," : "", val);
-   }
-
-   elog (ERROR, "%s referential integrity violation - key (%s)=(%s) "
-                  "%s %s %s in %s", constr, key_names, key_values,
-                     onfk ?  "referenced from" : "in", RelationGetRelationName (rel),
-                     onfk ?  "not found" : "still referenced",
-                     RelationGetRelationName (other_rel));
  }

  /* ----------
   * ri_BuildQueryKeyPkCheck -
   *
   *    Build up a new hashtable key for a prepared SPI plan of a
   *    check for PK rows in noaction triggers.
   *
   *        constr_type is FULL
   *        constr_id is the OID of the pg_trigger row that invoked us
--- 3303,3322 ----

Re: [GENERAL] postgres error reporting

От
Tom Lane
Дата:

Re: [GENERAL] postgres error reporting

От
Dmitry Tkach
Дата:
What? :-)
I received it completely empty :-)

Dima

Tom Lane wrote:


Re: [GENERAL] postgres error reporting

От
Bruce Momjian
Дата:
FYI, this has already been applied, two days ago.

---------------------------------------------------------------------------

Dmitry Tkach wrote:
>
> Tom Lane wrote:
>
> >Dima Tkach <dmitry@openratings.com> writes:
> >
> >
> >>What drives me crazy though is the way it reports referential integrity
> >>violations:
> >>
> >>
> >
> >
> >
> >>ERROR:  <unnamed> referential integrity violation - key referenced from
> >>child not found in parent
> >>
> >>
> >
> >
> >
> >>Now, is it really that hard to tell *which* value for the key was not
> >>found?
> >>
> >>
> >
> >Send a patch ;-)
> >
> >
> Ok :-) Here it is.
> There was also a bug, that I fixed in "no action" triggers - they were
> setting the uid to the owner of the PK table, but actually looking at
> the FK, so it would
> barf if it was owned by a different user, with no 'select' permission to
> the PK-owner....
>
> There are actually *two* different patches - In ri_patch.txt I also took
> the liberty to eliminate some code duplication by extracting the code,
> common between all of the triggers into separate functions... if you
> don't like that for some reason, there is a "lighter" version -
> ri_msg_patch.txt, that only takes care about the error reporting, and
> fixes that table owner problem, leaving everything else intact... Just
> pick the one you like better...
>
> These are both against REL7_3_STABLE. I compiled and tested them, and
> did not see any problems...
>
> If you need a separate version for the HEAD, let me know - I can send
> that too...
>
> Dima
>

> *** ri_triggers.c    Fri Feb 28 13:48:56 2003
> --- ri_triggers.old    Thu Feb 27 13:47:36 2003
> ***************
> *** 64,87 ****
>   #define RI_PLAN_NOACTION_DEL_CHECKREF    1
>   #define RI_PLAN_NOACTION_UPD_CHECKREF    1
>   #define RI_PLAN_RESTRICT_DEL_CHECKREF    1
>   #define RI_PLAN_RESTRICT_UPD_CHECKREF    1
>   #define RI_PLAN_SETNULL_DEL_DOUPDATE    1
>   #define RI_PLAN_SETNULL_UPD_DOUPDATE    1
>
>   #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
>   #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
>
> - #define RI_TRIGTYPE_INSERT 1
> - #define RI_TRIGTYPE_UPDATE 2
> - #define RI_TRIGTYPE_INUP   3
> - #define RI_TRIGTYPE_DELETE 4
>
>   /* ----------
>    * RI_QueryKey
>    *
>    *    The key identifying a prepared SPI plan in our private hashtable
>    * ----------
>    */
>   typedef struct RI_QueryKey
>   {
>       int32        constr_type;
> --- 64,83 ----
> ***************
> *** 145,202 ****
>   static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
>                  HeapTuple newtup, RI_QueryKey *key, int pairidx);
>   static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
>   static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
>                     Oid tgoid, int match_type, int tgnargs, char **tgargs);
>
>   static void ri_InitHashTables(void);
>   static void *ri_FetchPreparedPlan(RI_QueryKey *key);
>   static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
>
> - static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind);
> - static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel,
> -                                       Relation pk_rel, HeapTuple old_tuple,
> -                                       HeapTuple new_tuple, const char *constr);
> - static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel,
> -                                         HeapTuple check, HeapTuple upd,
> -                                         Datum *vals, char *nulls);
> - static void ri_ReportViolation (const char *constr, Relation pk_rel,
> -                                           Relation fk_rel, RI_QueryKey *qkey,
> -                                           HeapTuple violator);
>   /* ----------
>    * RI_FKey_check -
>    *
>    *    Check foreign key existence (combined for INSERT and UPDATE).
>    * ----------
>    */
>   static Datum
>   RI_FKey_check(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>       int            match_type;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
> --- 141,201 ----
>   static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
>                  HeapTuple newtup, RI_QueryKey *key, int pairidx);
>   static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
>   static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
>                     Oid tgoid, int match_type, int tgnargs, char **tgargs);
>
>   static void ri_InitHashTables(void);
>   static void *ri_FetchPreparedPlan(RI_QueryKey *key);
>   static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
>
>   /* ----------
>    * RI_FKey_check -
>    *
>    *    Check foreign key existence (combined for INSERT and UPDATE).
>    * ----------
>    */
>   static Datum
>   RI_FKey_check(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        check_values[RI_MAX_NUMKEYS];
> +     char        check_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
>       int            match_type;
> +     Oid            save_uid;
> +
> +     save_uid = GetUserId();
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_check() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_check() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
> !         !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_check() must be fired for INSERT or UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_check()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()",
> ***************
> *** 284,305 ****
>               qplan = SPI_saveplan(qplan);
>               ri_HashPreparedPlan(&qkey, qplan);
>           }
>
>           /*
>            * Execute the plan
>            */
>           if (SPI_connect() != SPI_OK_CONNECT)
>               elog(WARNING, "SPI_connect() failed in RI_FKey_check()");
>
> !         ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,NULL,
> !                               tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>           if (SPI_finish() != SPI_OK_FINISH)
>               elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>           heap_close(pk_rel, RowShareLock);
>
>           return PointerGetDatum(NULL);
>
>       }
>
> --- 283,314 ----
>               qplan = SPI_saveplan(qplan);
>               ri_HashPreparedPlan(&qkey, qplan);
>           }
>
>           /*
>            * Execute the plan
>            */
>           if (SPI_connect() != SPI_OK_CONNECT)
>               elog(WARNING, "SPI_connect() failed in RI_FKey_check()");
>
> !         SetUserId(RelationGetForm(pk_rel)->relowner);
> !
> !         if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
> !             elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
> !
> !         SetUserId(save_uid);
> !
> !         if (SPI_processed == 0)
> !             elog(ERROR, "%s referential integrity violation - "
> !                  "no rows found in %s",
> !                  tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                  RelationGetRelationName(pk_rel));
>
>           if (SPI_finish() != SPI_OK_FINISH)
>               elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>           heap_close(pk_rel, RowShareLock);
>
>           return PointerGetDatum(NULL);
>
>       }
>
> ***************
> *** 431,455 ****
>
>           /*
>            * Prepare, save and remember the new plan.
>            */
>           qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>           qplan = SPI_saveplan(qplan);
>           ri_HashPreparedPlan(&qkey, qplan);
>       }
>
>       /*
>        * Now check that foreign key exists in PK table
>        */
>
> !     ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,new_row,
> !                           tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>       heap_close(pk_rel, RowShareLock);
>
>       return PointerGetDatum(NULL);
>
>       /*
>        * Never reached
> --- 440,499 ----
>
>           /*
>            * Prepare, save and remember the new plan.
>            */
>           qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>           qplan = SPI_saveplan(qplan);
>           ri_HashPreparedPlan(&qkey, qplan);
>       }
>
>       /*
> +      * We have a plan now. Build up the arguments for SPI_execp() from the
> +      * key values in the new FK tuple.
> +      */
> +     for (i = 0; i < qkey.nkeypairs; i++)
> +     {
> +         /*
> +          * We can implement MATCH PARTIAL by excluding this column from
> +          * the query if it is null.  Simple!  Unfortunately, the
> +          * referential actions aren't so I've not bothered to do so for
> +          * the moment.
> +          */
> +
> +         check_values[i] = SPI_getbinval(new_row,
> +                                         fk_rel->rd_att,
> +                                       qkey.keypair[i][RI_KEYPAIR_FK_IDX],
> +                                         &isnull);
> +         if (isnull)
> +             check_nulls[i] = 'n';
> +         else
> +             check_nulls[i] = ' ';
> +     }
> +     check_nulls[i] = '\0';
> +
> +     /*
>        * Now check that foreign key exists in PK table
>        */
>
> !     SetUserId(RelationGetForm(pk_rel)->relowner);
> !
> !     if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
> !         elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
> !
> !     SetUserId(save_uid);
> !
> !     if (SPI_processed == 0)
> !         elog(ERROR, "%s referential integrity violation - "
> !              "key referenced from %s not found in %s",
> !              tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !              RelationGetRelationName(fk_rel),
> !              RelationGetRelationName(pk_rel));
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>       heap_close(pk_rel, RowShareLock);
>
>       return PointerGetDatum(NULL);
>
>       /*
>        * Never reached
> ***************
> *** 491,513 ****
> --- 535,563 ----
>    *    Check for matching value of old pk row in current state for
>    * noaction triggers. Returns false if no row was found and a fk row
>    * could potentially be referencing this row, true otherwise.
>    * ----------
>    */
>   static bool
>   ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, int tgnargs, char **tgargs)
>   {
>       void       *qplan;
>       RI_QueryKey qkey;
> +     bool        isnull;
> +     Datum        check_values[RI_MAX_NUMKEYS];
> +     char        check_nulls[RI_MAX_NUMKEYS + 1];
>       int            i;
> +     Oid            save_uid;
>       bool        result;
>
> +     save_uid = GetUserId();
> +
>       ri_BuildQueryKeyPkCheck(&qkey, tgoid,
>                               RI_PLAN_CHECK_LOOKUPPK, pk_rel,
>                               tgnargs, tgargs);
>
>       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
>       {
>           case RI_KEYS_ALL_NULL:
>
>               /*
>                * No check - nothing could have been referencing this row
> ***************
> *** 595,617 ****
>
>           /*
>            * Prepare, save and remember the new plan.
>            */
>           qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>           qplan = SPI_saveplan(qplan);
>           ri_HashPreparedPlan(&qkey, qplan);
>       }
>
>       /*
> !      * We have a plan now. Run it.
>        */
> !     result = ri_PerformCheck (&qkey, qplan,NULL, pk_rel, old_row, NULL, NULL);
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()");
>
>       return result;
>   }
>
>
>   /* ----------
>    * RI_FKey_noaction_del -
> --- 645,692 ----
>
>           /*
>            * Prepare, save and remember the new plan.
>            */
>           qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>           qplan = SPI_saveplan(qplan);
>           ri_HashPreparedPlan(&qkey, qplan);
>       }
>
>       /*
> !      * We have a plan now. Build up the arguments for SPI_execp() from the
> !      * key values in the new FK tuple.
>        */
> !     for (i = 0; i < qkey.nkeypairs; i++)
> !     {
> !         check_values[i] = SPI_getbinval(old_row,
> !                                         pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                         &isnull);
> !         if (isnull)
> !             check_nulls[i] = 'n';
> !         else
> !             check_nulls[i] = ' ';
> !     }
> !     check_nulls[i] = '\0';
> !
> !     /*
> !      * Now check that foreign key exists in PK table
> !      */
> !
> !     SetUserId(RelationGetForm(pk_rel)->relowner);
> !
> !     if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
> !         elog(ERROR, "SPI_execp() failed in ri_Check_Pk_Match()");
> !
> !     SetUserId(save_uid);
> !
> !     result = (SPI_processed != 0);
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()");
>
>       return result;
>   }
>
>
>   /* ----------
>    * RI_FKey_noaction_del -
> ***************
> *** 625,655 ****
>   RI_FKey_noaction_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>       int            match_type;
>
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
> --- 700,741 ----
>   RI_FKey_noaction_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        del_values[RI_MAX_NUMKEYS];
> +     char        del_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
>       int            match_type;
> +     Oid            save_uid;
>
> +     save_uid = GetUserId();
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_noaction_del() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_noaction_del() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_noaction_del() must be fired for DELETE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()",
> ***************
> *** 768,792 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
>
> !             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,
> !                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> --- 854,905 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the deleted PK tuple.
> !              */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 del_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     del_nulls[i] = 'n';
> !                 else
> !                     del_nulls[i] = ' ';
> !             }
> !             del_nulls[i] = '\0';
> !
> !             /*
> !              * Now check for existing references
>                */
> +             SetUserId(RelationGetForm(pk_rel)->relowner);
> +
> +             if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
> +                 elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");
>
> !             SetUserId(save_uid);
> !
> !             if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> ***************
> *** 817,846 ****
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>       int            match_type;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
> --- 930,971 ----
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
>       int            match_type;
> +     Oid            save_uid;
> +
> +     save_uid = GetUserId();
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_noaction_upd() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_noaction_upd() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_noaction_upd() must be fired for UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()",
> ***************
> *** 970,993 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
> !             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,
> !                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL noaction update.
> --- 1095,1146 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the updated PK tuple.
> !              */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 upd_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !             }
> !             upd_nulls[i] = '\0';
> !
> !             /*
> !              * Now check for existing references
>                */
> !             SetUserId(RelationGetForm(pk_rel)->relowner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");
> !
> !             SetUserId(save_uid);
> !
> !             if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL noaction update.
> ***************
> *** 1015,1043 ****
>   RI_FKey_cascade_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
> --- 1168,1207 ----
>   RI_FKey_cascade_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        del_values[RI_MAX_NUMKEYS];
> +     char        del_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_cascade_del() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_cascade_del() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_cascade_del() must be fired for DELETE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()",
> ***************
> *** 1058,1077 ****
> --- 1222,1242 ----
>        */
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 6) a) i):
>                *        MATCH <unspecified> or MATCH FULL
>                *            ... ON DELETE CASCADE
>                * ----------
>                */
> ***************
> *** 1142,1166 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments
> !              * from the key values in the deleted PK tuple and delete the
> !              * referencing rows
>                */
> !             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL cascaded delete.
> --- 1307,1352 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the deleted PK tuple.
>                */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 del_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     del_nulls[i] = 'n';
> !                 else
> !                     del_nulls[i] = ' ';
> !             }
> !             del_nulls[i] = '\0';
> !
> !             /*
> !              * Now delete constraint
> !              */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, del_values, del_nulls, 0) != SPI_OK_DELETE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_del()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL cascaded delete.
> ***************
> *** 1189,1218 ****
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>       int            j;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
> --- 1375,1415 ----
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS * 2];
> +     char        upd_nulls[RI_MAX_NUMKEYS * 2 + 1];
> +     bool        isnull;
>       int            i;
>       int            j;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_cascade_upd() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_cascade_upd() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_cascade_upd() must be fired for UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()",
> ***************
> *** 1234,1253 ****
> --- 1431,1451 ----
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       new_row = trigdata->tg_newtuple;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 7) a) i):
>                *        MATCH <unspecified> or MATCH FULL
>                *            ... ON UPDATE CASCADE
>                * ----------
>                */
> ***************
> *** 1339,1361 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
> !             ri_PerformCheck  (&qkey, qplan,fk_rel,pk_rel,old_row,new_row,NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL cascade update.
> --- 1537,1591 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the updated PK tuple.
> !              */
> !             for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++)
> !             {
> !                 upd_values[i] = SPI_getbinval(new_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !
> !                 upd_values[j] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[j] = 'n';
> !                 else
> !                     upd_nulls[j] = ' ';
> !             }
> !             upd_nulls[j] = '\0';
> !
> !             /*
> !              * Now update the existing references
>                */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_upd()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL cascade update.
> ***************
> *** 1390,1418 ****
>   RI_FKey_restrict_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
> --- 1620,1659 ----
>   RI_FKey_restrict_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        del_values[RI_MAX_NUMKEYS];
> +     char        del_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_restrict_del() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_restrict_del() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_restrict_del() must be fired for DELETE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()",
> ***************
> *** 1433,1452 ****
> --- 1674,1694 ----
>        */
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
>       pk_rel = trigdata->tg_relation;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 6) a) iv):
>                *        MATCH <unspecified> or MATCH FULL
>                *            ... ON DELETE CASCADE
>                * ----------
>                */
> ***************
> *** 1519,1543 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
>
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL,
> !                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> --- 1761,1813 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the deleted PK tuple.
> !              */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 del_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     del_nulls[i] = 'n';
> !                 else
> !                     del_nulls[i] = ' ';
> !             }
> !             del_nulls[i] = '\0';
> !
> !             /*
> !              * Now check for existing references
>                */
> +             save_uid = GetUserId();
> +             SetUserId(fk_owner);
> +
> +             if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
> +                 elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");
> +
> +             SetUserId(save_uid);
>
> !             if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> ***************
> *** 1573,1601 ****
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
> --- 1843,1882 ----
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_restrict_upd() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_restrict_upd() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_restrict_upd() must be fired for UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()",
> ***************
> *** 1617,1636 ****
> --- 1898,1918 ----
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock);
>       pk_rel = trigdata->tg_relation;
>       new_row = trigdata->tg_newtuple;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 6) a) iv):
>                *        MATCH <unspecified> or MATCH FULL
>                *            ... ON DELETE CASCADE
>                * ----------
>                */
> ***************
> *** 1713,1736 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL,
> !                                     tgargs[RI_CONSTRAINT_NAME_ARGNO]);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict update.
> --- 1995,2049 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the updated PK tuple.
>                */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 upd_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !             }
> !             upd_nulls[i] = '\0';
> !
> !             /*
> !              * Now check for existing references
> !              */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             SetUserId(RelationGetForm(pk_rel)->relowner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");
> !
> !             SetUserId(save_uid);
> !
> !             if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict update.
> ***************
> *** 1758,1786 ****
>   RI_FKey_setnull_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
> --- 2071,2110 ----
>   RI_FKey_setnull_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_setnull_del() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setnull_del() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setnull_del() must be fired for DELETE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()",
> ***************
> *** 1801,1820 ****
> --- 2125,2145 ----
>        */
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 6) a) ii):
>                *        MATCH <UNSPECIFIED> or MATCH FULL
>                *            ... ON DELETE SET NULL
>                * ----------
>                */
> ***************
> *** 1895,1917 ****
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> --- 2220,2265 ----
>
>                   /*
>                    * Prepare, save and remember the new plan.
>                    */
>                   qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids);
>                   qplan = SPI_saveplan(qplan);
>                   ri_HashPreparedPlan(&qkey, qplan);
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the updated PK tuple.
>                */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 upd_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !             }
> !             upd_nulls[i] = '\0';
> !
> !             /*
> !              * Now update the existing references
> !              */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_del()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> ***************
> *** 1940,1970 ****
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            i;
>       int            match_type;
>       bool        use_cached_query;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
> --- 2288,2329 ----
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
>       int            i;
>       int            match_type;
>       bool        use_cached_query;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_setnull_upd() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setnull_upd() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setnull_upd() must be fired for UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()",
> ***************
> *** 1987,2006 ****
> --- 2346,2366 ----
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       new_row = trigdata->tg_newtuple;
>       old_row = trigdata->tg_trigtuple;
>       match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (match_type)
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 7) a) ii) 2):
>                *        MATCH FULL
>                *            ... ON UPDATE SET NULL
>                * ----------
>                */
> ***************
> *** 2128,2151 ****
>                    * "standard" plan.
>                    */
>                   if (use_cached_query)
>                   {
>                       qplan = SPI_saveplan(qplan);
>                       ri_HashPreparedPlan(&qkey, qplan);
>                   }
>               }
>
>               /*
> !              * We have a plan now.
>                * Now update the existing references
>                */
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null update.
> --- 2488,2533 ----
>                    * "standard" plan.
>                    */
>                   if (use_cached_query)
>                   {
>                       qplan = SPI_saveplan(qplan);
>                       ri_HashPreparedPlan(&qkey, qplan);
>                   }
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the updated PK tuple.
> !              */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 upd_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !             }
> !             upd_nulls[i] = '\0';
> !
> !             /*
>                * Now update the existing references
>                */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_upd()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null update.
> ***************
> *** 2173,2200 ****
>   RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
> --- 2555,2594 ----
>   RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
> +     int            i;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_setdefault_del() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setdefault_del() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setdefault_del() must be fired for DELETE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()",
> ***************
> *** 2215,2234 ****
> --- 2609,2629 ----
>        */
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]))
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 6) a) iii):
>                *        MATCH <UNSPECIFIED> or MATCH FULL
>                *            ... ON DELETE SET DEFAULT
>                * ----------
>                */
> ***************
> *** 2353,2376 ****
>                                       spi_plan->targetlist);
>                               spi_qptle->expr = stringToNode(defval[j].adbin);
>
>                               break;
>                           }
>                       }
>                   }
>               }
>
>               /*
> !              * We have a plan now.
>                * Now update the existing references
>                */
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> --- 2748,2793 ----
>                                       spi_plan->targetlist);
>                               spi_qptle->expr = stringToNode(defval[j].adbin);
>
>                               break;
>                           }
>                       }
>                   }
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the deleted PK tuple.
> !              */
> !             for (i = 0; i < qkey.nkeypairs; i++)
> !             {
> !                 upd_values[i] = SPI_getbinval(old_row,
> !                                               pk_rel->rd_att,
> !                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> !                                               &isnull);
> !                 if (isnull)
> !                     upd_nulls[i] = 'n';
> !                 else
> !                     upd_nulls[i] = ' ';
> !             }
> !             upd_nulls[i] = '\0';
> !
> !             /*
>                * Now update the existing references
>                */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_del()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> ***************
> *** 2399,2427 ****
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
>       int            match_type;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
> --- 2816,2856 ----
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
>       int            tgnargs;
>       char      **tgargs;
>       Relation    fk_rel;
>       Relation    pk_rel;
>       HeapTuple    new_row;
>       HeapTuple    old_row;
>       RI_QueryKey qkey;
>       void       *qplan;
> +     Datum        upd_values[RI_MAX_NUMKEYS];
> +     char        upd_nulls[RI_MAX_NUMKEYS + 1];
> +     bool        isnull;
> +     int            i;
>       int            match_type;
> +     Oid            save_uid;
> +     Oid            fk_owner;
>
>       ReferentialIntegritySnapshotOverride = true;
>
>       /*
>        * Check that this is a valid trigger call on the right time and
>        * event.
>        */
> !     if (!CALLED_AS_TRIGGER(fcinfo))
> !         elog(ERROR, "RI_FKey_setdefault_upd() not fired by trigger manager");
> !     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> !         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setdefault_upd() must be fired AFTER ROW");
> !     if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> !         elog(ERROR, "RI_FKey_setdefault_upd() must be fired for UPDATE");
>
>       /*
>        * Check for the correct # of call arguments
>        */
>       tgnargs = trigdata->tg_trigger->tgnargs;
>       tgargs = trigdata->tg_trigger->tgargs;
>       if (tgnargs < 4 || (tgnargs % 2) != 0)
>           elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()");
>       if (tgnargs > RI_MAX_ARGUMENTS)
>           elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()",
> ***************
> *** 2443,2462 ****
> --- 2872,2892 ----
>       if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid))
>           elog(ERROR, "No target table given for trigger \"%s\" on \"%s\""
>                "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT",
>                trigdata->tg_trigger->tgname,
>                RelationGetRelationName(trigdata->tg_relation));
>
>       fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock);
>       pk_rel = trigdata->tg_relation;
>       new_row = trigdata->tg_newtuple;
>       old_row = trigdata->tg_trigtuple;
> +     fk_owner = RelationGetForm(fk_rel)->relowner;
>
>       match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);
>
>       switch (match_type)
>       {
>               /* ----------
>                * SQL3 11.9 <referential constraint definition>
>                *    Gereral rules 7) a) iii):
>                *        MATCH <UNSPECIFIED> or MATCH FULL
>                *            ... ON UPDATE SET DEFAULT
> ***************
> *** 2609,2632 ****
>                                   spi_qptle->expr = stringToNode(defval[j].adbin);
>
>                                   break;
>                               }
>                           }
>                       }
>                   }
>               }
>
>               /*
> !              * We have a plan now. Run it.
>                */
>
> !             ri_PerformCheck  (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> --- 3039,3084 ----
>                                   spi_qptle->expr = stringToNode(defval[j].adbin);
>
>                                   break;
>                               }
>                           }
>                       }
>                   }
>               }
>
>               /*
> !              * We have a plan now. Build up the arguments for SPI_execp()
> !              * from the key values in the deleted PK tuple.
>                */
> +             for (i = 0; i < qkey.nkeypairs; i++)
> +             {
> +                 upd_values[i] = SPI_getbinval(old_row,
> +                                               pk_rel->rd_att,
> +                                       qkey.keypair[i][RI_KEYPAIR_PK_IDX],
> +                                               &isnull);
> +                 if (isnull)
> +                     upd_nulls[i] = 'n';
> +                 else
> +                     upd_nulls[i] = ' ';
> +             }
> +             upd_nulls[i] = '\0';
>
> !             /*
> !              * Now update the existing references
> !              */
> !             save_uid = GetUserId();
> !             SetUserId(fk_owner);
> !
> !             if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE)
> !                 elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_upd()");
> !
> !             SetUserId(save_uid);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()");
>
>               heap_close(fk_rel, RowExclusiveLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL set null delete.
> ***************
> *** 2853,3039 ****
>           fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
>           if (fno == SPI_ERROR_NOATTRIBUTE)
>               elog(ERROR, "constraint %s: table %s does not have an attribute %s",
>                    argv[RI_CONSTRAINT_NAME_ARGNO],
>                    RelationGetRelationName(pk_rel),
>                    argv[j + 1]);
>           key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
>       }
>   }
>
> - static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind)
> - {
> -    TriggerData *trigdata = (TriggerData *) fcinfo -> context;
> -
> -     if (!CALLED_AS_TRIGGER(fcinfo))
> -         elog(ERROR, "%s() not fired by trigger manager", name);
> -     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
> -         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
> -         elog(ERROR, "%s() must be fired AFTER ROW", name);
> -
> -     if (tgkind == RI_TRIGTYPE_INUP &&
> -          !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
> -          !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> -         elog(ERROR, "%s() must be fired for INSERT or UPDATE", name);
> -     else if (tgkind == RI_TRIGTYPE_INSERT &&
> -                 !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
> -        elog (ERROR, "%s() must be fired for INSERT", name);
> -     else if (tgkind == RI_TRIGTYPE_UPDATE &&
> -                 !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
> -        elog (ERROR, "%s() must be fired for UPDATE", name);
> -     else if (tgkind == RI_TRIGTYPE_DELETE &&
> -                 !TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
> -        elog (ERROR, "%s() must be fired for DELETE", name);
> - }
> -
> -
> - static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel,
> -                                       Relation pk_rel, HeapTuple old_tuple,
> -                                       HeapTuple new_tuple, const char *constr)
> - {
> -   /* The query is always run against the FK table except
> -     * when this is an update/insert trigger on the FK table itself -
> -     * either RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS, which
> -     * is equivalent to nkeypairs == 0
> -     */
> -   Relation query_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
> -                         qkey -> nkeypairs == 0 ? pk_rel : fk_rel;
> -
> -   /** The values for the query are taken from the table on which the trigger
> -      * is called - it is normally the other one with respect to query_rel.
> -      * An exception is ri_Check_Pk_Match(), which uses the PK table for both
> -      * (the case when fk_rel == NULL)
> -      */
> -   Relation source_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK &&
> -                          fk_rel ?  fk_rel : pk_rel;
> -
> -   /** If the vals are taken from fk_rel, then ..FK_IDX, otherwise PK_IDX */
> -   int key_idx = source_rel == fk_rel ? RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
> -
> -   /** If constr is given, this is a 'noaction' trigger - we only want to check
> -      * if there are any rows that satisfy the query, thus limit=1, also, if
> -      * the query is LOOKUPPK, we are just checking if the row is there...
> -      * Otherwise, we want to perform some action on the matching rows, so
> -      * do not limit the number of results.
> -      */
> -   int limit = constr || qkey -> constr_queryno==RI_PLAN_CHECK_LOOKUPPK ? 1 : 0;
> -
> -   Oid save_uid = GetUserId ();
> -
> -   Datum        vals [RI_MAX_NUMKEYS * 2];
> -   char        nulls[RI_MAX_NUMKEYS * 2 + 1];
> -
> -   ri_ExtractValues (qkey, key_idx, source_rel,
> -                           new_tuple ? new_tuple : old_tuple,
> -                           new_tuple ? old_tuple : NULL, vals, nulls);
> -
> -   SetUserId (RelationGetForm (query_rel) -> relowner);
> -
> -   if (SPI_execp(qplan, vals, nulls, limit) < 0)
> -      elog(ERROR, "SPI_execp() failed in ri_PerformCheck()");
> -
> -   SetUserId(save_uid);
> -
> -   if (constr &&
> -         (SPI_processed==0) == (qkey->constr_queryno==RI_PLAN_CHECK_LOOKUPPK))
> -      ri_ReportViolation (constr, pk_rel, fk_rel, qkey,
> -                                 new_tuple ? new_tuple : old_tuple);
> -
> -   return SPI_processed != 0;
> - }
> -
> - static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel,
> -                                         HeapTuple check, HeapTuple upd,
> -                                         Datum *vals, char *nulls)
> - {
> -   int i,j;
> -   bool isnull;
> -
> -   for (i = 0, j = qkey -> nkeypairs; i < qkey -> nkeypairs; i++, j++)
> -   {
> -      vals[i]=SPI_getbinval(check,rel->rd_att,qkey->keypair[i][key_idx],&isnull);
> -      nulls[i]=isnull ? 'n' : ' ';
> -
> -      if (upd)
> -      {
> -         vals[j]=SPI_getbinval(upd,rel->rd_att,qkey->keypair[i][key_idx],&isnull);
> -         nulls[j] = isnull ? 'n' : ' ';
> -      }
> -   }
> -   nulls [upd ? j : i] = '\0';
> - }
> -
> - static void ri_ReportViolation (const char *constr, Relation pk_rel,
> -                                           Relation fk_rel, RI_QueryKey *qkey,
> -                                           HeapTuple violator)
> - {
> -   static char *null_str = "null";
> -
> -   char  key_names  [512];
> -   char  key_values [512];
> -
> -   char  *name_ptr = key_names;
> -   char  *val_ptr  = key_values;
> -   int  idx = 0;
> -
> -   /* If the failed constraint was on insert/update to the FK table,
> -     * we want the key names and values extracted from there, and the error
> -     * message to look like 'key blah referenced from FK not found in PK'
> -     * Otherwise, the attr names and values come from the PK table and the
> -     * message looks like 'key blah in PK still referenced in FK'.
> -     * So, rel is set to where the tuple description is coming from
> -     * (FK in the first case, and PK in the second case), and it also is
> -     * the first relation mentioned in the message, other_rel is respectively
> -     * the other relation.
> -     */
> -
> -   bool onfk   = (qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
> -
> -   int  key_idx = onfk       ?  RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
> -   Relation rel = onfk       ?  fk_rel : pk_rel;
> -   Relation other_rel = onfk ?  pk_rel : fk_rel;
> -
> -   /* Special case - if there are no keys at all, this is a 'no column'
> -     * constraint - no need to try to extract the values, and the message
> -     * in this case looks differently
> -     */
> -   if (qkey -> nkeypairs == 0)
> -      elog(ERROR, "%s referential integrity violation - no rows found in %s",
> -             constr, RelationGetRelationName(pk_rel));
> -
> -   for (idx = 0; idx < qkey->nkeypairs; idx++)
> -   {
> -      int  fnum  = qkey->keypair[idx][key_idx];
> -      char *name = SPI_fname (rel->rd_att, fnum);
> -      char *val  = SPI_getvalue (violator, rel->rd_att, fnum);
> -      if (!val)
> -         val = null_str;
> -
> -      if (name_ptr - key_names  + strlen(name) + 5  >= 512 ||
> -           val_ptr  - key_values + strlen (val) + 5  >= 512)
> -      {
> -         sprintf (name_ptr, "...");
> -         sprintf (val_ptr,  "...");
> -         break;
> -      }
> -
> -      name_ptr += sprintf (name_ptr, "%s%s", idx > 0 ? "," : "", name);
> -      val_ptr  += sprintf (val_ptr,  "%s%s", idx > 0 ? "," : "", val);
> -   }
> -
> -   elog (ERROR, "%s referential integrity violation - key (%s)=(%s) "
> -                  "%s %s %s in %s", constr, key_names, key_values,
> -                     onfk ?  "referenced from" : "in", RelationGetRelationName (rel),
> -                     onfk ?  "not found" : "still referenced",
> -                     RelationGetRelationName (other_rel));
> - }
> -
>   /* ----------
>    * ri_BuildQueryKeyPkCheck -
>    *
>    *    Build up a new hashtable key for a prepared SPI plan of a
>    *    check for PK rows in noaction triggers.
>    *
>    *        constr_type is FULL
>    *        constr_id is the OID of the pg_trigger row that invoked us
>    *        constr_queryno is an internal number of the query inside the proc
>    *        pk_relid is the OID of referenced relation
> --- 3305,3324 ----
> ***************
> *** 3378,3402 ****
>       /*
>        * If not found, lookup the operator, then do the function manager
>        * lookup, and remember that info.
>        */
>       if (!entry)
>       {
>           Oid            opr_proc;
>           FmgrInfo    finfo;
>
>           opr_proc = compatible_oper_funcid(makeList1(makeString("=")),
> !                                                      typeid, typeid, true);
>           if (!OidIsValid(opr_proc))
> !           elog (ERROR,
> !                   "ri_AttributesEqual(): cannot find '=' operator for type %u",
> !                   typeid);
>
>           /*
>            * Since fmgr_info could fail, call it *before* creating the
>            * hashtable entry --- otherwise we could elog leaving an
>            * incomplete entry in the hashtable.  Also, because this will be
>            * a permanent table entry, we must make sure any subsidiary
>            * structures of the fmgr record are kept in TopMemoryContext.
>            */
>           fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);
>
> --- 3663,3687 ----
>       /*
>        * If not found, lookup the operator, then do the function manager
>        * lookup, and remember that info.
>        */
>       if (!entry)
>       {
>           Oid            opr_proc;
>           FmgrInfo    finfo;
>
>           opr_proc = compatible_oper_funcid(makeList1(makeString("=")),
> !                                           typeid, typeid, true);
>           if (!OidIsValid(opr_proc))
> !             elog(ERROR,
> !             "ri_AttributesEqual(): cannot find '=' operator for type %u",
> !                  typeid);
>
>           /*
>            * Since fmgr_info could fail, call it *before* creating the
>            * hashtable entry --- otherwise we could elog leaving an
>            * incomplete entry in the hashtable.  Also, because this will be
>            * a permanent table entry, we must make sure any subsidiary
>            * structures of the fmgr record are kept in TopMemoryContext.
>            */
>           fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);
>

> *** ri_triggers.msg    Thu Feb 27 13:53:22 2003
> --- ri_triggers.old    Thu Feb 27 13:47:36 2003
> ***************
> *** 141,163 ****
>   static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup,
>                  HeapTuple newtup, RI_QueryKey *key, int pairidx);
>   static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue);
>   static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row,
>                     Oid tgoid, int match_type, int tgnargs, char **tgargs);
>
>   static void ri_InitHashTables(void);
>   static void *ri_FetchPreparedPlan(RI_QueryKey *key);
>   static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan);
>
> - static void ri_ReportViolation (const char *constr, Relation pk_rel,
> -                                           Relation fk_rel, RI_QueryKey *qkey,
> -                                           HeapTuple violator);
>   /* ----------
>    * RI_FKey_check -
>    *
>    *    Check foreign key existence (combined for INSERT and UPDATE).
>    * ----------
>    */
>   static Datum
>   RI_FKey_check(PG_FUNCTION_ARGS)
>   {
>       TriggerData *trigdata = (TriggerData *) fcinfo->context;
> --- 141,160 ----
> ***************
> *** 294,315 ****
>               elog(WARNING, "SPI_connect() failed in RI_FKey_check()");
>
>           SetUserId(RelationGetForm(pk_rel)->relowner);
>
>           if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
>               elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
>
>           SetUserId(save_uid);
>
>           if (SPI_processed == 0)
> !           ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
> !                                      &qkey, NULL);
>
>           if (SPI_finish() != SPI_OK_FINISH)
>               elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>           heap_close(pk_rel, RowShareLock);
>
>           return PointerGetDatum(NULL);
>
>       }
>
> --- 291,314 ----
>               elog(WARNING, "SPI_connect() failed in RI_FKey_check()");
>
>           SetUserId(RelationGetForm(pk_rel)->relowner);
>
>           if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
>               elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
>
>           SetUserId(save_uid);
>
>           if (SPI_processed == 0)
> !             elog(ERROR, "%s referential integrity violation - "
> !                  "no rows found in %s",
> !                  tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                  RelationGetRelationName(pk_rel));
>
>           if (SPI_finish() != SPI_OK_FINISH)
>               elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>           heap_close(pk_rel, RowShareLock);
>
>           return PointerGetDatum(NULL);
>
>       }
>
> ***************
> *** 476,497 ****
>        */
>
>       SetUserId(RelationGetForm(pk_rel)->relowner);
>
>       if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
>           elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
>
>       SetUserId(save_uid);
>
>       if (SPI_processed == 0)
> !       ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
> !                                  &qkey, new_row);
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>       heap_close(pk_rel, RowShareLock);
>
>       return PointerGetDatum(NULL);
>
>       /*
>        * Never reached
> --- 475,499 ----
>        */
>
>       SetUserId(RelationGetForm(pk_rel)->relowner);
>
>       if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT)
>           elog(ERROR, "SPI_execp() failed in RI_FKey_check()");
>
>       SetUserId(save_uid);
>
>       if (SPI_processed == 0)
> !         elog(ERROR, "%s referential integrity violation - "
> !              "key referenced from %s not found in %s",
> !              tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !              RelationGetRelationName(fk_rel),
> !              RelationGetRelationName(pk_rel));
>
>       if (SPI_finish() != SPI_OK_FINISH)
>           elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
>       heap_close(pk_rel, RowShareLock);
>
>       return PointerGetDatum(NULL);
>
>       /*
>        * Never reached
> ***************
> *** 871,900 ****
>                   if (isnull)
>                       del_nulls[i] = 'n';
>                   else
>                       del_nulls[i] = ' ';
>               }
>               del_nulls[i] = '\0';
>
>               /*
>                * Now check for existing references
>                */
> !             SetUserId(RelationGetForm(fk_rel)->relowner);
>
>               if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                                          pk_rel, fk_rel, &qkey, old_row);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> --- 873,905 ----
>                   if (isnull)
>                       del_nulls[i] = 'n';
>                   else
>                       del_nulls[i] = ' ';
>               }
>               del_nulls[i] = '\0';
>
>               /*
>                * Now check for existing references
>                */
> !             SetUserId(RelationGetForm(pk_rel)->relowner);
>
>               if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> ***************
> *** 1109,1138 ****
>                   if (isnull)
>                       upd_nulls[i] = 'n';
>                   else
>                       upd_nulls[i] = ' ';
>               }
>               upd_nulls[i] = '\0';
>
>               /*
>                * Now check for existing references
>                */
> !             SetUserId(RelationGetForm(fk_rel)->relowner);
>
>               if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                                          pk_rel, fk_rel, &qkey, old_row);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL noaction update.
> --- 1114,1146 ----
>                   if (isnull)
>                       upd_nulls[i] = 'n';
>                   else
>                       upd_nulls[i] = ' ';
>               }
>               upd_nulls[i] = '\0';
>
>               /*
>                * Now check for existing references
>                */
> !             SetUserId(RelationGetForm(pk_rel)->relowner);
>
>               if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL noaction update.
> ***************
> *** 1781,1802 ****
>                */
>               save_uid = GetUserId();
>               SetUserId(fk_owner);
>
>               if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                                          pk_rel, fk_rel, &qkey, old_row);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> --- 1789,1813 ----
>                */
>               save_uid = GetUserId();
>               SetUserId(fk_owner);
>
>               if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict delete.
> ***************
> *** 2014,2035 ****
>               SetUserId(fk_owner);
>
>               SetUserId(RelationGetForm(pk_rel)->relowner);
>
>               if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !               ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                                          pk_rel, fk_rel, &qkey, old_row);
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict update.
> --- 2025,2049 ----
>               SetUserId(fk_owner);
>
>               SetUserId(RelationGetForm(pk_rel)->relowner);
>
>               if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT)
>                   elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()");
>
>               SetUserId(save_uid);
>
>               if (SPI_processed > 0)
> !                 elog(ERROR, "%s referential integrity violation - "
> !                      "key in %s still referenced from %s",
> !                      tgargs[RI_CONSTRAINT_NAME_ARGNO],
> !                      RelationGetRelationName(pk_rel),
> !                      RelationGetRelationName(fk_rel));
>
>               if (SPI_finish() != SPI_OK_FINISH)
>                   elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()");
>
>               heap_close(fk_rel, RowShareLock);
>
>               return PointerGetDatum(NULL);
>
>               /*
>                * Handle MATCH PARTIAL restrict update.
> ***************
> *** 3289,3373 ****
>           key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;
>
>           fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]);
>           if (fno == SPI_ERROR_NOATTRIBUTE)
>               elog(ERROR, "constraint %s: table %s does not have an attribute %s",
>                    argv[RI_CONSTRAINT_NAME_ARGNO],
>                    RelationGetRelationName(pk_rel),
>                    argv[j + 1]);
>           key->keypair[i][RI_KEYPAIR_PK_IDX] = fno;
>       }
> - }
> -
> - static void ri_ReportViolation (const char *constr, Relation pk_rel,
> -                                           Relation fk_rel, RI_QueryKey *qkey,
> -                                           HeapTuple violator)
> - {
> -   static char *null_str = "null";
> -
> -   char  key_names  [512];
> -   char  key_values [512];
> -
> -   char  *name_ptr = key_names;
> -   char  *val_ptr  = key_values;
> -   int  idx = 0;
> -
> -   /* If the failed constraint was on insert/update to the FK table,
> -     * we want the key names and values extracted from there, and the error
> -     * message to look like 'key blah referenced from FK not found in PK'
> -     * Otherwise, the attr names and values come from the PK table and the
> -     * message looks like 'key blah in PK still referenced in FK'.
> -     * So, rel is set to where the tuple description is coming from
> -     * (FK in the first case, and PK in the second case), and it also is
> -     * the first relation mentioned in the message, other_rel is respectively
> -     * the other relation.
> -     */
> -
> -   bool onfk   = (qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
> -
> -   int  key_idx = onfk       ?  RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX;
> -   Relation rel = onfk       ?  fk_rel : pk_rel;
> -   Relation other_rel = onfk ?  pk_rel : fk_rel;
> -
> -   /* Special case - if there are no keys at all, this is a 'no column'
> -     * constraint - no need to try to extract the values, and the message
> -     * in this case looks differently
> -     */
> -   if (qkey -> nkeypairs == 0)
> -      elog(ERROR, "%s referential integrity violation - no rows found in %s",
> -             constr, RelationGetRelationName(pk_rel));
> -
> -   for (idx = 0; idx < qkey->nkeypairs; idx++)
> -   {
> -      int  fnum  = qkey->keypair[idx][key_idx];
> -      char *name = SPI_fname (rel->rd_att, fnum);
> -      char *val  = SPI_getvalue (violator, rel->rd_att, fnum);
> -      if (!val)
> -         val = null_str;
> -
> -      if (name_ptr - key_names  + strlen(name) + 5  >= 512 ||
> -           val_ptr  - key_values + strlen (val) + 5  >= 512)
> -      {
> -         sprintf (name_ptr, "...");
> -         sprintf (val_ptr,  "...");
> -         break;
> -      }
> -
> -      name_ptr += sprintf (name_ptr, "%s%s", idx > 0 ? "," : "", name);
> -      val_ptr  += sprintf (val_ptr,  "%s%s", idx > 0 ? "," : "", val);
> -   }
> -
> -   elog (ERROR, "%s referential integrity violation - key (%s)=(%s) "
> -                  "%s %s %s in %s", constr, key_names, key_values,
> -                     onfk ?  "referenced from" : "in", RelationGetRelationName (rel),
> -                     onfk ?  "not found" : "still referenced",
> -                     RelationGetRelationName (other_rel));
>   }
>
>   /* ----------
>    * ri_BuildQueryKeyPkCheck -
>    *
>    *    Build up a new hashtable key for a prepared SPI plan of a
>    *    check for PK rows in noaction triggers.
>    *
>    *        constr_type is FULL
>    *        constr_id is the OID of the pg_trigger row that invoked us
> --- 3303,3322 ----

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073