logo       
Google Custom Search
    AddThis Social Bookmark Button

Re: [GENERAL] postgres error reporting: msg#00115

Subject: Re: [GENERAL] postgres error reporting
FYI, this has already been applied, two days ago.

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

Dmitry Tkach wrote:
> 
> Tom Lane wrote:
> 
> >Dima Tkach <dmitry@xxxxxxxxxxxxxxx> 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)
>