Thread: postgres error reporting

postgres error reporting

From
chisel@herlpacker.co.uk (Chisel Wright)
Date:
I like postgres, I find I get on much better with it than mysql.
I have one really big problem with it though.

Why is the error reporting so bad/vague for failed inserts?

I have a perl script that's doing an insert query with 45-50 fields.
There seems to be some bad data in the file I'm importing from.

The message from postgres:

  DBD::Pg::db do failed: ERROR:  pg_atoi: zero-length string at
/path/to/Module.pm line 348, <STDIN> line 1.

Which field doesn't it like?! Why can't it say something like:

  DBD::Pg::db do failed: ERROR:  pg_atoi: zero-length string in field
SomeTableField ....

Is there something I'm missing that will give me this information?

It's nothing to do with the perl either. Pasting the query into psql
gives:

  ERROR:  pg_atoi: zero-length string

No clues as to which field or piece of data it is complaining about.

Does anyone know how to find this out?


Chisel

Re: postgres error reporting

From
Bruce Momjian
Date:
New in 7.3, you can specify an empty COPY column for an integer.  It has
to be a zero or some other value.

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

Chisel Wright wrote:
> I like postgres, I find I get on much better with it than mysql.
> I have one really big problem with it though.
>
> Why is the error reporting so bad/vague for failed inserts?
>
> I have a perl script that's doing an insert query with 45-50 fields.
> There seems to be some bad data in the file I'm importing from.
>
> The message from postgres:
>
>   DBD::Pg::db do failed: ERROR:  pg_atoi: zero-length string at
> /path/to/Module.pm line 348, <STDIN> line 1.
>
> Which field doesn't it like?! Why can't it say something like:
>
>   DBD::Pg::db do failed: ERROR:  pg_atoi: zero-length string in field
> SomeTableField ....
>
> Is there something I'm missing that will give me this information?
>
> It's nothing to do with the perl either. Pasting the query into psql
> gives:
>
>   ERROR:  pg_atoi: zero-length string
>
> No clues as to which field or piece of data it is complaining about.
>
> Does anyone know how to find this out?
>
>
> Chisel
>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Don't 'kill -9' the postmaster
>

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

Re: postgres error reporting

From
"Mike Mascari"
Date:
From: "Bruce Momjian" <pgman@candle.pha.pa.us>
>
> New in 7.3, you can specify an empty COPY column for an
integer.  It has
> to be a zero or some other value.

That's true of course (that you cannot specify an empty column
for an integer). But Chisel's complaint still remains; the error
reporting in some messages (particularly type-related errors)
fails to provide any concrete information as to which attribute
of which table caused the failure. One would have to log queries
to determine where the failure took place. I don't think its
unfair to say that other DBMS products do a better job of
reporting more details of the failure - schema, table,
attribute, etc.

Mike Mascari
mascarm@mascari.com

>
> Chisel Wright wrote:
> > I like postgres, I find I get on much better with it than
mysql.
> > I have one really big problem with it though.
> >
> > Why is the error reporting so bad/vague for failed inserts?
> >
> >   ERROR:  pg_atoi: zero-length string
> >
> > No clues as to which field or piece of data it is
complaining about.
> >
> > Does anyone know how to find this out?



Re: postgres error reporting

From
Bruce Momjian
Date:
The problem is that the error comes at a very low level in the code.

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

Mike Mascari wrote:
> From: "Bruce Momjian" <pgman@candle.pha.pa.us>
> >
> > New in 7.3, you can specify an empty COPY column for an
> integer.  It has
> > to be a zero or some other value.
>
> That's true of course (that you cannot specify an empty column
> for an integer). But Chisel's complaint still remains; the error
> reporting in some messages (particularly type-related errors)
> fails to provide any concrete information as to which attribute
> of which table caused the failure. One would have to log queries
> to determine where the failure took place. I don't think its
> unfair to say that other DBMS products do a better job of
> reporting more details of the failure - schema, table,
> attribute, etc.
>
> Mike Mascari
> mascarm@mascari.com
>
> >
> > Chisel Wright wrote:
> > > I like postgres, I find I get on much better with it than
> mysql.
> > > I have one really big problem with it though.
> > >
> > > Why is the error reporting so bad/vague for failed inserts?
> > >
> > >   ERROR:  pg_atoi: zero-length string
> > >
> > > No clues as to which field or piece of data it is
> complaining about.
> > >
> > > Does anyone know how to find this out?
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
> subscribe-nomail command to majordomo@postgresql.org so that your
> message can get through to the mailing list cleanly
>

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

Re: postgres error reporting

From
Dima Tkach
Date:
Bruce Momjian wrote:
> The problem is that the error comes at a very low level in the code.
>

Well... If it knows the input file name and line number (and the message
looks like it does), it is hard to imagine it could not report at least
the column index in this case (I understand, that the column name is too
much to ask, but the index would, probably suffice anyway).

Well... I don't really know what is the problem with finding out which
column is empty in the single line of text, so I don't care much about
this one...

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?
Would be really useful to know if you are trying to insert a few hundred
thousand rows, and only one of them has a problem :-(

Dima


> ---------------------------------------------------------------------------
>
> Mike Mascari wrote:
>
>>From: "Bruce Momjian" <pgman@candle.pha.pa.us>
>>
>>>New in 7.3, you can specify an empty COPY column for an
>>
>>integer.  It has
>>
>>>to be a zero or some other value.
>>
>>That's true of course (that you cannot specify an empty column
>>for an integer). But Chisel's complaint still remains; the error
>>reporting in some messages (particularly type-related errors)
>>fails to provide any concrete information as to which attribute
>>of which table caused the failure. One would have to log queries
>>to determine where the failure took place. I don't think its
>>unfair to say that other DBMS products do a better job of
>>reporting more details of the failure - schema, table,
>>attribute, etc.
>>
>>Mike Mascari
>>mascarm@mascari.com
>>
>>
>>>Chisel Wright wrote:
>>>
>>>>I like postgres, I find I get on much better with it than
>>
>>mysql.
>>
>>>>I have one really big problem with it though.
>>>>
>>>>Why is the error reporting so bad/vague for failed inserts?
>>>>
>>>>  ERROR:  pg_atoi: zero-length string
>>>>
>>>>No clues as to which field or piece of data it is
>>
>>complaining about.
>>
>>>>Does anyone know how to find this out?
>>
>>
>>
>>---------------------------(end of broadcast)---------------------------
>>TIP 3: if posting/reading through Usenet, please send an appropriate
>>subscribe-nomail command to majordomo@postgresql.org so that your
>>message can get through to the mailing list cleanly
>>
>
>


Re: postgres error reporting

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> The problem is that the error comes at a very low level in the code.

Doesn't seem like it would be that difficult to extend the
copy-line-number hook to include a field number, or even field name.

            regards, tom lane

Re: postgres error reporting

From
Tom Lane
Date:
Dima Tkach <dmitry@openratings.com> writes:
> What drives me crazy though is the way it reports referential integrity
> violations:

> ERROR:  <unnamed> referential integrity violation - key referenced from
> child not found in parent

> Now, is it really that hard to tell *which* value for the key was not
> found?

Send a patch ;-)

            regards, tom lane

Re: postgres error reporting

From
chisel@herlpacker.co.uk (Chisel Wright)
Date:
Dima Tkach <dmitry@openratings.com> wrote in message news:<3E52BA7E.60901@openratings.com>...

> Well... I don't really know what is the problem with finding out which
> column is empty in the single line of text, so I don't care much about
> this one...

I was using that as an example. Here's another:

DBD::Pg::db do failed: ERROR:  value too long for type character
varying(10) at /path/to/Module.pm

The insert statement looks like this:

INSERT into personal_information

(preferred_dealer,choose_company_car,title,job_title,hot_lead_profile,contact_method_phone,budget,work_phone,address4,original_urn,monthly_bud


get,source_code,urn_source,contact_method_mail,dealer_code,insertion_date,update_no,email_opt_out,address1,md5,address2,initials,volvo_owner,num_children_under_16,urn,address5,


receive_volvo_magazine,postcode,address3,original_source_code,hot_lead,campaign_code,email,age_of_children,home_phone,last_name,status)
values
('',NULL,'DR','','','N','C','','XXXX','GWC1690532',NULL,'CMCMXX860C','BCO','N','55522','12/11/2001','166621','N','XXX','XXXX','','A','',NULL,'3311132','','','XXXX','','CMCMXX860C','','CM1801','XXXX',NULL,'','XXXX','O')


Because the error doesn't say:

  value too long for type character varying(10) in column X

or

  value too long for type character varying(10) in column title

(for example)

I now have to get the table description, make a note of all the colums
which ar e varchar(10), and check the value (by hand) for each one in
turn, until I find the invalid value.

Although I made it look specific my complaint it with the vagueness of
the error reporting in general. It could be so much more helpful.


I understand the opensource ethos, so maybe I should sheck out the pg
source and see what's involved... although I don't know if I'm up to
the task. It scares me before even looking... :)

Chisel

Re: postgres error reporting

From
Tom Lane
Date:

Re: postgres error reporting

From
Dmitry Tkach
Date:
What? :-)
I received it completely empty :-)

Dima

Tom Lane wrote:


Re: postgres error reporting

From
Dmitry Tkach
Date:
Tom Lane wrote:

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

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

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

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

Dima

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

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

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

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

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

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

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

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

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

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

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

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

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

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

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

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

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

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

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

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

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

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

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

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

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

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

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

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

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

      return result;
  }


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

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

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

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

      return result;
  }


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


      ReferentialIntegritySnapshotOverride = true;

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

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

+     save_uid = GetUserId();

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

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

                              break;
                          }
                      }
                  }
              }

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

                              break;
                          }
                      }
                  }
              }

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

      ReferentialIntegritySnapshotOverride = true;

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

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

      ReferentialIntegritySnapshotOverride = true;

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

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

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

      match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]);

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

                                  break;
                              }
                          }
                      }
                  }
              }

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

                                  break;
                              }
                          }
                      }
                  }
              }

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

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

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

              heap_close(fk_rel, RowExclusiveLock);

              return PointerGetDatum(NULL);

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

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

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

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

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

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

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

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

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

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

          SetUserId(RelationGetForm(pk_rel)->relowner);

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

          SetUserId(save_uid);

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

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

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

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

          SetUserId(RelationGetForm(pk_rel)->relowner);

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

          SetUserId(save_uid);

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

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

          heap_close(pk_rel, RowShareLock);

          return PointerGetDatum(NULL);

      }

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

      SetUserId(RelationGetForm(pk_rel)->relowner);

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

      SetUserId(save_uid);

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

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

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

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

      SetUserId(RelationGetForm(pk_rel)->relowner);

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

      SetUserId(save_uid);

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

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

      heap_close(pk_rel, RowShareLock);

      return PointerGetDatum(NULL);

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

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

              SetUserId(RelationGetForm(pk_rel)->relowner);

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

              SetUserId(RelationGetForm(pk_rel)->relowner);

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

              SetUserId(save_uid);

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

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

              heap_close(fk_rel, RowShareLock);

              return PointerGetDatum(NULL);

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

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

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