]> granicus.if.org Git - postgresql/commitdiff
Remove derived fields from RI_QueryKey, and do a bit of other cleanup.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Jun 2012 22:50:03 +0000 (18:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Jun 2012 22:50:29 +0000 (18:50 -0400)
We really only need the foreign key constraint's OID and the query type
code to uniquely identify each plan we are caching for FK checks.  The
other stuff that was in the struct had no business being used as part of
a hash key, and was all just being copied from struct RI_ConstraintInfo
anyway.  Get rid of the unnecessary fields, and readjust various function
APIs to make them use RI_ConstraintInfo not RI_QueryKey as info source.

I'd be surprised if this makes any measurable performance difference,
but it certainly feels cleaner.

src/backend/utils/adt/ri_triggers.c

index 5de6816a49abaa8aa6575bc44dd67cef914e5342..cd1bb262a3539e9737f26ffe1d0a9fe122ec166a 100644 (file)
 #define RI_KEYS_SOME_NULL                              1
 #define RI_KEYS_NONE_NULL                              2
 
-/* queryno values must be distinct for the convenience of ri_PerformCheck */
-#define RI_PLAN_CHECK_LOOKUPPK_NOCOLS  1
-#define RI_PLAN_CHECK_LOOKUPPK                 2
+/* RI query type codes */
+/* these queries are executed against the PK (referenced) table: */
+#define RI_PLAN_CHECK_LOOKUPPK                 1
+#define RI_PLAN_CHECK_LOOKUPPK_FROM_PK 2
+#define RI_PLAN_LAST_ON_PK                             RI_PLAN_CHECK_LOOKUPPK_FROM_PK
+/* these queries are executed against the FK (referencing) table: */
 #define RI_PLAN_CASCADE_DEL_DODELETE   3
 #define RI_PLAN_CASCADE_UPD_DOUPDATE   4
 #define RI_PLAN_NOACTION_DEL_CHECKREF  5
@@ -77,6 +80,8 @@
 #define RI_PLAN_RESTRICT_UPD_CHECKREF  8
 #define RI_PLAN_SETNULL_DEL_DOUPDATE   9
 #define RI_PLAN_SETNULL_UPD_DOUPDATE   10
+#define RI_PLAN_SETDEFAULT_DEL_DOUPDATE        11
+#define RI_PLAN_SETDEFAULT_UPD_DOUPDATE        12
 
 #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
 #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
@@ -90,9 +95,6 @@
 #define RI_TRIGTYPE_INUP   3
 #define RI_TRIGTYPE_DELETE 4
 
-#define RI_KEYPAIR_FK_IDX      0
-#define RI_KEYPAIR_PK_IDX      1
-
 
 /* ----------
  * RI_ConstraintInfo
@@ -129,13 +131,8 @@ typedef struct RI_ConstraintInfo
  */
 typedef struct RI_QueryKey
 {
-       char            constr_type;
-       Oid                     constr_id;
-       int32           constr_queryno;
-       Oid                     fk_relid;
-       Oid                     pk_relid;
-       int32           nkeypairs;
-       int16           keypair[RI_MAX_NUMKEYS][2];
+       Oid                     constr_id;              /* OID of pg_constraint entry */
+       int32           constr_queryno; /* query type ID, see RI_PLAN_XXX above */
 } RI_QueryKey;
 
 
@@ -197,14 +194,11 @@ static void ri_GenerateQual(StringInfo buf,
                                const char *rightop, Oid rightoptype);
 static void ri_add_cast_to(StringInfo buf, Oid typid);
 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
-static int ri_NullCheck(Relation rel, HeapTuple tup,
-                        RI_QueryKey *key, int pairidx);
-static void ri_BuildQueryKeyFull(RI_QueryKey *key,
-                                        const RI_ConstraintInfo *riinfo,
-                                        int32 constr_queryno);
-static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key,
-                                               const RI_ConstraintInfo *riinfo,
-                                               int32 constr_queryno);
+static int ri_NullCheck(HeapTuple tup,
+                        const RI_ConstraintInfo *riinfo, bool rel_is_pk);
+static void ri_BuildQueryKey(RI_QueryKey *key,
+                                const RI_ConstraintInfo *riinfo,
+                                int32 constr_queryno);
 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
                         const RI_ConstraintInfo *riinfo, bool rel_is_pk);
 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
@@ -225,18 +219,18 @@ static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
                         RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
                         bool cache_plan);
-static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
+static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
+                               RI_QueryKey *qkey, SPIPlanPtr qplan,
                                Relation fk_rel, Relation pk_rel,
                                HeapTuple old_tuple, HeapTuple new_tuple,
-                               bool detectNewRows,
-                               int expect_OK, const char *constrname);
-static void ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
-                                Relation rel, HeapTuple tuple,
+                               bool detectNewRows, int expect_OK);
+static void ri_ExtractValues(Relation rel, HeapTuple tup,
+                                const RI_ConstraintInfo *riinfo, bool rel_is_pk,
                                 Datum *vals, char *nulls);
-static void ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
+static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
                                   Relation pk_rel, Relation fk_rel,
                                   HeapTuple violator, TupleDesc tupdesc,
-                                  bool spi_err);
+                                  int queryno, bool spi_err);
 
 
 /* ----------
@@ -320,11 +314,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
         */
        if (riinfo.nkeys == 0)
        {
-               ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK_NOCOLS);
-
                if (SPI_connect() != SPI_OK_CONNECT)
                        elog(ERROR, "SPI_connect failed");
 
+               ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
+
                if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                {
                        char            querystr[MAX_QUOTED_REL_NAME_LEN + 100];
@@ -348,12 +342,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
                /*
                 * Execute the plan
                 */
-               ri_PerformCheck(&qkey, qplan,
+               ri_PerformCheck(&riinfo, &qkey, qplan,
                                                fk_rel, pk_rel,
                                                NULL, NULL,
                                                false,
-                                               SPI_OK_SELECT,
-                                               NameStr(riinfo.conname));
+                                               SPI_OK_SELECT);
 
                if (SPI_finish() != SPI_OK_FINISH)
                        elog(ERROR, "SPI_finish failed");
@@ -368,9 +361,7 @@ RI_FKey_check(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("MATCH PARTIAL not yet implemented")));
 
-       ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
-
-       switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
+       switch (ri_NullCheck(new_row, &riinfo, false))
        {
                case RI_KEYS_ALL_NULL:
 
@@ -448,6 +439,8 @@ RI_FKey_check(PG_FUNCTION_ARGS)
        /*
         * Fetch or prepare a saved plan for the real check
         */
+       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
+
        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
        {
                StringInfoData querybuf;
@@ -493,12 +486,11 @@ RI_FKey_check(PG_FUNCTION_ARGS)
        /*
         * Now check that foreign key exists in PK table
         */
-       ri_PerformCheck(&qkey, qplan,
+       ri_PerformCheck(&riinfo, &qkey, qplan,
                                        fk_rel, pk_rel,
                                        NULL, new_row,
                                        false,
-                                       SPI_OK_SELECT,
-                                       NameStr(riinfo.conname));
+                                       SPI_OK_SELECT);
 
        if (SPI_finish() != SPI_OK_FINISH)
                elog(ERROR, "SPI_finish failed");
@@ -553,9 +545,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
        int                     i;
        bool            result;
 
-       ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
-
-       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+       switch (ri_NullCheck(old_row, riinfo, true))
        {
                case RI_KEYS_ALL_NULL:
 
@@ -615,6 +605,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
        /*
         * Fetch or prepare a saved plan for the real check
         */
+       ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK_FROM_PK);
+
        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
        {
                StringInfoData querybuf;
@@ -659,11 +651,11 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
        /*
         * We have a plan now. Run it.
         */
-       result = ri_PerformCheck(&qkey, qplan,
+       result = ri_PerformCheck(riinfo, &qkey, qplan,
                                                         fk_rel, pk_rel,
                                                         old_row, NULL,
                                                         true,          /* treat like update */
-                                                        SPI_OK_SELECT, NULL);
+                                                        SPI_OK_SELECT);
 
        if (SPI_finish() != SPI_OK_FINISH)
                elog(ERROR, "SPI_finish failed");
@@ -740,10 +732,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_NOACTION_DEL_CHECKREF);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -767,9 +756,10 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
                                elog(ERROR, "SPI_connect failed");
 
                        /*
-                        * Fetch or prepare a saved plan for the restrict delete lookup if
-                        * foreign references exist
+                        * Fetch or prepare a saved plan for the restrict delete lookup
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_NOACTION_DEL_CHECKREF);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -816,12 +806,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to check for existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_SELECT,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_SELECT);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -911,10 +900,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_NOACTION_UPD_CHECKREF);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -957,9 +943,10 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
                                elog(ERROR, "SPI_connect failed");
 
                        /*
-                        * Fetch or prepare a saved plan for the noaction update lookup if
-                        * foreign references exist
+                        * Fetch or prepare a saved plan for the noaction update lookup
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_NOACTION_UPD_CHECKREF);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1006,12 +993,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to check for existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_SELECT,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_SELECT);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1096,10 +1082,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_CASCADE_DEL_DODELETE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -1125,6 +1108,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
                        /*
                         * Fetch or prepare a saved plan for the cascaded delete
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1170,12 +1155,11 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
                         * 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,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_DELETE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_DELETE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1264,10 +1248,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_CASCADE_UPD_DOUPDATE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -1300,9 +1281,10 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
                                elog(ERROR, "SPI_connect failed");
 
                        /*
-                        * Fetch or prepare a saved plan for the cascaded update of
-                        * foreign references
+                        * Fetch or prepare a saved plan for the cascaded update
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1360,12 +1342,11 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to update the existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, new_row,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_UPDATE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_UPDATE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1455,10 +1436,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_RESTRICT_DEL_CHECKREF);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -1482,9 +1460,10 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
                                elog(ERROR, "SPI_connect failed");
 
                        /*
-                        * Fetch or prepare a saved plan for the restrict delete lookup if
-                        * foreign references exist
+                        * Fetch or prepare a saved plan for the restrict delete lookup
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_RESTRICT_DEL_CHECKREF);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1531,12 +1510,11 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to check for existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_SELECT,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_SELECT);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1629,10 +1607,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_RESTRICT_UPD_CHECKREF);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -1665,9 +1640,10 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
                                elog(ERROR, "SPI_connect failed");
 
                        /*
-                        * Fetch or prepare a saved plan for the restrict update lookup if
-                        * foreign references exist
+                        * Fetch or prepare a saved plan for the restrict update lookup
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_RESTRICT_UPD_CHECKREF);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1714,12 +1690,11 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to check for existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_SELECT,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_SELECT);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1804,10 +1779,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -1833,6 +1805,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
                        /*
                         * Fetch or prepare a saved plan for the set null delete operation
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETNULL_DEL_DOUPDATE);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -1887,12 +1861,11 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to check for existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_UPDATE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_UPDATE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -1979,10 +1952,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_SETNULL_UPD_DOUPDATE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -2017,6 +1987,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
                        /*
                         * Fetch or prepare a saved plan for the set null update operation
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETNULL_UPD_DOUPDATE);
+
                        if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
                        {
                                StringInfoData querybuf;
@@ -2071,12 +2043,11 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to update the existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_UPDATE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_UPDATE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -2160,10 +2131,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -2191,6 +2159,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
                         * Unfortunately we need to do it on every invocation because the
                         * default value could potentially change between calls.
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETDEFAULT_DEL_DOUPDATE);
+
                        {
                                StringInfoData querybuf;
                                StringInfoData qualbuf;
@@ -2245,12 +2215,11 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to update the existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_UPDATE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_UPDATE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -2346,10 +2315,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
                         */
                case FKCONSTR_MATCH_SIMPLE:
                case FKCONSTR_MATCH_FULL:
-                       ri_BuildQueryKeyFull(&qkey, &riinfo,
-                                                                RI_PLAN_SETNULL_DEL_DOUPDATE);
-
-                       switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
+                       switch (ri_NullCheck(old_row, &riinfo, true))
                        {
                                case RI_KEYS_ALL_NULL:
                                case RI_KEYS_SOME_NULL:
@@ -2386,6 +2352,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
                         * Unfortunately we need to do it on every invocation because the
                         * default value could potentially change between calls.
                         */
+                       ri_BuildQueryKey(&qkey, &riinfo, RI_PLAN_SETDEFAULT_UPD_DOUPDATE);
+
                        {
                                StringInfoData querybuf;
                                StringInfoData qualbuf;
@@ -2440,12 +2408,11 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
                        /*
                         * We have a plan now. Run it to update the existing references.
                         */
-                       ri_PerformCheck(&qkey, qplan,
+                       ri_PerformCheck(&riinfo, &qkey, qplan,
                                                        fk_rel, pk_rel,
                                                        old_row, NULL,
                                                        true,           /* must detect new rows */
-                                                       SPI_OK_UPDATE,
-                                                       NameStr(riinfo.conname));
+                                                       SPI_OK_UPDATE);
 
                        if (SPI_finish() != SPI_OK_FINISH)
                                elog(ERROR, "SPI_finish failed");
@@ -2604,7 +2571,6 @@ bool
 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 {
        RI_ConstraintInfo riinfo;
-       const char *constrname = trigger->tgname;
        StringInfoData querybuf;
        char            pkrelname[MAX_QUOTED_REL_NAME_LEN];
        char            fkrelname[MAX_QUOTED_REL_NAME_LEN];
@@ -2799,46 +2765,42 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
        {
                HeapTuple       tuple = SPI_tuptable->vals[0];
                TupleDesc       tupdesc = SPI_tuptable->tupdesc;
-               RI_QueryKey qkey;
+
+               /*
+                * The columns to look at in the result tuple are 1..N, not whatever
+                * they are in the fk_rel.  Hack up riinfo so that the subroutines
+                * called here will behave properly.
+                *
+                * In addition to this, we have to pass the correct tupdesc to
+                * ri_ReportViolation, overriding its normal habit of using the pk_rel
+                * or fk_rel's tupdesc.
+                */
+               for (i = 0; i < riinfo.nkeys; i++)
+                       riinfo.fk_attnums[i] = i + 1;
 
                /*
                 * If it's MATCH FULL, and there are any nulls in the FK keys,
                 * complain about that rather than the lack of a match.  MATCH FULL
                 * disallows partially-null FK rows.
                 */
-               if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL)
-               {
-                       bool            isnull = false;
-
-                       for (i = 1; i <= riinfo.nkeys; i++)
-                       {
-                               (void) SPI_getbinval(tuple, tupdesc, i, &isnull);
-                               if (isnull)
-                                       break;
-                       }
-                       if (isnull)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
-                                                errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-                                                               RelationGetRelationName(fk_rel),
-                                                               constrname),
-                                                errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
-               }
+               if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
+                       ri_NullCheck(tuple, &riinfo, false) != RI_KEYS_NONE_NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
+                                        errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
+                                                       RelationGetRelationName(fk_rel),
+                                                       NameStr(riinfo.conname)),
+                                        errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
 
                /*
-                * Although we didn't cache the query, we need to set up a fake query
-                * key to pass to ri_ReportViolation.
+                * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
+                * query, which isn't true, but will cause it to use riinfo.fk_attnums
+                * as we need.
                 */
-               MemSet(&qkey, 0, sizeof(qkey));
-               qkey.constr_queryno = RI_PLAN_CHECK_LOOKUPPK;
-               qkey.nkeypairs = riinfo.nkeys;
-               for (i = 0; i < riinfo.nkeys; i++)
-                       qkey.keypair[i][RI_KEYPAIR_FK_IDX] = i + 1;
-
-               ri_ReportViolation(&qkey, constrname,
+               ri_ReportViolation(&riinfo,
                                                   pk_rel, fk_rel,
                                                   tuple, tupdesc,
-                                                  false);
+                                                  RI_PLAN_CHECK_LOOKUPPK, false);
        }
 
        if (SPI_finish() != SPI_OK_FINISH)
@@ -3015,36 +2977,26 @@ ri_GenerateQualCollation(StringInfo buf, Oid collation)
 }
 
 /* ----------
- * ri_BuildQueryKeyFull -
+ * ri_BuildQueryKey -
  *
- *     Build up a new hashtable key for a prepared SPI plan of a
- *     constraint trigger of MATCH FULL.
+ *     Construct a hashtable key for a prepared SPI plan of an FK constraint.
  *
  *             key: output argument, *key is filled in based on the other arguments
  *             riinfo: info from pg_constraint entry
- *             constr_queryno: an internal number of the query inside the proc
- *
- *     At least for MATCH FULL this builds a unique key per plan.
+ *             constr_queryno: an internal number identifying the query type
+ *                     (see RI_PLAN_XXX constants at head of file)
  * ----------
  */
 static void
-ri_BuildQueryKeyFull(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
-                                        int32 constr_queryno)
+ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
+                                int32 constr_queryno)
 {
-       int                     i;
-
-       MemSet(key, 0, sizeof(RI_QueryKey));
-       key->constr_type = FKCONSTR_MATCH_FULL;
+       /*
+        * We assume struct RI_QueryKey contains no padding bytes, else we'd
+        * need to use memset to clear them.
+        */
        key->constr_id = riinfo->constraint_id;
        key->constr_queryno = constr_queryno;
-       key->fk_relid = riinfo->fk_relid;
-       key->pk_relid = riinfo->pk_relid;
-       key->nkeypairs = riinfo->nkeys;
-       for (i = 0; i < riinfo->nkeys; i++)
-       {
-               key->keypair[i][RI_KEYPAIR_FK_IDX] = riinfo->fk_attnums[i];
-               key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
-       }
 }
 
 /*
@@ -3269,12 +3221,10 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
        int                     save_sec_context;
 
        /*
-        * 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
+        * Use the query type code to determine whether the query is run against
+        * the PK or FK table; we'll do the check as that table's owner
         */
-       if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
-               qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
+       if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
                query_rel = pk_rel;
        else
                query_rel = fk_rel;
@@ -3307,15 +3257,15 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
  * Perform a query to enforce an RI restriction
  */
 static bool
-ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
+ri_PerformCheck(const RI_ConstraintInfo *riinfo,
+                               RI_QueryKey *qkey, SPIPlanPtr qplan,
                                Relation fk_rel, Relation pk_rel,
                                HeapTuple old_tuple, HeapTuple new_tuple,
-                               bool detectNewRows,
-                               int expect_OK, const char *constrname)
+                               bool detectNewRows, int expect_OK)
 {
        Relation        query_rel,
                                source_rel;
-       int                     key_idx;
+       bool            source_is_pk;
        Snapshot        test_snapshot;
        Snapshot        crosscheck_snapshot;
        int                     limit;
@@ -3326,45 +3276,44 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
        char            nulls[RI_MAX_NUMKEYS * 2];
 
        /*
-        * 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
+        * Use the query type code to determine whether the query is run against
+        * the PK or FK table; we'll do the check as that table's owner
         */
-       if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
-               qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
+       if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
                query_rel = pk_rel;
        else
                query_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 constrname == NULL)
+        * 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
+        * (and sets queryno to RI_PLAN_CHECK_LOOKUPPK_FROM_PK).  We might
+        * eventually need some less klugy way to determine this.
         */
-       if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK && constrname != NULL)
+       if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
        {
                source_rel = fk_rel;
-               key_idx = RI_KEYPAIR_FK_IDX;
+               source_is_pk = false;
        }
        else
        {
                source_rel = pk_rel;
-               key_idx = RI_KEYPAIR_PK_IDX;
+               source_is_pk = true;
        }
 
        /* Extract the parameters to be passed into the query */
        if (new_tuple)
        {
-               ri_ExtractValues(qkey, key_idx, source_rel, new_tuple,
+               ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
                                                 vals, nulls);
                if (old_tuple)
-                       ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
-                                                        vals + qkey->nkeypairs, nulls + qkey->nkeypairs);
+                       ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
+                                                        vals + riinfo->nkeys, nulls + riinfo->nkeys);
        }
        else
        {
-               ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
+               ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
                                                 vals, nulls);
        }
 
@@ -3419,20 +3368,21 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
                elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
 
        if (expect_OK >= 0 && spi_result != expect_OK)
-               ri_ReportViolation(qkey, constrname ? constrname : "",
+               ri_ReportViolation(riinfo,
                                                   pk_rel, fk_rel,
                                                   new_tuple ? new_tuple : old_tuple,
                                                   NULL,
-                                                  true);
+                                                  qkey->constr_queryno, true);
 
        /* XXX wouldn't it be clearer to do this part at the caller? */
-       if (constrname && expect_OK == SPI_OK_SELECT &&
+       if (qkey->constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK &&
+               expect_OK == SPI_OK_SELECT &&
        (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
-               ri_ReportViolation(qkey, constrname,
+               ri_ReportViolation(riinfo,
                                                   pk_rel, fk_rel,
                                                   new_tuple ? new_tuple : old_tuple,
                                                   NULL,
-                                                  false);
+                                                  qkey->constr_queryno, false);
 
        return SPI_processed != 0;
 }
@@ -3441,18 +3391,24 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
  * Extract fields from a tuple into Datum/nulls arrays
  */
 static void
-ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
-                                Relation rel, HeapTuple tuple,
+ri_ExtractValues(Relation rel, HeapTuple tup,
+                                const RI_ConstraintInfo *riinfo, bool rel_is_pk,
                                 Datum *vals, char *nulls)
 {
+       TupleDesc       tupdesc = rel->rd_att;
+       const int16 *attnums;
        int                     i;
        bool            isnull;
 
-       for (i = 0; i < qkey->nkeypairs; i++)
+       if (rel_is_pk)
+               attnums = riinfo->pk_attnums;
+       else
+               attnums = riinfo->fk_attnums;
+
+       for (i = 0; i < riinfo->nkeys; i++)
        {
-               vals[i] = SPI_getbinval(tuple, rel->rd_att,
-                                                               qkey->keypair[i][key_idx],
-                                                               &isnull);
+               vals[i] = heap_getattr(tup, attnums[i], tupdesc,
+                                                          &isnull);
                nulls[i] = isnull ? 'n' : ' ';
        }
 }
@@ -3467,23 +3423,23 @@ ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
  * message looks like 'key blah is still referenced from FK'.
  */
 static void
-ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
+ri_ReportViolation(const RI_ConstraintInfo *riinfo,
                                   Relation pk_rel, Relation fk_rel,
                                   HeapTuple violator, TupleDesc tupdesc,
-                                  bool spi_err)
+                                  int queryno, bool spi_err)
 {
        StringInfoData key_names;
        StringInfoData key_values;
        bool            onfk;
-       int                     idx,
-                               key_idx;
+       const int16 *attnums;
+       int                     idx;
 
        if (spi_err)
                ereport(ERROR,
                                (errcode(ERRCODE_INTERNAL_ERROR),
                                 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
                                                RelationGetRelationName(pk_rel),
-                                               constrname,
+                                               NameStr(riinfo->conname),
                                                RelationGetRelationName(fk_rel)),
                                 errhint("This is most likely due to a rule having rewritten the query.")));
 
@@ -3491,16 +3447,16 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
         * Determine which relation to complain about.  If tupdesc wasn't passed
         * by caller, assume the violator tuple came from there.
         */
-       onfk = (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
+       onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
        if (onfk)
        {
-               key_idx = RI_KEYPAIR_FK_IDX;
+               attnums = riinfo->fk_attnums;
                if (tupdesc == NULL)
                        tupdesc = fk_rel->rd_att;
        }
        else
        {
-               key_idx = RI_KEYPAIR_PK_IDX;
+               attnums = riinfo->pk_attnums;
                if (tupdesc == NULL)
                        tupdesc = pk_rel->rd_att;
        }
@@ -3510,12 +3466,13 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
         * constraint - no need to try to extract the values, and the message in
         * this case looks different.
         */
-       if (qkey->nkeypairs == 0)
+       if (riinfo->nkeys == 0)
        {
                ereport(ERROR,
                                (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
                                 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-                                               RelationGetRelationName(fk_rel), constrname),
+                                               RelationGetRelationName(fk_rel),
+                                               NameStr(riinfo->conname)),
                                 errdetail("No rows were found in \"%s\".",
                                                   RelationGetRelationName(pk_rel))));
        }
@@ -3523,9 +3480,9 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
        /* Get printable versions of the keys involved */
        initStringInfo(&key_names);
        initStringInfo(&key_values);
-       for (idx = 0; idx < qkey->nkeypairs; idx++)
+       for (idx = 0; idx < riinfo->nkeys; idx++)
        {
-               int                     fnum = qkey->keypair[idx][key_idx];
+               int                     fnum = attnums[idx];
                char       *name,
                                   *val;
 
@@ -3547,7 +3504,8 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
                ereport(ERROR,
                                (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
                                 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
-                                               RelationGetRelationName(fk_rel), constrname),
+                                               RelationGetRelationName(fk_rel),
+                                               NameStr(riinfo->conname)),
                                 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
                                                   key_names.data, key_values.data,
                                                   RelationGetRelationName(pk_rel))));
@@ -3556,45 +3514,13 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
                                (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
                                 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
                                                RelationGetRelationName(pk_rel),
-                                               constrname, RelationGetRelationName(fk_rel)),
+                                               NameStr(riinfo->conname),
+                                               RelationGetRelationName(fk_rel)),
                        errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
                                          key_names.data, key_values.data,
                                          RelationGetRelationName(fk_rel))));
 }
 
-/* ----------
- * ri_BuildQueryKeyPkCheck -
- *
- *     Build up a new hashtable key for a prepared SPI plan of a
- *     check for PK rows in noaction triggers.
- *
- *             key: output argument, *key is filled in based on the other arguments
- *             riinfo: info from pg_constraint entry
- *             constr_queryno: an internal number of the query inside the proc
- *
- *     At least for MATCH FULL this builds a unique key per plan.
- * ----------
- */
-static void
-ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
-                                               int32 constr_queryno)
-{
-       int                     i;
-
-       MemSet(key, 0, sizeof(RI_QueryKey));
-       key->constr_type = FKCONSTR_MATCH_FULL;
-       key->constr_id = riinfo->constraint_id;
-       key->constr_queryno = constr_queryno;
-       key->fk_relid = InvalidOid;
-       key->pk_relid = riinfo->pk_relid;
-       key->nkeypairs = riinfo->nkeys;
-       for (i = 0; i < riinfo->nkeys; i++)
-       {
-               key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
-               key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
-       }
-}
-
 
 /* ----------
  * ri_NullCheck -
@@ -3605,18 +3531,22 @@ ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
  * ----------
  */
 static int
-ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
+ri_NullCheck(HeapTuple tup,
+                        const RI_ConstraintInfo *riinfo, bool rel_is_pk)
 {
+       const int16 *attnums;
        int                     i;
-       bool            isnull;
        bool            allnull = true;
        bool            nonenull = true;
 
-       for (i = 0; i < key->nkeypairs; i++)
+       if (rel_is_pk)
+               attnums = riinfo->pk_attnums;
+       else
+               attnums = riinfo->fk_attnums;
+
+       for (i = 0; i < riinfo->nkeys; i++)
        {
-               isnull = false;
-               SPI_getbinval(tup, rel->rd_att, key->keypair[i][pairidx], &isnull);
-               if (isnull)
+               if (heap_attisnull(tup, attnums[i]))
                        nonenull = false;
                else
                        allnull = false;
@@ -3779,14 +3709,14 @@ ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
                /*
                 * Get one attribute's oldvalue. If it is NULL - they're not equal.
                 */
-               oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
+               oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
                if (isnull)
                        return false;
 
                /*
                 * Get one attribute's newvalue. If it is NULL - they're not equal.
                 */
-               newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
+               newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
                if (isnull)
                        return false;