Re: [GENERAL] postgres error reporting - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [GENERAL] postgres error reporting
Date
Msg-id 200303180025.h2I0Pa800449@candle.pha.pa.us
Whole thread Raw
In response to Re: [GENERAL] postgres error reporting  (Dmitry Tkach <dmitry@openratings.com>)
List pgsql-patches
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

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: psql patch (2)
Next
From: Bruce Momjian
Date:
Subject: Re: hashed crosstab