LCOV - code coverage report
Current view: top level - src/backend/utils/adt - ri_triggers.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 817 951 85.9 %
Date: 2017-09-29 13:40:31 Functions: 40 41 97.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* ----------
       2             :  * ri_triggers.c
       3             :  *
       4             :  *  Generic trigger procedures for referential integrity constraint
       5             :  *  checks.
       6             :  *
       7             :  *  Note about memory management: the private hashtables kept here live
       8             :  *  across query and transaction boundaries, in fact they live as long as
       9             :  *  the backend does.  This works because the hashtable structures
      10             :  *  themselves are allocated by dynahash.c in its permanent DynaHashCxt,
      11             :  *  and the SPI plans they point to are saved using SPI_keepplan().
      12             :  *  There is not currently any provision for throwing away a no-longer-needed
      13             :  *  plan --- consider improving this someday.
      14             :  *
      15             :  *
      16             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      17             :  *
      18             :  * src/backend/utils/adt/ri_triggers.c
      19             :  *
      20             :  * ----------
      21             :  */
      22             : 
      23             : 
      24             : /* ----------
      25             :  * Internal TODO:
      26             :  *
      27             :  *      Add MATCH PARTIAL logic.
      28             :  * ----------
      29             :  */
      30             : 
      31             : #include "postgres.h"
      32             : 
      33             : #include "access/htup_details.h"
      34             : #include "access/sysattr.h"
      35             : #include "access/xact.h"
      36             : #include "catalog/pg_collation.h"
      37             : #include "catalog/pg_constraint.h"
      38             : #include "catalog/pg_operator.h"
      39             : #include "catalog/pg_type.h"
      40             : #include "commands/trigger.h"
      41             : #include "executor/executor.h"
      42             : #include "executor/spi.h"
      43             : #include "lib/ilist.h"
      44             : #include "parser/parse_coerce.h"
      45             : #include "parser/parse_relation.h"
      46             : #include "miscadmin.h"
      47             : #include "storage/bufmgr.h"
      48             : #include "utils/acl.h"
      49             : #include "utils/builtins.h"
      50             : #include "utils/fmgroids.h"
      51             : #include "utils/guc.h"
      52             : #include "utils/inval.h"
      53             : #include "utils/lsyscache.h"
      54             : #include "utils/memutils.h"
      55             : #include "utils/rel.h"
      56             : #include "utils/rls.h"
      57             : #include "utils/snapmgr.h"
      58             : #include "utils/syscache.h"
      59             : #include "utils/tqual.h"
      60             : 
      61             : 
      62             : /* ----------
      63             :  * Local definitions
      64             :  * ----------
      65             :  */
      66             : 
      67             : #define RI_MAX_NUMKEYS                  INDEX_MAX_KEYS
      68             : 
      69             : #define RI_INIT_CONSTRAINTHASHSIZE      64
      70             : #define RI_INIT_QUERYHASHSIZE           (RI_INIT_CONSTRAINTHASHSIZE * 4)
      71             : 
      72             : #define RI_KEYS_ALL_NULL                0
      73             : #define RI_KEYS_SOME_NULL               1
      74             : #define RI_KEYS_NONE_NULL               2
      75             : 
      76             : /* RI query type codes */
      77             : /* these queries are executed against the PK (referenced) table: */
      78             : #define RI_PLAN_CHECK_LOOKUPPK          1
      79             : #define RI_PLAN_CHECK_LOOKUPPK_FROM_PK  2
      80             : #define RI_PLAN_LAST_ON_PK              RI_PLAN_CHECK_LOOKUPPK_FROM_PK
      81             : /* these queries are executed against the FK (referencing) table: */
      82             : #define RI_PLAN_CASCADE_DEL_DODELETE    3
      83             : #define RI_PLAN_CASCADE_UPD_DOUPDATE    4
      84             : #define RI_PLAN_RESTRICT_DEL_CHECKREF   5
      85             : #define RI_PLAN_RESTRICT_UPD_CHECKREF   6
      86             : #define RI_PLAN_SETNULL_DEL_DOUPDATE    7
      87             : #define RI_PLAN_SETNULL_UPD_DOUPDATE    8
      88             : #define RI_PLAN_SETDEFAULT_DEL_DOUPDATE 9
      89             : #define RI_PLAN_SETDEFAULT_UPD_DOUPDATE 10
      90             : 
      91             : #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
      92             : #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
      93             : 
      94             : #define RIAttName(rel, attnum)  NameStr(*attnumAttName(rel, attnum))
      95             : #define RIAttType(rel, attnum)  attnumTypeId(rel, attnum)
      96             : #define RIAttCollation(rel, attnum) attnumCollationId(rel, attnum)
      97             : 
      98             : #define RI_TRIGTYPE_INSERT 1
      99             : #define RI_TRIGTYPE_UPDATE 2
     100             : #define RI_TRIGTYPE_DELETE 3
     101             : 
     102             : 
     103             : /* ----------
     104             :  * RI_ConstraintInfo
     105             :  *
     106             :  *  Information extracted from an FK pg_constraint entry.  This is cached in
     107             :  *  ri_constraint_cache.
     108             :  * ----------
     109             :  */
     110             : typedef struct RI_ConstraintInfo
     111             : {
     112             :     Oid         constraint_id;  /* OID of pg_constraint entry (hash key) */
     113             :     bool        valid;          /* successfully initialized? */
     114             :     uint32      oidHashValue;   /* hash value of pg_constraint OID */
     115             :     NameData    conname;        /* name of the FK constraint */
     116             :     Oid         pk_relid;       /* referenced relation */
     117             :     Oid         fk_relid;       /* referencing relation */
     118             :     char        confupdtype;    /* foreign key's ON UPDATE action */
     119             :     char        confdeltype;    /* foreign key's ON DELETE action */
     120             :     char        confmatchtype;  /* foreign key's match type */
     121             :     int         nkeys;          /* number of key columns */
     122             :     int16       pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
     123             :     int16       fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
     124             :     Oid         pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
     125             :     Oid         pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
     126             :     Oid         ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
     127             :     dlist_node  valid_link;     /* Link in list of valid entries */
     128             : } RI_ConstraintInfo;
     129             : 
     130             : 
     131             : /* ----------
     132             :  * RI_QueryKey
     133             :  *
     134             :  *  The key identifying a prepared SPI plan in our query hashtable
     135             :  * ----------
     136             :  */
     137             : typedef struct RI_QueryKey
     138             : {
     139             :     Oid         constr_id;      /* OID of pg_constraint entry */
     140             :     int32       constr_queryno; /* query type ID, see RI_PLAN_XXX above */
     141             : } RI_QueryKey;
     142             : 
     143             : 
     144             : /* ----------
     145             :  * RI_QueryHashEntry
     146             :  * ----------
     147             :  */
     148             : typedef struct RI_QueryHashEntry
     149             : {
     150             :     RI_QueryKey key;
     151             :     SPIPlanPtr  plan;
     152             : } RI_QueryHashEntry;
     153             : 
     154             : 
     155             : /* ----------
     156             :  * RI_CompareKey
     157             :  *
     158             :  *  The key identifying an entry showing how to compare two values
     159             :  * ----------
     160             :  */
     161             : typedef struct RI_CompareKey
     162             : {
     163             :     Oid         eq_opr;         /* the equality operator to apply */
     164             :     Oid         typeid;         /* the data type to apply it to */
     165             : } RI_CompareKey;
     166             : 
     167             : 
     168             : /* ----------
     169             :  * RI_CompareHashEntry
     170             :  * ----------
     171             :  */
     172             : typedef struct RI_CompareHashEntry
     173             : {
     174             :     RI_CompareKey key;
     175             :     bool        valid;          /* successfully initialized? */
     176             :     FmgrInfo    eq_opr_finfo;   /* call info for equality fn */
     177             :     FmgrInfo    cast_func_finfo;    /* in case we must coerce input */
     178             : } RI_CompareHashEntry;
     179             : 
     180             : 
     181             : /* ----------
     182             :  * Local data
     183             :  * ----------
     184             :  */
     185             : static HTAB *ri_constraint_cache = NULL;
     186             : static HTAB *ri_query_cache = NULL;
     187             : static HTAB *ri_compare_cache = NULL;
     188             : static dlist_head ri_constraint_cache_valid_list;
     189             : static int  ri_constraint_cache_valid_count = 0;
     190             : 
     191             : 
     192             : /* ----------
     193             :  * Local function prototypes
     194             :  * ----------
     195             :  */
     196             : static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
     197             :                   HeapTuple old_row,
     198             :                   const RI_ConstraintInfo *riinfo);
     199             : static Datum ri_restrict_del(TriggerData *trigdata, bool is_no_action);
     200             : static Datum ri_restrict_upd(TriggerData *trigdata, bool is_no_action);
     201             : static void quoteOneName(char *buffer, const char *name);
     202             : static void quoteRelationName(char *buffer, Relation rel);
     203             : static void ri_GenerateQual(StringInfo buf,
     204             :                 const char *sep,
     205             :                 const char *leftop, Oid leftoptype,
     206             :                 Oid opoid,
     207             :                 const char *rightop, Oid rightoptype);
     208             : static void ri_add_cast_to(StringInfo buf, Oid typid);
     209             : static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
     210             : static int ri_NullCheck(HeapTuple tup,
     211             :              const RI_ConstraintInfo *riinfo, bool rel_is_pk);
     212             : static void ri_BuildQueryKey(RI_QueryKey *key,
     213             :                  const RI_ConstraintInfo *riinfo,
     214             :                  int32 constr_queryno);
     215             : static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
     216             :              const RI_ConstraintInfo *riinfo, bool rel_is_pk);
     217             : static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
     218             :                    Datum oldvalue, Datum newvalue);
     219             : 
     220             : static void ri_InitHashTables(void);
     221             : static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
     222             : static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
     223             : static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
     224             : static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
     225             : 
     226             : static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
     227             :                 int tgkind);
     228             : static const RI_ConstraintInfo *ri_FetchConstraintInfo(Trigger *trigger,
     229             :                        Relation trig_rel, bool rel_is_pk);
     230             : static const RI_ConstraintInfo *ri_LoadConstraintInfo(Oid constraintOid);
     231             : static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
     232             :              RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
     233             :              bool cache_plan);
     234             : static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
     235             :                 RI_QueryKey *qkey, SPIPlanPtr qplan,
     236             :                 Relation fk_rel, Relation pk_rel,
     237             :                 HeapTuple old_tuple, HeapTuple new_tuple,
     238             :                 bool detectNewRows, int expect_OK);
     239             : static void ri_ExtractValues(Relation rel, HeapTuple tup,
     240             :                  const RI_ConstraintInfo *riinfo, bool rel_is_pk,
     241             :                  Datum *vals, char *nulls);
     242             : static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
     243             :                    Relation pk_rel, Relation fk_rel,
     244             :                    HeapTuple violator, TupleDesc tupdesc,
     245             :                    int queryno, bool spi_err);
     246             : 
     247             : 
     248             : /* ----------
     249             :  * RI_FKey_check -
     250             :  *
     251             :  *  Check foreign key existence (combined for INSERT and UPDATE).
     252             :  * ----------
     253             :  */
     254             : static Datum
     255         282 : RI_FKey_check(TriggerData *trigdata)
     256             : {
     257             :     const RI_ConstraintInfo *riinfo;
     258             :     Relation    fk_rel;
     259             :     Relation    pk_rel;
     260             :     HeapTuple   new_row;
     261             :     Buffer      new_row_buf;
     262             :     RI_QueryKey qkey;
     263             :     SPIPlanPtr  qplan;
     264             :     int         i;
     265             : 
     266             :     /*
     267             :      * Get arguments.
     268             :      */
     269         282 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
     270             :                                     trigdata->tg_relation, false);
     271             : 
     272         282 :     if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
     273             :     {
     274          34 :         new_row = trigdata->tg_newtuple;
     275          34 :         new_row_buf = trigdata->tg_newtuplebuf;
     276             :     }
     277             :     else
     278             :     {
     279         248 :         new_row = trigdata->tg_trigtuple;
     280         248 :         new_row_buf = trigdata->tg_trigtuplebuf;
     281             :     }
     282             : 
     283             :     /*
     284             :      * We should not even consider checking the row if it is no longer valid,
     285             :      * since it was either deleted (so the deferred check should be skipped)
     286             :      * or updated (in which case only the latest version of the row should be
     287             :      * checked).  Test its liveness according to SnapshotSelf.  We need pin
     288             :      * and lock on the buffer to call HeapTupleSatisfiesVisibility.  Caller
     289             :      * should be holding pin, but not lock.
     290             :      */
     291         282 :     LockBuffer(new_row_buf, BUFFER_LOCK_SHARE);
     292         282 :     if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
     293             :     {
     294          10 :         LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
     295          10 :         return PointerGetDatum(NULL);
     296             :     }
     297         272 :     LockBuffer(new_row_buf, BUFFER_LOCK_UNLOCK);
     298             : 
     299             :     /*
     300             :      * Get the relation descriptors of the FK and PK tables.
     301             :      *
     302             :      * pk_rel is opened in RowShareLock mode since that's what our eventual
     303             :      * SELECT FOR KEY SHARE will get on it.
     304             :      */
     305         272 :     fk_rel = trigdata->tg_relation;
     306         272 :     pk_rel = heap_open(riinfo->pk_relid, RowShareLock);
     307             : 
     308         272 :     if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
     309           0 :         ereport(ERROR,
     310             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     311             :                  errmsg("MATCH PARTIAL not yet implemented")));
     312             : 
     313         272 :     switch (ri_NullCheck(new_row, riinfo, false))
     314             :     {
     315             :         case RI_KEYS_ALL_NULL:
     316             : 
     317             :             /*
     318             :              * No further check needed - an all-NULL key passes every type of
     319             :              * foreign key constraint.
     320             :              */
     321          13 :             heap_close(pk_rel, RowShareLock);
     322          13 :             return PointerGetDatum(NULL);
     323             : 
     324             :         case RI_KEYS_SOME_NULL:
     325             : 
     326             :             /*
     327             :              * This is the only case that differs between the three kinds of
     328             :              * MATCH.
     329             :              */
     330          20 :             switch (riinfo->confmatchtype)
     331             :             {
     332             :                 case FKCONSTR_MATCH_FULL:
     333             : 
     334             :                     /*
     335             :                      * Not allowed - MATCH FULL says either all or none of the
     336             :                      * attributes can be NULLs
     337             :                      */
     338           4 :                     ereport(ERROR,
     339             :                             (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
     340             :                              errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
     341             :                                     RelationGetRelationName(fk_rel),
     342             :                                     NameStr(riinfo->conname)),
     343             :                              errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
     344             :                              errtableconstraint(fk_rel,
     345             :                                                 NameStr(riinfo->conname))));
     346             :                     heap_close(pk_rel, RowShareLock);
     347             :                     return PointerGetDatum(NULL);
     348             : 
     349             :                 case FKCONSTR_MATCH_SIMPLE:
     350             : 
     351             :                     /*
     352             :                      * MATCH SIMPLE - if ANY column is null, the key passes
     353             :                      * the constraint.
     354             :                      */
     355          16 :                     heap_close(pk_rel, RowShareLock);
     356          16 :                     return PointerGetDatum(NULL);
     357             : 
     358             :                 case FKCONSTR_MATCH_PARTIAL:
     359             : 
     360             :                     /*
     361             :                      * MATCH PARTIAL - all non-null columns must match. (not
     362             :                      * implemented, can be done by modifying the query below
     363             :                      * to only include non-null columns, or by writing a
     364             :                      * special version here)
     365             :                      */
     366           0 :                     ereport(ERROR,
     367             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     368             :                              errmsg("MATCH PARTIAL not yet implemented")));
     369             :                     heap_close(pk_rel, RowShareLock);
     370             :                     return PointerGetDatum(NULL);
     371             : 
     372             :                 default:
     373           0 :                     elog(ERROR, "unrecognized confmatchtype: %d",
     374             :                          riinfo->confmatchtype);
     375             :                     break;
     376             :             }
     377             : 
     378             :         case RI_KEYS_NONE_NULL:
     379             : 
     380             :             /*
     381             :              * Have a full qualified key - continue below for all three kinds
     382             :              * of MATCH.
     383             :              */
     384         239 :             break;
     385             :     }
     386             : 
     387         239 :     if (SPI_connect() != SPI_OK_CONNECT)
     388           0 :         elog(ERROR, "SPI_connect failed");
     389             : 
     390             :     /*
     391             :      * Fetch or prepare a saved plan for the real check
     392             :      */
     393         239 :     ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
     394             : 
     395         239 :     if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
     396             :     {
     397             :         StringInfoData querybuf;
     398             :         char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
     399             :         char        attname[MAX_QUOTED_NAME_LEN];
     400             :         char        paramname[16];
     401             :         const char *querysep;
     402             :         Oid         queryoids[RI_MAX_NUMKEYS];
     403             : 
     404             :         /* ----------
     405             :          * The query string built is
     406             :          *  SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
     407             :          *         FOR KEY SHARE OF x
     408             :          * The type id's for the $ parameters are those of the
     409             :          * corresponding FK attributes.
     410             :          * ----------
     411             :          */
     412          63 :         initStringInfo(&querybuf);
     413          63 :         quoteRelationName(pkrelname, pk_rel);
     414          63 :         appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
     415          63 :         querysep = "WHERE";
     416         140 :         for (i = 0; i < riinfo->nkeys; i++)
     417             :         {
     418          77 :             Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
     419          77 :             Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
     420             : 
     421          77 :             quoteOneName(attname,
     422          77 :                          RIAttName(pk_rel, riinfo->pk_attnums[i]));
     423          77 :             sprintf(paramname, "$%d", i + 1);
     424          77 :             ri_GenerateQual(&querybuf, querysep,
     425             :                             attname, pk_type,
     426             :                             riinfo->pf_eq_oprs[i],
     427             :                             paramname, fk_type);
     428          77 :             querysep = "AND";
     429          77 :             queryoids[i] = fk_type;
     430             :         }
     431          63 :         appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
     432             : 
     433             :         /* Prepare and save the plan */
     434          63 :         qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
     435             :                              &qkey, fk_rel, pk_rel, true);
     436             :     }
     437             : 
     438             :     /*
     439             :      * Now check that foreign key exists in PK table
     440             :      */
     441         239 :     ri_PerformCheck(riinfo, &qkey, qplan,
     442             :                     fk_rel, pk_rel,
     443             :                     NULL, new_row,
     444             :                     false,
     445             :                     SPI_OK_SELECT);
     446             : 
     447         195 :     if (SPI_finish() != SPI_OK_FINISH)
     448           0 :         elog(ERROR, "SPI_finish failed");
     449             : 
     450         195 :     heap_close(pk_rel, RowShareLock);
     451             : 
     452         195 :     return PointerGetDatum(NULL);
     453             : }
     454             : 
     455             : 
     456             : /* ----------
     457             :  * RI_FKey_check_ins -
     458             :  *
     459             :  *  Check foreign key existence at insert event on FK table.
     460             :  * ----------
     461             :  */
     462             : Datum
     463         248 : RI_FKey_check_ins(PG_FUNCTION_ARGS)
     464             : {
     465             :     /*
     466             :      * Check that this is a valid trigger call on the right time and event.
     467             :      */
     468         248 :     ri_CheckTrigger(fcinfo, "RI_FKey_check_ins", RI_TRIGTYPE_INSERT);
     469             : 
     470             :     /*
     471             :      * Share code with UPDATE case.
     472             :      */
     473         248 :     return RI_FKey_check((TriggerData *) fcinfo->context);
     474             : }
     475             : 
     476             : 
     477             : /* ----------
     478             :  * RI_FKey_check_upd -
     479             :  *
     480             :  *  Check foreign key existence at update event on FK table.
     481             :  * ----------
     482             :  */
     483             : Datum
     484          34 : RI_FKey_check_upd(PG_FUNCTION_ARGS)
     485             : {
     486             :     /*
     487             :      * Check that this is a valid trigger call on the right time and event.
     488             :      */
     489          34 :     ri_CheckTrigger(fcinfo, "RI_FKey_check_upd", RI_TRIGTYPE_UPDATE);
     490             : 
     491             :     /*
     492             :      * Share code with INSERT case.
     493             :      */
     494          34 :     return RI_FKey_check((TriggerData *) fcinfo->context);
     495             : }
     496             : 
     497             : 
     498             : /* ----------
     499             :  * ri_Check_Pk_Match
     500             :  *
     501             :  * Check to see if another PK row has been created that provides the same
     502             :  * key values as the "old_row" that's been modified or deleted in our trigger
     503             :  * event.  Returns true if a match is found in the PK table.
     504             :  *
     505             :  * We assume the caller checked that the old_row contains no NULL key values,
     506             :  * since otherwise a match is impossible.
     507             :  * ----------
     508             :  */
     509             : static bool
     510          53 : ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
     511             :                   HeapTuple old_row,
     512             :                   const RI_ConstraintInfo *riinfo)
     513             : {
     514             :     SPIPlanPtr  qplan;
     515             :     RI_QueryKey qkey;
     516             :     int         i;
     517             :     bool        result;
     518             : 
     519             :     /* Only called for non-null rows */
     520          53 :     Assert(ri_NullCheck(old_row, riinfo, true) == RI_KEYS_NONE_NULL);
     521             : 
     522          53 :     if (SPI_connect() != SPI_OK_CONNECT)
     523           0 :         elog(ERROR, "SPI_connect failed");
     524             : 
     525             :     /*
     526             :      * Fetch or prepare a saved plan for checking PK table with values coming
     527             :      * from a PK row
     528             :      */
     529          53 :     ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK_FROM_PK);
     530             : 
     531          53 :     if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
     532             :     {
     533             :         StringInfoData querybuf;
     534             :         char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
     535             :         char        attname[MAX_QUOTED_NAME_LEN];
     536             :         char        paramname[16];
     537             :         const char *querysep;
     538             :         Oid         queryoids[RI_MAX_NUMKEYS];
     539             : 
     540             :         /* ----------
     541             :          * The query string built is
     542             :          *  SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
     543             :          *         FOR KEY SHARE OF x
     544             :          * The type id's for the $ parameters are those of the
     545             :          * PK attributes themselves.
     546             :          * ----------
     547             :          */
     548          15 :         initStringInfo(&querybuf);
     549          15 :         quoteRelationName(pkrelname, pk_rel);
     550          15 :         appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
     551          15 :         querysep = "WHERE";
     552          41 :         for (i = 0; i < riinfo->nkeys; i++)
     553             :         {
     554          26 :             Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
     555             : 
     556          26 :             quoteOneName(attname,
     557          26 :                          RIAttName(pk_rel, riinfo->pk_attnums[i]));
     558          26 :             sprintf(paramname, "$%d", i + 1);
     559          26 :             ri_GenerateQual(&querybuf, querysep,
     560             :                             attname, pk_type,
     561             :                             riinfo->pp_eq_oprs[i],
     562             :                             paramname, pk_type);
     563          26 :             querysep = "AND";
     564          26 :             queryoids[i] = pk_type;
     565             :         }
     566          15 :         appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
     567             : 
     568             :         /* Prepare and save the plan */
     569          15 :         qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
     570             :                              &qkey, fk_rel, pk_rel, true);
     571             :     }
     572             : 
     573             :     /*
     574             :      * We have a plan now. Run it.
     575             :      */
     576          53 :     result = ri_PerformCheck(riinfo, &qkey, qplan,
     577             :                              fk_rel, pk_rel,
     578             :                              old_row, NULL,
     579             :                              true,  /* treat like update */
     580             :                              SPI_OK_SELECT);
     581             : 
     582          53 :     if (SPI_finish() != SPI_OK_FINISH)
     583           0 :         elog(ERROR, "SPI_finish failed");
     584             : 
     585          53 :     return result;
     586             : }
     587             : 
     588             : 
     589             : /* ----------
     590             :  * RI_FKey_noaction_del -
     591             :  *
     592             :  *  Give an error and roll back the current transaction if the
     593             :  *  delete has resulted in a violation of the given referential
     594             :  *  integrity constraint.
     595             :  * ----------
     596             :  */
     597             : Datum
     598          26 : RI_FKey_noaction_del(PG_FUNCTION_ARGS)
     599             : {
     600             :     /*
     601             :      * Check that this is a valid trigger call on the right time and event.
     602             :      */
     603          26 :     ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
     604             : 
     605             :     /*
     606             :      * Share code with RESTRICT case.
     607             :      */
     608          26 :     return ri_restrict_del((TriggerData *) fcinfo->context, true);
     609             : }
     610             : 
     611             : /* ----------
     612             :  * RI_FKey_restrict_del -
     613             :  *
     614             :  *  Restrict delete from PK table to rows unreferenced by foreign key.
     615             :  *
     616             :  *  The SQL standard intends that this referential action occur exactly when
     617             :  *  the delete is performed, rather than after.  This appears to be
     618             :  *  the only difference between "NO ACTION" and "RESTRICT".  In Postgres
     619             :  *  we still implement this as an AFTER trigger, but it's non-deferrable.
     620             :  * ----------
     621             :  */
     622             : Datum
     623           0 : RI_FKey_restrict_del(PG_FUNCTION_ARGS)
     624             : {
     625             :     /*
     626             :      * Check that this is a valid trigger call on the right time and event.
     627             :      */
     628           0 :     ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
     629             : 
     630             :     /*
     631             :      * Share code with NO ACTION case.
     632             :      */
     633           0 :     return ri_restrict_del((TriggerData *) fcinfo->context, false);
     634             : }
     635             : 
     636             : /* ----------
     637             :  * ri_restrict_del -
     638             :  *
     639             :  *  Common code for ON DELETE RESTRICT and ON DELETE NO ACTION.
     640             :  * ----------
     641             :  */
     642             : static Datum
     643          26 : ri_restrict_del(TriggerData *trigdata, bool is_no_action)
     644             : {
     645             :     const RI_ConstraintInfo *riinfo;
     646             :     Relation    fk_rel;
     647             :     Relation    pk_rel;
     648             :     HeapTuple   old_row;
     649             :     RI_QueryKey qkey;
     650             :     SPIPlanPtr  qplan;
     651             :     int         i;
     652             : 
     653             :     /*
     654             :      * Get arguments.
     655             :      */
     656          26 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
     657             :                                     trigdata->tg_relation, true);
     658             : 
     659             :     /*
     660             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
     661             :      *
     662             :      * fk_rel is opened in RowShareLock mode since that's what our eventual
     663             :      * SELECT FOR KEY SHARE will get on it.
     664             :      */
     665          26 :     fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
     666          26 :     pk_rel = trigdata->tg_relation;
     667          26 :     old_row = trigdata->tg_trigtuple;
     668             : 
     669          26 :     switch (riinfo->confmatchtype)
     670             :     {
     671             :             /* ----------
     672             :              * SQL:2008 15.17 <Execution of referential actions>
     673             :              *  General rules 9) a) iv):
     674             :              *      MATCH SIMPLE/FULL
     675             :              *          ... ON DELETE RESTRICT
     676             :              * ----------
     677             :              */
     678             :         case FKCONSTR_MATCH_SIMPLE:
     679             :         case FKCONSTR_MATCH_FULL:
     680          26 :             switch (ri_NullCheck(old_row, riinfo, true))
     681             :             {
     682             :                 case RI_KEYS_ALL_NULL:
     683             :                 case RI_KEYS_SOME_NULL:
     684             : 
     685             :                     /*
     686             :                      * No check needed - there cannot be any reference to old
     687             :                      * key if it contains a NULL
     688             :                      */
     689           0 :                     heap_close(fk_rel, RowShareLock);
     690           0 :                     return PointerGetDatum(NULL);
     691             : 
     692             :                 case RI_KEYS_NONE_NULL:
     693             : 
     694             :                     /*
     695             :                      * Have a full qualified key - continue below
     696             :                      */
     697          26 :                     break;
     698             :             }
     699             : 
     700             :             /*
     701             :              * If another PK row now exists providing the old key values, we
     702             :              * should not do anything.  However, this check should only be
     703             :              * made in the NO ACTION case; in RESTRICT cases we don't wish to
     704             :              * allow another row to be substituted.
     705             :              */
     706          52 :             if (is_no_action &&
     707          26 :                 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
     708             :             {
     709           0 :                 heap_close(fk_rel, RowShareLock);
     710           0 :                 return PointerGetDatum(NULL);
     711             :             }
     712             : 
     713          26 :             if (SPI_connect() != SPI_OK_CONNECT)
     714           0 :                 elog(ERROR, "SPI_connect failed");
     715             : 
     716             :             /*
     717             :              * Fetch or prepare a saved plan for the restrict delete lookup
     718             :              */
     719          26 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_DEL_CHECKREF);
     720             : 
     721          26 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
     722             :             {
     723             :                 StringInfoData querybuf;
     724             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
     725             :                 char        attname[MAX_QUOTED_NAME_LEN];
     726             :                 char        paramname[16];
     727             :                 const char *querysep;
     728             :                 Oid         queryoids[RI_MAX_NUMKEYS];
     729             : 
     730             :                 /* ----------
     731             :                  * The query string built is
     732             :                  *  SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
     733             :                  *         FOR KEY SHARE OF x
     734             :                  * The type id's for the $ parameters are those of the
     735             :                  * corresponding PK attributes.
     736             :                  * ----------
     737             :                  */
     738          13 :                 initStringInfo(&querybuf);
     739          13 :                 quoteRelationName(fkrelname, fk_rel);
     740          13 :                 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
     741             :                                  fkrelname);
     742          13 :                 querysep = "WHERE";
     743          35 :                 for (i = 0; i < riinfo->nkeys; i++)
     744             :                 {
     745          22 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
     746          22 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
     747             : 
     748          22 :                     quoteOneName(attname,
     749          22 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
     750          22 :                     sprintf(paramname, "$%d", i + 1);
     751          22 :                     ri_GenerateQual(&querybuf, querysep,
     752             :                                     paramname, pk_type,
     753             :                                     riinfo->pf_eq_oprs[i],
     754             :                                     attname, fk_type);
     755          22 :                     querysep = "AND";
     756          22 :                     queryoids[i] = pk_type;
     757             :                 }
     758          13 :                 appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
     759             : 
     760             :                 /* Prepare and save the plan */
     761          13 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
     762             :                                      &qkey, fk_rel, pk_rel, true);
     763             :             }
     764             : 
     765             :             /*
     766             :              * We have a plan now. Run it to check for existing references.
     767             :              */
     768          26 :             ri_PerformCheck(riinfo, &qkey, qplan,
     769             :                             fk_rel, pk_rel,
     770             :                             old_row, NULL,
     771             :                             true,   /* must detect new rows */
     772             :                             SPI_OK_SELECT);
     773             : 
     774          16 :             if (SPI_finish() != SPI_OK_FINISH)
     775           0 :                 elog(ERROR, "SPI_finish failed");
     776             : 
     777          16 :             heap_close(fk_rel, RowShareLock);
     778             : 
     779          16 :             return PointerGetDatum(NULL);
     780             : 
     781             :             /*
     782             :              * Handle MATCH PARTIAL restrict delete.
     783             :              */
     784             :         case FKCONSTR_MATCH_PARTIAL:
     785           0 :             ereport(ERROR,
     786             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     787             :                      errmsg("MATCH PARTIAL not yet implemented")));
     788             :             return PointerGetDatum(NULL);
     789             : 
     790             :         default:
     791           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
     792             :                  riinfo->confmatchtype);
     793             :             break;
     794             :     }
     795             : 
     796             :     /* Never reached */
     797             :     return PointerGetDatum(NULL);
     798             : }
     799             : 
     800             : 
     801             : /* ----------
     802             :  * RI_FKey_noaction_upd -
     803             :  *
     804             :  *  Give an error and roll back the current transaction if the
     805             :  *  update has resulted in a violation of the given referential
     806             :  *  integrity constraint.
     807             :  * ----------
     808             :  */
     809             : Datum
     810          27 : RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
     811             : {
     812             :     /*
     813             :      * Check that this is a valid trigger call on the right time and event.
     814             :      */
     815          27 :     ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
     816             : 
     817             :     /*
     818             :      * Share code with RESTRICT case.
     819             :      */
     820          27 :     return ri_restrict_upd((TriggerData *) fcinfo->context, true);
     821             : }
     822             : 
     823             : /* ----------
     824             :  * RI_FKey_restrict_upd -
     825             :  *
     826             :  *  Restrict update of PK to rows unreferenced by foreign key.
     827             :  *
     828             :  *  The SQL standard intends that this referential action occur exactly when
     829             :  *  the update is performed, rather than after.  This appears to be
     830             :  *  the only difference between "NO ACTION" and "RESTRICT".  In Postgres
     831             :  *  we still implement this as an AFTER trigger, but it's non-deferrable.
     832             :  * ----------
     833             :  */
     834             : Datum
     835           3 : RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
     836             : {
     837             :     /*
     838             :      * Check that this is a valid trigger call on the right time and event.
     839             :      */
     840           3 :     ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
     841             : 
     842             :     /*
     843             :      * Share code with NO ACTION case.
     844             :      */
     845           3 :     return ri_restrict_upd((TriggerData *) fcinfo->context, false);
     846             : }
     847             : 
     848             : /* ----------
     849             :  * ri_restrict_upd -
     850             :  *
     851             :  *  Common code for ON UPDATE RESTRICT and ON UPDATE NO ACTION.
     852             :  * ----------
     853             :  */
     854             : static Datum
     855          30 : ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
     856             : {
     857             :     const RI_ConstraintInfo *riinfo;
     858             :     Relation    fk_rel;
     859             :     Relation    pk_rel;
     860             :     HeapTuple   new_row;
     861             :     HeapTuple   old_row;
     862             :     RI_QueryKey qkey;
     863             :     SPIPlanPtr  qplan;
     864             :     int         i;
     865             : 
     866             :     /*
     867             :      * Get arguments.
     868             :      */
     869          30 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
     870             :                                     trigdata->tg_relation, true);
     871             : 
     872             :     /*
     873             :      * Get the relation descriptors of the FK and PK tables and the new and
     874             :      * old tuple.
     875             :      *
     876             :      * fk_rel is opened in RowShareLock mode since that's what our eventual
     877             :      * SELECT FOR KEY SHARE will get on it.
     878             :      */
     879          30 :     fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
     880          30 :     pk_rel = trigdata->tg_relation;
     881          30 :     new_row = trigdata->tg_newtuple;
     882          30 :     old_row = trigdata->tg_trigtuple;
     883             : 
     884          30 :     switch (riinfo->confmatchtype)
     885             :     {
     886             :             /* ----------
     887             :              * SQL:2008 15.17 <Execution of referential actions>
     888             :              *  General rules 10) a) iv):
     889             :              *      MATCH SIMPLE/FULL
     890             :              *          ... ON UPDATE RESTRICT
     891             :              * ----------
     892             :              */
     893             :         case FKCONSTR_MATCH_SIMPLE:
     894             :         case FKCONSTR_MATCH_FULL:
     895          30 :             switch (ri_NullCheck(old_row, riinfo, true))
     896             :             {
     897             :                 case RI_KEYS_ALL_NULL:
     898             :                 case RI_KEYS_SOME_NULL:
     899             : 
     900             :                     /*
     901             :                      * No check needed - there cannot be any reference to old
     902             :                      * key if it contains a NULL
     903             :                      */
     904           0 :                     heap_close(fk_rel, RowShareLock);
     905           0 :                     return PointerGetDatum(NULL);
     906             : 
     907             :                 case RI_KEYS_NONE_NULL:
     908             : 
     909             :                     /*
     910             :                      * Have a full qualified key - continue below
     911             :                      */
     912          30 :                     break;
     913             :             }
     914             : 
     915             :             /*
     916             :              * No need to check anything if old and new keys are equal
     917             :              */
     918          30 :             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
     919             :             {
     920           0 :                 heap_close(fk_rel, RowShareLock);
     921           0 :                 return PointerGetDatum(NULL);
     922             :             }
     923             : 
     924             :             /*
     925             :              * If another PK row now exists providing the old key values, we
     926             :              * should not do anything.  However, this check should only be
     927             :              * made in the NO ACTION case; in RESTRICT cases we don't wish to
     928             :              * allow another row to be substituted.
     929             :              */
     930          57 :             if (is_no_action &&
     931          27 :                 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
     932             :             {
     933           3 :                 heap_close(fk_rel, RowShareLock);
     934           3 :                 return PointerGetDatum(NULL);
     935             :             }
     936             : 
     937          27 :             if (SPI_connect() != SPI_OK_CONNECT)
     938           0 :                 elog(ERROR, "SPI_connect failed");
     939             : 
     940             :             /*
     941             :              * Fetch or prepare a saved plan for the restrict update lookup
     942             :              */
     943          27 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_UPD_CHECKREF);
     944             : 
     945          27 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
     946             :             {
     947             :                 StringInfoData querybuf;
     948             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
     949             :                 char        attname[MAX_QUOTED_NAME_LEN];
     950             :                 char        paramname[16];
     951             :                 const char *querysep;
     952             :                 Oid         queryoids[RI_MAX_NUMKEYS];
     953             : 
     954             :                 /* ----------
     955             :                  * The query string built is
     956             :                  *  SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
     957             :                  * The type id's for the $ parameters are those of the
     958             :                  * corresponding PK attributes.
     959             :                  * ----------
     960             :                  */
     961          10 :                 initStringInfo(&querybuf);
     962          10 :                 quoteRelationName(fkrelname, fk_rel);
     963          10 :                 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
     964             :                                  fkrelname);
     965          10 :                 querysep = "WHERE";
     966          27 :                 for (i = 0; i < riinfo->nkeys; i++)
     967             :                 {
     968          17 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
     969          17 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
     970             : 
     971          17 :                     quoteOneName(attname,
     972          17 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
     973          17 :                     sprintf(paramname, "$%d", i + 1);
     974          17 :                     ri_GenerateQual(&querybuf, querysep,
     975             :                                     paramname, pk_type,
     976             :                                     riinfo->pf_eq_oprs[i],
     977             :                                     attname, fk_type);
     978          17 :                     querysep = "AND";
     979          17 :                     queryoids[i] = pk_type;
     980             :                 }
     981          10 :                 appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
     982             : 
     983             :                 /* Prepare and save the plan */
     984          10 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
     985             :                                      &qkey, fk_rel, pk_rel, true);
     986             :             }
     987             : 
     988             :             /*
     989             :              * We have a plan now. Run it to check for existing references.
     990             :              */
     991          27 :             ri_PerformCheck(riinfo, &qkey, qplan,
     992             :                             fk_rel, pk_rel,
     993             :                             old_row, NULL,
     994             :                             true,   /* must detect new rows */
     995             :                             SPI_OK_SELECT);
     996             : 
     997          19 :             if (SPI_finish() != SPI_OK_FINISH)
     998           0 :                 elog(ERROR, "SPI_finish failed");
     999             : 
    1000          19 :             heap_close(fk_rel, RowShareLock);
    1001             : 
    1002          19 :             return PointerGetDatum(NULL);
    1003             : 
    1004             :             /*
    1005             :              * Handle MATCH PARTIAL restrict update.
    1006             :              */
    1007             :         case FKCONSTR_MATCH_PARTIAL:
    1008           0 :             ereport(ERROR,
    1009             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1010             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1011             :             return PointerGetDatum(NULL);
    1012             : 
    1013             :         default:
    1014           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1015             :                  riinfo->confmatchtype);
    1016             :             break;
    1017             :     }
    1018             : 
    1019             :     /* Never reached */
    1020             :     return PointerGetDatum(NULL);
    1021             : }
    1022             : 
    1023             : 
    1024             : /* ----------
    1025             :  * RI_FKey_cascade_del -
    1026             :  *
    1027             :  *  Cascaded delete foreign key references at delete event on PK table.
    1028             :  * ----------
    1029             :  */
    1030             : Datum
    1031           6 : RI_FKey_cascade_del(PG_FUNCTION_ARGS)
    1032             : {
    1033           6 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1034             :     const RI_ConstraintInfo *riinfo;
    1035             :     Relation    fk_rel;
    1036             :     Relation    pk_rel;
    1037             :     HeapTuple   old_row;
    1038             :     RI_QueryKey qkey;
    1039             :     SPIPlanPtr  qplan;
    1040             :     int         i;
    1041             : 
    1042             :     /*
    1043             :      * Check that this is a valid trigger call on the right time and event.
    1044             :      */
    1045           6 :     ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
    1046             : 
    1047             :     /*
    1048             :      * Get arguments.
    1049             :      */
    1050           6 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1051             :                                     trigdata->tg_relation, true);
    1052             : 
    1053             :     /*
    1054             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
    1055             :      *
    1056             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1057             :      * eventual DELETE will get on it.
    1058             :      */
    1059           6 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1060           6 :     pk_rel = trigdata->tg_relation;
    1061           6 :     old_row = trigdata->tg_trigtuple;
    1062             : 
    1063           6 :     switch (riinfo->confmatchtype)
    1064             :     {
    1065             :             /* ----------
    1066             :              * SQL:2008 15.17 <Execution of referential actions>
    1067             :              *  General rules 9) a) i):
    1068             :              *      MATCH SIMPLE/FULL
    1069             :              *          ... ON DELETE CASCADE
    1070             :              * ----------
    1071             :              */
    1072             :         case FKCONSTR_MATCH_SIMPLE:
    1073             :         case FKCONSTR_MATCH_FULL:
    1074           6 :             switch (ri_NullCheck(old_row, riinfo, true))
    1075             :             {
    1076             :                 case RI_KEYS_ALL_NULL:
    1077             :                 case RI_KEYS_SOME_NULL:
    1078             : 
    1079             :                     /*
    1080             :                      * No check needed - there cannot be any reference to old
    1081             :                      * key if it contains a NULL
    1082             :                      */
    1083           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1084           0 :                     return PointerGetDatum(NULL);
    1085             : 
    1086             :                 case RI_KEYS_NONE_NULL:
    1087             : 
    1088             :                     /*
    1089             :                      * Have a full qualified key - continue below
    1090             :                      */
    1091           6 :                     break;
    1092             :             }
    1093             : 
    1094           6 :             if (SPI_connect() != SPI_OK_CONNECT)
    1095           0 :                 elog(ERROR, "SPI_connect failed");
    1096             : 
    1097             :             /*
    1098             :              * Fetch or prepare a saved plan for the cascaded delete
    1099             :              */
    1100           6 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
    1101             : 
    1102           6 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1103             :             {
    1104             :                 StringInfoData querybuf;
    1105             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1106             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1107             :                 char        paramname[16];
    1108             :                 const char *querysep;
    1109             :                 Oid         queryoids[RI_MAX_NUMKEYS];
    1110             : 
    1111             :                 /* ----------
    1112             :                  * The query string built is
    1113             :                  *  DELETE FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
    1114             :                  * The type id's for the $ parameters are those of the
    1115             :                  * corresponding PK attributes.
    1116             :                  * ----------
    1117             :                  */
    1118           4 :                 initStringInfo(&querybuf);
    1119           4 :                 quoteRelationName(fkrelname, fk_rel);
    1120           4 :                 appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
    1121           4 :                 querysep = "WHERE";
    1122          10 :                 for (i = 0; i < riinfo->nkeys; i++)
    1123             :                 {
    1124           6 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    1125           6 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    1126             : 
    1127           6 :                     quoteOneName(attname,
    1128           6 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    1129           6 :                     sprintf(paramname, "$%d", i + 1);
    1130           6 :                     ri_GenerateQual(&querybuf, querysep,
    1131             :                                     paramname, pk_type,
    1132             :                                     riinfo->pf_eq_oprs[i],
    1133             :                                     attname, fk_type);
    1134           6 :                     querysep = "AND";
    1135           6 :                     queryoids[i] = pk_type;
    1136             :                 }
    1137             : 
    1138             :                 /* Prepare and save the plan */
    1139           4 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
    1140             :                                      &qkey, fk_rel, pk_rel, true);
    1141             :             }
    1142             : 
    1143             :             /*
    1144             :              * We have a plan now. Build up the arguments from the key values
    1145             :              * in the deleted PK tuple and delete the referencing rows
    1146             :              */
    1147           6 :             ri_PerformCheck(riinfo, &qkey, qplan,
    1148             :                             fk_rel, pk_rel,
    1149             :                             old_row, NULL,
    1150             :                             true,   /* must detect new rows */
    1151             :                             SPI_OK_DELETE);
    1152             : 
    1153           6 :             if (SPI_finish() != SPI_OK_FINISH)
    1154           0 :                 elog(ERROR, "SPI_finish failed");
    1155             : 
    1156           6 :             heap_close(fk_rel, RowExclusiveLock);
    1157             : 
    1158           6 :             return PointerGetDatum(NULL);
    1159             : 
    1160             :             /*
    1161             :              * Handle MATCH PARTIAL cascaded delete.
    1162             :              */
    1163             :         case FKCONSTR_MATCH_PARTIAL:
    1164           0 :             ereport(ERROR,
    1165             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1166             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1167             :             return PointerGetDatum(NULL);
    1168             : 
    1169             :         default:
    1170           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1171             :                  riinfo->confmatchtype);
    1172             :             break;
    1173             :     }
    1174             : 
    1175             :     /* Never reached */
    1176             :     return PointerGetDatum(NULL);
    1177             : }
    1178             : 
    1179             : 
    1180             : /* ----------
    1181             :  * RI_FKey_cascade_upd -
    1182             :  *
    1183             :  *  Cascaded update foreign key references at update event on PK table.
    1184             :  * ----------
    1185             :  */
    1186             : Datum
    1187          10 : RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
    1188             : {
    1189          10 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1190             :     const RI_ConstraintInfo *riinfo;
    1191             :     Relation    fk_rel;
    1192             :     Relation    pk_rel;
    1193             :     HeapTuple   new_row;
    1194             :     HeapTuple   old_row;
    1195             :     RI_QueryKey qkey;
    1196             :     SPIPlanPtr  qplan;
    1197             :     int         i;
    1198             :     int         j;
    1199             : 
    1200             :     /*
    1201             :      * Check that this is a valid trigger call on the right time and event.
    1202             :      */
    1203          10 :     ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
    1204             : 
    1205             :     /*
    1206             :      * Get arguments.
    1207             :      */
    1208          10 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1209             :                                     trigdata->tg_relation, true);
    1210             : 
    1211             :     /*
    1212             :      * Get the relation descriptors of the FK and PK tables and the new and
    1213             :      * old tuple.
    1214             :      *
    1215             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1216             :      * eventual UPDATE will get on it.
    1217             :      */
    1218          10 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1219          10 :     pk_rel = trigdata->tg_relation;
    1220          10 :     new_row = trigdata->tg_newtuple;
    1221          10 :     old_row = trigdata->tg_trigtuple;
    1222             : 
    1223          10 :     switch (riinfo->confmatchtype)
    1224             :     {
    1225             :             /* ----------
    1226             :              * SQL:2008 15.17 <Execution of referential actions>
    1227             :              *  General rules 10) a) i):
    1228             :              *      MATCH SIMPLE/FULL
    1229             :              *          ... ON UPDATE CASCADE
    1230             :              * ----------
    1231             :              */
    1232             :         case FKCONSTR_MATCH_SIMPLE:
    1233             :         case FKCONSTR_MATCH_FULL:
    1234          10 :             switch (ri_NullCheck(old_row, riinfo, true))
    1235             :             {
    1236             :                 case RI_KEYS_ALL_NULL:
    1237             :                 case RI_KEYS_SOME_NULL:
    1238             : 
    1239             :                     /*
    1240             :                      * No check needed - there cannot be any reference to old
    1241             :                      * key if it contains a NULL
    1242             :                      */
    1243           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1244           0 :                     return PointerGetDatum(NULL);
    1245             : 
    1246             :                 case RI_KEYS_NONE_NULL:
    1247             : 
    1248             :                     /*
    1249             :                      * Have a full qualified key - continue below
    1250             :                      */
    1251          10 :                     break;
    1252             :             }
    1253             : 
    1254             :             /*
    1255             :              * No need to do anything if old and new keys are equal
    1256             :              */
    1257          10 :             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
    1258             :             {
    1259           0 :                 heap_close(fk_rel, RowExclusiveLock);
    1260           0 :                 return PointerGetDatum(NULL);
    1261             :             }
    1262             : 
    1263          10 :             if (SPI_connect() != SPI_OK_CONNECT)
    1264           0 :                 elog(ERROR, "SPI_connect failed");
    1265             : 
    1266             :             /*
    1267             :              * Fetch or prepare a saved plan for the cascaded update
    1268             :              */
    1269          10 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
    1270             : 
    1271          10 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1272             :             {
    1273             :                 StringInfoData querybuf;
    1274             :                 StringInfoData qualbuf;
    1275             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1276             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1277             :                 char        paramname[16];
    1278             :                 const char *querysep;
    1279             :                 const char *qualsep;
    1280             :                 Oid         queryoids[RI_MAX_NUMKEYS * 2];
    1281             : 
    1282             :                 /* ----------
    1283             :                  * The query string built is
    1284             :                  *  UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]
    1285             :                  *          WHERE $n = fkatt1 [AND ...]
    1286             :                  * The type id's for the $ parameters are those of the
    1287             :                  * corresponding PK attributes.  Note that we are assuming
    1288             :                  * there is an assignment cast from the PK to the FK type;
    1289             :                  * else the parser will fail.
    1290             :                  * ----------
    1291             :                  */
    1292           7 :                 initStringInfo(&querybuf);
    1293           7 :                 initStringInfo(&qualbuf);
    1294           7 :                 quoteRelationName(fkrelname, fk_rel);
    1295           7 :                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
    1296           7 :                 querysep = "";
    1297           7 :                 qualsep = "WHERE";
    1298          16 :                 for (i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
    1299             :                 {
    1300           9 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    1301           9 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    1302             : 
    1303           9 :                     quoteOneName(attname,
    1304           9 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    1305           9 :                     appendStringInfo(&querybuf,
    1306             :                                      "%s %s = $%d",
    1307             :                                      querysep, attname, i + 1);
    1308           9 :                     sprintf(paramname, "$%d", j + 1);
    1309           9 :                     ri_GenerateQual(&qualbuf, qualsep,
    1310             :                                     paramname, pk_type,
    1311             :                                     riinfo->pf_eq_oprs[i],
    1312             :                                     attname, fk_type);
    1313           9 :                     querysep = ",";
    1314           9 :                     qualsep = "AND";
    1315           9 :                     queryoids[i] = pk_type;
    1316           9 :                     queryoids[j] = pk_type;
    1317             :                 }
    1318           7 :                 appendStringInfoString(&querybuf, qualbuf.data);
    1319             : 
    1320             :                 /* Prepare and save the plan */
    1321           7 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
    1322             :                                      &qkey, fk_rel, pk_rel, true);
    1323             :             }
    1324             : 
    1325             :             /*
    1326             :              * We have a plan now. Run it to update the existing references.
    1327             :              */
    1328          10 :             ri_PerformCheck(riinfo, &qkey, qplan,
    1329             :                             fk_rel, pk_rel,
    1330             :                             old_row, new_row,
    1331             :                             true,   /* must detect new rows */
    1332             :                             SPI_OK_UPDATE);
    1333             : 
    1334          10 :             if (SPI_finish() != SPI_OK_FINISH)
    1335           0 :                 elog(ERROR, "SPI_finish failed");
    1336             : 
    1337          10 :             heap_close(fk_rel, RowExclusiveLock);
    1338             : 
    1339          10 :             return PointerGetDatum(NULL);
    1340             : 
    1341             :             /*
    1342             :              * Handle MATCH PARTIAL cascade update.
    1343             :              */
    1344             :         case FKCONSTR_MATCH_PARTIAL:
    1345           0 :             ereport(ERROR,
    1346             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1347             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1348             :             return PointerGetDatum(NULL);
    1349             : 
    1350             :         default:
    1351           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1352             :                  riinfo->confmatchtype);
    1353             :             break;
    1354             :     }
    1355             : 
    1356             :     /* Never reached */
    1357             :     return PointerGetDatum(NULL);
    1358             : }
    1359             : 
    1360             : 
    1361             : /* ----------
    1362             :  * RI_FKey_setnull_del -
    1363             :  *
    1364             :  *  Set foreign key references to NULL values at delete event on PK table.
    1365             :  * ----------
    1366             :  */
    1367             : Datum
    1368          10 : RI_FKey_setnull_del(PG_FUNCTION_ARGS)
    1369             : {
    1370          10 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1371             :     const RI_ConstraintInfo *riinfo;
    1372             :     Relation    fk_rel;
    1373             :     Relation    pk_rel;
    1374             :     HeapTuple   old_row;
    1375             :     RI_QueryKey qkey;
    1376             :     SPIPlanPtr  qplan;
    1377             :     int         i;
    1378             : 
    1379             :     /*
    1380             :      * Check that this is a valid trigger call on the right time and event.
    1381             :      */
    1382          10 :     ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
    1383             : 
    1384             :     /*
    1385             :      * Get arguments.
    1386             :      */
    1387          10 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1388             :                                     trigdata->tg_relation, true);
    1389             : 
    1390             :     /*
    1391             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
    1392             :      *
    1393             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1394             :      * eventual UPDATE will get on it.
    1395             :      */
    1396          10 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1397          10 :     pk_rel = trigdata->tg_relation;
    1398          10 :     old_row = trigdata->tg_trigtuple;
    1399             : 
    1400          10 :     switch (riinfo->confmatchtype)
    1401             :     {
    1402             :             /* ----------
    1403             :              * SQL:2008 15.17 <Execution of referential actions>
    1404             :              *  General rules 9) a) ii):
    1405             :              *      MATCH SIMPLE/FULL
    1406             :              *          ... ON DELETE SET NULL
    1407             :              * ----------
    1408             :              */
    1409             :         case FKCONSTR_MATCH_SIMPLE:
    1410             :         case FKCONSTR_MATCH_FULL:
    1411          10 :             switch (ri_NullCheck(old_row, riinfo, true))
    1412             :             {
    1413             :                 case RI_KEYS_ALL_NULL:
    1414             :                 case RI_KEYS_SOME_NULL:
    1415             : 
    1416             :                     /*
    1417             :                      * No check needed - there cannot be any reference to old
    1418             :                      * key if it contains a NULL
    1419             :                      */
    1420           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1421           0 :                     return PointerGetDatum(NULL);
    1422             : 
    1423             :                 case RI_KEYS_NONE_NULL:
    1424             : 
    1425             :                     /*
    1426             :                      * Have a full qualified key - continue below
    1427             :                      */
    1428          10 :                     break;
    1429             :             }
    1430             : 
    1431          10 :             if (SPI_connect() != SPI_OK_CONNECT)
    1432           0 :                 elog(ERROR, "SPI_connect failed");
    1433             : 
    1434             :             /*
    1435             :              * Fetch or prepare a saved plan for the set null delete operation
    1436             :              */
    1437          10 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DEL_DOUPDATE);
    1438             : 
    1439          10 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1440             :             {
    1441             :                 StringInfoData querybuf;
    1442             :                 StringInfoData qualbuf;
    1443             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1444             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1445             :                 char        paramname[16];
    1446             :                 const char *querysep;
    1447             :                 const char *qualsep;
    1448             :                 Oid         queryoids[RI_MAX_NUMKEYS];
    1449             : 
    1450             :                 /* ----------
    1451             :                  * The query string built is
    1452             :                  *  UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
    1453             :                  *          WHERE $1 = fkatt1 [AND ...]
    1454             :                  * The type id's for the $ parameters are those of the
    1455             :                  * corresponding PK attributes.
    1456             :                  * ----------
    1457             :                  */
    1458           5 :                 initStringInfo(&querybuf);
    1459           5 :                 initStringInfo(&qualbuf);
    1460           5 :                 quoteRelationName(fkrelname, fk_rel);
    1461           5 :                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
    1462           5 :                 querysep = "";
    1463           5 :                 qualsep = "WHERE";
    1464          13 :                 for (i = 0; i < riinfo->nkeys; i++)
    1465             :                 {
    1466           8 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    1467           8 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    1468             : 
    1469           8 :                     quoteOneName(attname,
    1470           8 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    1471           8 :                     appendStringInfo(&querybuf,
    1472             :                                      "%s %s = NULL",
    1473             :                                      querysep, attname);
    1474           8 :                     sprintf(paramname, "$%d", i + 1);
    1475           8 :                     ri_GenerateQual(&qualbuf, qualsep,
    1476             :                                     paramname, pk_type,
    1477             :                                     riinfo->pf_eq_oprs[i],
    1478             :                                     attname, fk_type);
    1479           8 :                     querysep = ",";
    1480           8 :                     qualsep = "AND";
    1481           8 :                     queryoids[i] = pk_type;
    1482             :                 }
    1483           5 :                 appendStringInfoString(&querybuf, qualbuf.data);
    1484             : 
    1485             :                 /* Prepare and save the plan */
    1486           5 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
    1487             :                                      &qkey, fk_rel, pk_rel, true);
    1488             :             }
    1489             : 
    1490             :             /*
    1491             :              * We have a plan now. Run it to check for existing references.
    1492             :              */
    1493          10 :             ri_PerformCheck(riinfo, &qkey, qplan,
    1494             :                             fk_rel, pk_rel,
    1495             :                             old_row, NULL,
    1496             :                             true,   /* must detect new rows */
    1497             :                             SPI_OK_UPDATE);
    1498             : 
    1499          10 :             if (SPI_finish() != SPI_OK_FINISH)
    1500           0 :                 elog(ERROR, "SPI_finish failed");
    1501             : 
    1502          10 :             heap_close(fk_rel, RowExclusiveLock);
    1503             : 
    1504          10 :             return PointerGetDatum(NULL);
    1505             : 
    1506             :             /*
    1507             :              * Handle MATCH PARTIAL set null delete.
    1508             :              */
    1509             :         case FKCONSTR_MATCH_PARTIAL:
    1510           0 :             ereport(ERROR,
    1511             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1512             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1513             :             return PointerGetDatum(NULL);
    1514             : 
    1515             :         default:
    1516           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1517             :                  riinfo->confmatchtype);
    1518             :             break;
    1519             :     }
    1520             : 
    1521             :     /* Never reached */
    1522             :     return PointerGetDatum(NULL);
    1523             : }
    1524             : 
    1525             : 
    1526             : /* ----------
    1527             :  * RI_FKey_setnull_upd -
    1528             :  *
    1529             :  *  Set foreign key references to NULL at update event on PK table.
    1530             :  * ----------
    1531             :  */
    1532             : Datum
    1533           3 : RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
    1534             : {
    1535           3 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1536             :     const RI_ConstraintInfo *riinfo;
    1537             :     Relation    fk_rel;
    1538             :     Relation    pk_rel;
    1539             :     HeapTuple   new_row;
    1540             :     HeapTuple   old_row;
    1541             :     RI_QueryKey qkey;
    1542             :     SPIPlanPtr  qplan;
    1543             :     int         i;
    1544             : 
    1545             :     /*
    1546             :      * Check that this is a valid trigger call on the right time and event.
    1547             :      */
    1548           3 :     ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
    1549             : 
    1550             :     /*
    1551             :      * Get arguments.
    1552             :      */
    1553           3 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1554             :                                     trigdata->tg_relation, true);
    1555             : 
    1556             :     /*
    1557             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
    1558             :      *
    1559             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1560             :      * eventual UPDATE will get on it.
    1561             :      */
    1562           3 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1563           3 :     pk_rel = trigdata->tg_relation;
    1564           3 :     new_row = trigdata->tg_newtuple;
    1565           3 :     old_row = trigdata->tg_trigtuple;
    1566             : 
    1567           3 :     switch (riinfo->confmatchtype)
    1568             :     {
    1569             :             /* ----------
    1570             :              * SQL:2008 15.17 <Execution of referential actions>
    1571             :              *  General rules 10) a) ii):
    1572             :              *      MATCH SIMPLE/FULL
    1573             :              *          ... ON UPDATE SET NULL
    1574             :              * ----------
    1575             :              */
    1576             :         case FKCONSTR_MATCH_SIMPLE:
    1577             :         case FKCONSTR_MATCH_FULL:
    1578           3 :             switch (ri_NullCheck(old_row, riinfo, true))
    1579             :             {
    1580             :                 case RI_KEYS_ALL_NULL:
    1581             :                 case RI_KEYS_SOME_NULL:
    1582             : 
    1583             :                     /*
    1584             :                      * No check needed - there cannot be any reference to old
    1585             :                      * key if it contains a NULL
    1586             :                      */
    1587           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1588           0 :                     return PointerGetDatum(NULL);
    1589             : 
    1590             :                 case RI_KEYS_NONE_NULL:
    1591             : 
    1592             :                     /*
    1593             :                      * Have a full qualified key - continue below
    1594             :                      */
    1595           3 :                     break;
    1596             :             }
    1597             : 
    1598             :             /*
    1599             :              * No need to do anything if old and new keys are equal
    1600             :              */
    1601           3 :             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
    1602             :             {
    1603           0 :                 heap_close(fk_rel, RowExclusiveLock);
    1604           0 :                 return PointerGetDatum(NULL);
    1605             :             }
    1606             : 
    1607           3 :             if (SPI_connect() != SPI_OK_CONNECT)
    1608           0 :                 elog(ERROR, "SPI_connect failed");
    1609             : 
    1610             :             /*
    1611             :              * Fetch or prepare a saved plan for the set null update operation
    1612             :              */
    1613           3 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_UPD_DOUPDATE);
    1614             : 
    1615           3 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1616             :             {
    1617             :                 StringInfoData querybuf;
    1618             :                 StringInfoData qualbuf;
    1619             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1620             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1621             :                 char        paramname[16];
    1622             :                 const char *querysep;
    1623             :                 const char *qualsep;
    1624             :                 Oid         queryoids[RI_MAX_NUMKEYS];
    1625             : 
    1626             :                 /* ----------
    1627             :                  * The query string built is
    1628             :                  *  UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
    1629             :                  *          WHERE $1 = fkatt1 [AND ...]
    1630             :                  * The type id's for the $ parameters are those of the
    1631             :                  * corresponding PK attributes.
    1632             :                  * ----------
    1633             :                  */
    1634           2 :                 initStringInfo(&querybuf);
    1635           2 :                 initStringInfo(&qualbuf);
    1636           2 :                 quoteRelationName(fkrelname, fk_rel);
    1637           2 :                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
    1638           2 :                 querysep = "";
    1639           2 :                 qualsep = "WHERE";
    1640           7 :                 for (i = 0; i < riinfo->nkeys; i++)
    1641             :                 {
    1642           5 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    1643           5 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    1644             : 
    1645           5 :                     quoteOneName(attname,
    1646           5 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    1647           5 :                     appendStringInfo(&querybuf,
    1648             :                                      "%s %s = NULL",
    1649             :                                      querysep, attname);
    1650           5 :                     sprintf(paramname, "$%d", i + 1);
    1651           5 :                     ri_GenerateQual(&qualbuf, qualsep,
    1652             :                                     paramname, pk_type,
    1653             :                                     riinfo->pf_eq_oprs[i],
    1654             :                                     attname, fk_type);
    1655           5 :                     querysep = ",";
    1656           5 :                     qualsep = "AND";
    1657           5 :                     queryoids[i] = pk_type;
    1658             :                 }
    1659           2 :                 appendStringInfoString(&querybuf, qualbuf.data);
    1660             : 
    1661             :                 /* Prepare and save the plan */
    1662           2 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
    1663             :                                      &qkey, fk_rel, pk_rel, true);
    1664             :             }
    1665             : 
    1666             :             /*
    1667             :              * We have a plan now. Run it to update the existing references.
    1668             :              */
    1669           3 :             ri_PerformCheck(riinfo, &qkey, qplan,
    1670             :                             fk_rel, pk_rel,
    1671             :                             old_row, NULL,
    1672             :                             true,   /* must detect new rows */
    1673             :                             SPI_OK_UPDATE);
    1674             : 
    1675           3 :             if (SPI_finish() != SPI_OK_FINISH)
    1676           0 :                 elog(ERROR, "SPI_finish failed");
    1677             : 
    1678           3 :             heap_close(fk_rel, RowExclusiveLock);
    1679             : 
    1680           3 :             return PointerGetDatum(NULL);
    1681             : 
    1682             :             /*
    1683             :              * Handle MATCH PARTIAL set null update.
    1684             :              */
    1685             :         case FKCONSTR_MATCH_PARTIAL:
    1686           0 :             ereport(ERROR,
    1687             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1688             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1689             :             return PointerGetDatum(NULL);
    1690             : 
    1691             :         default:
    1692           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1693             :                  riinfo->confmatchtype);
    1694             :             break;
    1695             :     }
    1696             : 
    1697             :     /* Never reached */
    1698             :     return PointerGetDatum(NULL);
    1699             : }
    1700             : 
    1701             : 
    1702             : /* ----------
    1703             :  * RI_FKey_setdefault_del -
    1704             :  *
    1705             :  *  Set foreign key references to defaults at delete event on PK table.
    1706             :  * ----------
    1707             :  */
    1708             : Datum
    1709           8 : RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
    1710             : {
    1711           8 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1712             :     const RI_ConstraintInfo *riinfo;
    1713             :     Relation    fk_rel;
    1714             :     Relation    pk_rel;
    1715             :     HeapTuple   old_row;
    1716             :     RI_QueryKey qkey;
    1717             :     SPIPlanPtr  qplan;
    1718             : 
    1719             :     /*
    1720             :      * Check that this is a valid trigger call on the right time and event.
    1721             :      */
    1722           8 :     ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
    1723             : 
    1724             :     /*
    1725             :      * Get arguments.
    1726             :      */
    1727           8 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1728             :                                     trigdata->tg_relation, true);
    1729             : 
    1730             :     /*
    1731             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
    1732             :      *
    1733             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1734             :      * eventual UPDATE will get on it.
    1735             :      */
    1736           8 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1737           8 :     pk_rel = trigdata->tg_relation;
    1738           8 :     old_row = trigdata->tg_trigtuple;
    1739             : 
    1740           8 :     switch (riinfo->confmatchtype)
    1741             :     {
    1742             :             /* ----------
    1743             :              * SQL:2008 15.17 <Execution of referential actions>
    1744             :              *  General rules 9) a) iii):
    1745             :              *      MATCH SIMPLE/FULL
    1746             :              *          ... ON DELETE SET DEFAULT
    1747             :              * ----------
    1748             :              */
    1749             :         case FKCONSTR_MATCH_SIMPLE:
    1750             :         case FKCONSTR_MATCH_FULL:
    1751           8 :             switch (ri_NullCheck(old_row, riinfo, true))
    1752             :             {
    1753             :                 case RI_KEYS_ALL_NULL:
    1754             :                 case RI_KEYS_SOME_NULL:
    1755             : 
    1756             :                     /*
    1757             :                      * No check needed - there cannot be any reference to old
    1758             :                      * key if it contains a NULL
    1759             :                      */
    1760           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1761           0 :                     return PointerGetDatum(NULL);
    1762             : 
    1763             :                 case RI_KEYS_NONE_NULL:
    1764             : 
    1765             :                     /*
    1766             :                      * Have a full qualified key - continue below
    1767             :                      */
    1768           8 :                     break;
    1769             :             }
    1770             : 
    1771           8 :             if (SPI_connect() != SPI_OK_CONNECT)
    1772           0 :                 elog(ERROR, "SPI_connect failed");
    1773             : 
    1774             :             /*
    1775             :              * Fetch or prepare a saved plan for the set default delete
    1776             :              * operation
    1777             :              */
    1778           8 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DEL_DOUPDATE);
    1779             : 
    1780           8 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1781             :             {
    1782             :                 StringInfoData querybuf;
    1783             :                 StringInfoData qualbuf;
    1784             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1785             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1786             :                 char        paramname[16];
    1787             :                 const char *querysep;
    1788             :                 const char *qualsep;
    1789             :                 Oid         queryoids[RI_MAX_NUMKEYS];
    1790             :                 int         i;
    1791             : 
    1792             :                 /* ----------
    1793             :                  * The query string built is
    1794             :                  *  UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
    1795             :                  *          WHERE $1 = fkatt1 [AND ...]
    1796             :                  * The type id's for the $ parameters are those of the
    1797             :                  * corresponding PK attributes.
    1798             :                  * ----------
    1799             :                  */
    1800           4 :                 initStringInfo(&querybuf);
    1801           4 :                 initStringInfo(&qualbuf);
    1802           4 :                 quoteRelationName(fkrelname, fk_rel);
    1803           4 :                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
    1804           4 :                 querysep = "";
    1805           4 :                 qualsep = "WHERE";
    1806          11 :                 for (i = 0; i < riinfo->nkeys; i++)
    1807             :                 {
    1808           7 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    1809           7 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    1810             : 
    1811           7 :                     quoteOneName(attname,
    1812           7 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    1813           7 :                     appendStringInfo(&querybuf,
    1814             :                                      "%s %s = DEFAULT",
    1815             :                                      querysep, attname);
    1816           7 :                     sprintf(paramname, "$%d", i + 1);
    1817           7 :                     ri_GenerateQual(&qualbuf, qualsep,
    1818             :                                     paramname, pk_type,
    1819             :                                     riinfo->pf_eq_oprs[i],
    1820             :                                     attname, fk_type);
    1821           7 :                     querysep = ",";
    1822           7 :                     qualsep = "AND";
    1823           7 :                     queryoids[i] = pk_type;
    1824             :                 }
    1825           4 :                 appendStringInfoString(&querybuf, qualbuf.data);
    1826             : 
    1827             :                 /* Prepare and save the plan */
    1828           4 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
    1829             :                                      &qkey, fk_rel, pk_rel, true);
    1830             :             }
    1831             : 
    1832             :             /*
    1833             :              * We have a plan now. Run it to update the existing references.
    1834             :              */
    1835           8 :             ri_PerformCheck(riinfo, &qkey, qplan,
    1836             :                             fk_rel, pk_rel,
    1837             :                             old_row, NULL,
    1838             :                             true,   /* must detect new rows */
    1839             :                             SPI_OK_UPDATE);
    1840             : 
    1841           8 :             if (SPI_finish() != SPI_OK_FINISH)
    1842           0 :                 elog(ERROR, "SPI_finish failed");
    1843             : 
    1844           8 :             heap_close(fk_rel, RowExclusiveLock);
    1845             : 
    1846             :             /*
    1847             :              * If we just deleted the PK row whose key was equal to the FK
    1848             :              * columns' default values, and a referencing row exists in the FK
    1849             :              * table, we would have updated that row to the same values it
    1850             :              * already had --- and RI_FKey_fk_upd_check_required would hence
    1851             :              * believe no check is necessary.  So we need to do another lookup
    1852             :              * now and in case a reference still exists, abort the operation.
    1853             :              * That is already implemented in the NO ACTION trigger, so just
    1854             :              * run it.  (This recheck is only needed in the SET DEFAULT case,
    1855             :              * since CASCADE would remove such rows, while SET NULL is certain
    1856             :              * to result in rows that satisfy the FK constraint.)
    1857             :              */
    1858           8 :             RI_FKey_noaction_del(fcinfo);
    1859             : 
    1860           6 :             return PointerGetDatum(NULL);
    1861             : 
    1862             :             /*
    1863             :              * Handle MATCH PARTIAL set default delete.
    1864             :              */
    1865             :         case FKCONSTR_MATCH_PARTIAL:
    1866           0 :             ereport(ERROR,
    1867             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1868             :                      errmsg("MATCH PARTIAL not yet implemented")));
    1869             :             return PointerGetDatum(NULL);
    1870             : 
    1871             :         default:
    1872           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    1873             :                  riinfo->confmatchtype);
    1874             :             break;
    1875             :     }
    1876             : 
    1877             :     /* Never reached */
    1878             :     return PointerGetDatum(NULL);
    1879             : }
    1880             : 
    1881             : 
    1882             : /* ----------
    1883             :  * RI_FKey_setdefault_upd -
    1884             :  *
    1885             :  *  Set foreign key references to defaults at update event on PK table.
    1886             :  * ----------
    1887             :  */
    1888             : Datum
    1889           5 : RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
    1890             : {
    1891           5 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    1892             :     const RI_ConstraintInfo *riinfo;
    1893             :     Relation    fk_rel;
    1894             :     Relation    pk_rel;
    1895             :     HeapTuple   new_row;
    1896             :     HeapTuple   old_row;
    1897             :     RI_QueryKey qkey;
    1898             :     SPIPlanPtr  qplan;
    1899             : 
    1900             :     /*
    1901             :      * Check that this is a valid trigger call on the right time and event.
    1902             :      */
    1903           5 :     ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
    1904             : 
    1905             :     /*
    1906             :      * Get arguments.
    1907             :      */
    1908           5 :     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
    1909             :                                     trigdata->tg_relation, true);
    1910             : 
    1911             :     /*
    1912             :      * Get the relation descriptors of the FK and PK tables and the old tuple.
    1913             :      *
    1914             :      * fk_rel is opened in RowExclusiveLock mode since that's what our
    1915             :      * eventual UPDATE will get on it.
    1916             :      */
    1917           5 :     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
    1918           5 :     pk_rel = trigdata->tg_relation;
    1919           5 :     new_row = trigdata->tg_newtuple;
    1920           5 :     old_row = trigdata->tg_trigtuple;
    1921             : 
    1922           5 :     switch (riinfo->confmatchtype)
    1923             :     {
    1924             :             /* ----------
    1925             :              * SQL:2008 15.17 <Execution of referential actions>
    1926             :              *  General rules 10) a) iii):
    1927             :              *      MATCH SIMPLE/FULL
    1928             :              *          ... ON UPDATE SET DEFAULT
    1929             :              * ----------
    1930             :              */
    1931             :         case FKCONSTR_MATCH_SIMPLE:
    1932             :         case FKCONSTR_MATCH_FULL:
    1933           5 :             switch (ri_NullCheck(old_row, riinfo, true))
    1934             :             {
    1935             :                 case RI_KEYS_ALL_NULL:
    1936             :                 case RI_KEYS_SOME_NULL:
    1937             : 
    1938             :                     /*
    1939             :                      * No check needed - there cannot be any reference to old
    1940             :                      * key if it contains a NULL
    1941             :                      */
    1942           0 :                     heap_close(fk_rel, RowExclusiveLock);
    1943           0 :                     return PointerGetDatum(NULL);
    1944             : 
    1945             :                 case RI_KEYS_NONE_NULL:
    1946             : 
    1947             :                     /*
    1948             :                      * Have a full qualified key - continue below
    1949             :                      */
    1950           5 :                     break;
    1951             :             }
    1952             : 
    1953             :             /*
    1954             :              * No need to do anything if old and new keys are equal
    1955             :              */
    1956           5 :             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
    1957             :             {
    1958           0 :                 heap_close(fk_rel, RowExclusiveLock);
    1959           0 :                 return PointerGetDatum(NULL);
    1960             :             }
    1961             : 
    1962           5 :             if (SPI_connect() != SPI_OK_CONNECT)
    1963           0 :                 elog(ERROR, "SPI_connect failed");
    1964             : 
    1965             :             /*
    1966             :              * Fetch or prepare a saved plan for the set default update
    1967             :              * operation
    1968             :              */
    1969           5 :             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_UPD_DOUPDATE);
    1970             : 
    1971           5 :             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
    1972             :             {
    1973             :                 StringInfoData querybuf;
    1974             :                 StringInfoData qualbuf;
    1975             :                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    1976             :                 char        attname[MAX_QUOTED_NAME_LEN];
    1977             :                 char        paramname[16];
    1978             :                 const char *querysep;
    1979             :                 const char *qualsep;
    1980             :                 Oid         queryoids[RI_MAX_NUMKEYS];
    1981             :                 int         i;
    1982             : 
    1983             :                 /* ----------
    1984             :                  * The query string built is
    1985             :                  *  UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
    1986             :                  *          WHERE $1 = fkatt1 [AND ...]
    1987             :                  * The type id's for the $ parameters are those of the
    1988             :                  * corresponding PK attributes.
    1989             :                  * ----------
    1990             :                  */
    1991           2 :                 initStringInfo(&querybuf);
    1992           2 :                 initStringInfo(&qualbuf);
    1993           2 :                 quoteRelationName(fkrelname, fk_rel);
    1994           2 :                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
    1995           2 :                 querysep = "";
    1996           2 :                 qualsep = "WHERE";
    1997           7 :                 for (i = 0; i < riinfo->nkeys; i++)
    1998             :                 {
    1999           5 :                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    2000           5 :                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    2001             : 
    2002           5 :                     quoteOneName(attname,
    2003           5 :                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
    2004           5 :                     appendStringInfo(&querybuf,
    2005             :                                      "%s %s = DEFAULT",
    2006             :                                      querysep, attname);
    2007           5 :                     sprintf(paramname, "$%d", i + 1);
    2008           5 :                     ri_GenerateQual(&qualbuf, qualsep,
    2009             :                                     paramname, pk_type,
    2010             :                                     riinfo->pf_eq_oprs[i],
    2011             :                                     attname, fk_type);
    2012           5 :                     querysep = ",";
    2013           5 :                     qualsep = "AND";
    2014           5 :                     queryoids[i] = pk_type;
    2015             :                 }
    2016           2 :                 appendStringInfoString(&querybuf, qualbuf.data);
    2017             : 
    2018             :                 /* Prepare and save the plan */
    2019           2 :                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
    2020             :                                      &qkey, fk_rel, pk_rel, true);
    2021             :             }
    2022             : 
    2023             :             /*
    2024             :              * We have a plan now. Run it to update the existing references.
    2025             :              */
    2026           5 :             ri_PerformCheck(riinfo, &qkey, qplan,
    2027             :                             fk_rel, pk_rel,
    2028             :                             old_row, NULL,
    2029             :                             true,   /* must detect new rows */
    2030             :                             SPI_OK_UPDATE);
    2031             : 
    2032           5 :             if (SPI_finish() != SPI_OK_FINISH)
    2033           0 :                 elog(ERROR, "SPI_finish failed");
    2034             : 
    2035           5 :             heap_close(fk_rel, RowExclusiveLock);
    2036             : 
    2037             :             /*
    2038             :              * If we just updated the PK row whose key was equal to the FK
    2039             :              * columns' default values, and a referencing row exists in the FK
    2040             :              * table, we would have updated that row to the same values it
    2041             :              * already had --- and RI_FKey_fk_upd_check_required would hence
    2042             :              * believe no check is necessary.  So we need to do another lookup
    2043             :              * now and in case a reference still exists, abort the operation.
    2044             :              * That is already implemented in the NO ACTION trigger, so just
    2045             :              * run it.  (This recheck is only needed in the SET DEFAULT case,
    2046             :              * since CASCADE must change the FK key values, while SET NULL is
    2047             :              * certain to result in rows that satisfy the FK constraint.)
    2048             :              */
    2049           5 :             RI_FKey_noaction_upd(fcinfo);
    2050             : 
    2051           5 :             return PointerGetDatum(NULL);
    2052             : 
    2053             :             /*
    2054             :              * Handle MATCH PARTIAL set default update.
    2055             :              */
    2056             :         case FKCONSTR_MATCH_PARTIAL:
    2057           0 :             ereport(ERROR,
    2058             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2059             :                      errmsg("MATCH PARTIAL not yet implemented")));
    2060             :             return PointerGetDatum(NULL);
    2061             : 
    2062             :         default:
    2063           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    2064             :                  riinfo->confmatchtype);
    2065             :             break;
    2066             :     }
    2067             : 
    2068             :     /* Never reached */
    2069             :     return PointerGetDatum(NULL);
    2070             : }
    2071             : 
    2072             : 
    2073             : /* ----------
    2074             :  * RI_FKey_pk_upd_check_required -
    2075             :  *
    2076             :  *  Check if we really need to fire the RI trigger for an update to a PK
    2077             :  *  relation.  This is called by the AFTER trigger queue manager to see if
    2078             :  *  it can skip queuing an instance of an RI trigger.  Returns TRUE if the
    2079             :  *  trigger must be fired, FALSE if we can prove the constraint will still
    2080             :  *  be satisfied.
    2081             :  * ----------
    2082             :  */
    2083             : bool
    2084          56 : RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
    2085             :                               HeapTuple old_row, HeapTuple new_row)
    2086             : {
    2087             :     const RI_ConstraintInfo *riinfo;
    2088             : 
    2089             :     /*
    2090             :      * Get arguments.
    2091             :      */
    2092          56 :     riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
    2093             : 
    2094          56 :     switch (riinfo->confmatchtype)
    2095             :     {
    2096             :         case FKCONSTR_MATCH_SIMPLE:
    2097             :         case FKCONSTR_MATCH_FULL:
    2098             : 
    2099             :             /*
    2100             :              * If any old key value is NULL, the row could not have been
    2101             :              * referenced by an FK row, so no check is needed.
    2102             :              */
    2103          56 :             if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
    2104           0 :                 return false;
    2105             : 
    2106             :             /* If all old and new key values are equal, no check is needed */
    2107          56 :             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
    2108          11 :                 return false;
    2109             : 
    2110             :             /* Else we need to fire the trigger. */
    2111          45 :             return true;
    2112             : 
    2113             :             /* Handle MATCH PARTIAL check. */
    2114             :         case FKCONSTR_MATCH_PARTIAL:
    2115           0 :             ereport(ERROR,
    2116             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2117             :                      errmsg("MATCH PARTIAL not yet implemented")));
    2118             :             break;
    2119             : 
    2120             :         default:
    2121           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    2122             :                  riinfo->confmatchtype);
    2123             :             break;
    2124             :     }
    2125             : 
    2126             :     /* Never reached */
    2127             :     return false;
    2128             : }
    2129             : 
    2130             : /* ----------
    2131             :  * RI_FKey_fk_upd_check_required -
    2132             :  *
    2133             :  *  Check if we really need to fire the RI trigger for an update to an FK
    2134             :  *  relation.  This is called by the AFTER trigger queue manager to see if
    2135             :  *  it can skip queuing an instance of an RI trigger.  Returns TRUE if the
    2136             :  *  trigger must be fired, FALSE if we can prove the constraint will still
    2137             :  *  be satisfied.
    2138             :  * ----------
    2139             :  */
    2140             : bool
    2141          77 : RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
    2142             :                               HeapTuple old_row, HeapTuple new_row)
    2143             : {
    2144             :     const RI_ConstraintInfo *riinfo;
    2145             : 
    2146             :     /*
    2147             :      * Get arguments.
    2148             :      */
    2149          77 :     riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
    2150             : 
    2151          77 :     switch (riinfo->confmatchtype)
    2152             :     {
    2153             :         case FKCONSTR_MATCH_SIMPLE:
    2154             : 
    2155             :             /*
    2156             :              * If any new key value is NULL, the row must satisfy the
    2157             :              * constraint, so no check is needed.
    2158             :              */
    2159          72 :             if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
    2160          20 :                 return false;
    2161             : 
    2162             :             /*
    2163             :              * If the original row was inserted by our own transaction, we
    2164             :              * must fire the trigger whether or not the keys are equal.  This
    2165             :              * is because our UPDATE will invalidate the INSERT so that the
    2166             :              * INSERT RI trigger will not do anything; so we had better do the
    2167             :              * UPDATE check.  (We could skip this if we knew the INSERT
    2168             :              * trigger already fired, but there is no easy way to know that.)
    2169             :              */
    2170          52 :             if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
    2171          15 :                 return true;
    2172             : 
    2173             :             /* If all old and new key values are equal, no check is needed */
    2174          37 :             if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
    2175          20 :                 return false;
    2176             : 
    2177             :             /* Else we need to fire the trigger. */
    2178          17 :             return true;
    2179             : 
    2180             :         case FKCONSTR_MATCH_FULL:
    2181             : 
    2182             :             /*
    2183             :              * If all new key values are NULL, the row must satisfy the
    2184             :              * constraint, so no check is needed.  On the other hand, if only
    2185             :              * some of them are NULL, the row must fail the constraint.  We
    2186             :              * must not throw error here, because the row might get
    2187             :              * invalidated before the constraint is to be checked, but we
    2188             :              * should queue the event to apply the check later.
    2189             :              */
    2190           5 :             switch (ri_NullCheck(new_row, riinfo, false))
    2191             :             {
    2192             :                 case RI_KEYS_ALL_NULL:
    2193           2 :                     return false;
    2194             :                 case RI_KEYS_SOME_NULL:
    2195           0 :                     return true;
    2196             :                 case RI_KEYS_NONE_NULL:
    2197           3 :                     break;      /* continue with the check */
    2198             :             }
    2199             : 
    2200             :             /*
    2201             :              * If the original row was inserted by our own transaction, we
    2202             :              * must fire the trigger whether or not the keys are equal.  This
    2203             :              * is because our UPDATE will invalidate the INSERT so that the
    2204             :              * INSERT RI trigger will not do anything; so we had better do the
    2205             :              * UPDATE check.  (We could skip this if we knew the INSERT
    2206             :              * trigger already fired, but there is no easy way to know that.)
    2207             :              */
    2208           3 :             if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
    2209           0 :                 return true;
    2210             : 
    2211             :             /* If all old and new key values are equal, no check is needed */
    2212           3 :             if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
    2213           0 :                 return false;
    2214             : 
    2215             :             /* Else we need to fire the trigger. */
    2216           3 :             return true;
    2217             : 
    2218             :             /* Handle MATCH PARTIAL check. */
    2219             :         case FKCONSTR_MATCH_PARTIAL:
    2220           0 :             ereport(ERROR,
    2221             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2222             :                      errmsg("MATCH PARTIAL not yet implemented")));
    2223             :             break;
    2224             : 
    2225             :         default:
    2226           0 :             elog(ERROR, "unrecognized confmatchtype: %d",
    2227             :                  riinfo->confmatchtype);
    2228             :             break;
    2229             :     }
    2230             : 
    2231             :     /* Never reached */
    2232             :     return false;
    2233             : }
    2234             : 
    2235             : /* ----------
    2236             :  * RI_Initial_Check -
    2237             :  *
    2238             :  *  Check an entire table for non-matching values using a single query.
    2239             :  *  This is not a trigger procedure, but is called during ALTER TABLE
    2240             :  *  ADD FOREIGN KEY to validate the initial table contents.
    2241             :  *
    2242             :  *  We expect that the caller has made provision to prevent any problems
    2243             :  *  caused by concurrent actions. This could be either by locking rel and
    2244             :  *  pkrel at ShareRowExclusiveLock or higher, or by otherwise ensuring
    2245             :  *  that triggers implementing the checks are already active.
    2246             :  *  Hence, we do not need to lock individual rows for the check.
    2247             :  *
    2248             :  *  If the check fails because the current user doesn't have permissions
    2249             :  *  to read both tables, return false to let our caller know that they will
    2250             :  *  need to do something else to check the constraint.
    2251             :  * ----------
    2252             :  */
    2253             : bool
    2254          26 : RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
    2255             : {
    2256             :     const RI_ConstraintInfo *riinfo;
    2257             :     StringInfoData querybuf;
    2258             :     char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
    2259             :     char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
    2260             :     char        pkattname[MAX_QUOTED_NAME_LEN + 3];
    2261             :     char        fkattname[MAX_QUOTED_NAME_LEN + 3];
    2262             :     RangeTblEntry *pkrte;
    2263             :     RangeTblEntry *fkrte;
    2264             :     const char *sep;
    2265             :     int         i;
    2266             :     int         save_nestlevel;
    2267             :     char        workmembuf[32];
    2268             :     int         spi_result;
    2269             :     SPIPlanPtr  qplan;
    2270             : 
    2271             :     /* Fetch constraint info. */
    2272          26 :     riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
    2273             : 
    2274             :     /*
    2275             :      * Check to make sure current user has enough permissions to do the test
    2276             :      * query.  (If not, caller can fall back to the trigger method, which
    2277             :      * works because it changes user IDs on the fly.)
    2278             :      *
    2279             :      * XXX are there any other show-stopper conditions to check?
    2280             :      */
    2281          26 :     pkrte = makeNode(RangeTblEntry);
    2282          26 :     pkrte->rtekind = RTE_RELATION;
    2283          26 :     pkrte->relid = RelationGetRelid(pk_rel);
    2284          26 :     pkrte->relkind = pk_rel->rd_rel->relkind;
    2285          26 :     pkrte->requiredPerms = ACL_SELECT;
    2286             : 
    2287          26 :     fkrte = makeNode(RangeTblEntry);
    2288          26 :     fkrte->rtekind = RTE_RELATION;
    2289          26 :     fkrte->relid = RelationGetRelid(fk_rel);
    2290          26 :     fkrte->relkind = fk_rel->rd_rel->relkind;
    2291          26 :     fkrte->requiredPerms = ACL_SELECT;
    2292             : 
    2293          61 :     for (i = 0; i < riinfo->nkeys; i++)
    2294             :     {
    2295             :         int         attno;
    2296             : 
    2297          35 :         attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
    2298          35 :         pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
    2299             : 
    2300          35 :         attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
    2301          35 :         fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
    2302             :     }
    2303             : 
    2304          26 :     if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
    2305           0 :         return false;
    2306             : 
    2307             :     /*
    2308             :      * Also punt if RLS is enabled on either table unless this role has the
    2309             :      * bypassrls right or is the table owner of the table(s) involved which
    2310             :      * have RLS enabled.
    2311             :      */
    2312          26 :     if (!has_bypassrls_privilege(GetUserId()) &&
    2313           0 :         ((pk_rel->rd_rel->relrowsecurity &&
    2314           0 :           !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
    2315           0 :          (fk_rel->rd_rel->relrowsecurity &&
    2316           0 :           !pg_class_ownercheck(fkrte->relid, GetUserId()))))
    2317           0 :         return false;
    2318             : 
    2319             :     /*----------
    2320             :      * The query string built is:
    2321             :      *  SELECT fk.keycols FROM ONLY relname fk
    2322             :      *   LEFT OUTER JOIN ONLY pkrelname pk
    2323             :      *   ON (pk.pkkeycol1=fk.keycol1 [AND ...])
    2324             :      *   WHERE pk.pkkeycol1 IS NULL AND
    2325             :      * For MATCH SIMPLE:
    2326             :      *   (fk.keycol1 IS NOT NULL [AND ...])
    2327             :      * For MATCH FULL:
    2328             :      *   (fk.keycol1 IS NOT NULL [OR ...])
    2329             :      *
    2330             :      * We attach COLLATE clauses to the operators when comparing columns
    2331             :      * that have different collations.
    2332             :      *----------
    2333             :      */
    2334          26 :     initStringInfo(&querybuf);
    2335          26 :     appendStringInfoString(&querybuf, "SELECT ");
    2336          26 :     sep = "";
    2337          61 :     for (i = 0; i < riinfo->nkeys; i++)
    2338             :     {
    2339          35 :         quoteOneName(fkattname,
    2340          35 :                      RIAttName(fk_rel, riinfo->fk_attnums[i]));
    2341          35 :         appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
    2342          35 :         sep = ", ";
    2343             :     }
    2344             : 
    2345          26 :     quoteRelationName(pkrelname, pk_rel);
    2346          26 :     quoteRelationName(fkrelname, fk_rel);
    2347          26 :     appendStringInfo(&querybuf,
    2348             :                      " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
    2349             :                      fkrelname, pkrelname);
    2350             : 
    2351          26 :     strcpy(pkattname, "pk.");
    2352          26 :     strcpy(fkattname, "fk.");
    2353          26 :     sep = "(";
    2354          61 :     for (i = 0; i < riinfo->nkeys; i++)
    2355             :     {
    2356          35 :         Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
    2357          35 :         Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
    2358          35 :         Oid         pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
    2359          35 :         Oid         fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
    2360             : 
    2361          35 :         quoteOneName(pkattname + 3,
    2362          35 :                      RIAttName(pk_rel, riinfo->pk_attnums[i]));
    2363          35 :         quoteOneName(fkattname + 3,
    2364          35 :                      RIAttName(fk_rel, riinfo->fk_attnums[i]));
    2365          35 :         ri_GenerateQual(&querybuf, sep,
    2366             :                         pkattname, pk_type,
    2367             :                         riinfo->pf_eq_oprs[i],
    2368             :                         fkattname, fk_type);
    2369          35 :         if (pk_coll != fk_coll)
    2370           2 :             ri_GenerateQualCollation(&querybuf, pk_coll);
    2371          35 :         sep = "AND";
    2372             :     }
    2373             : 
    2374             :     /*
    2375             :      * It's sufficient to test any one pk attribute for null to detect a join
    2376             :      * failure.
    2377             :      */
    2378          26 :     quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
    2379          26 :     appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
    2380             : 
    2381          26 :     sep = "";
    2382          61 :     for (i = 0; i < riinfo->nkeys; i++)
    2383             :     {
    2384          35 :         quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
    2385          35 :         appendStringInfo(&querybuf,
    2386             :                          "%sfk.%s IS NOT NULL",
    2387             :                          sep, fkattname);
    2388          35 :         switch (riinfo->confmatchtype)
    2389             :         {
    2390             :             case FKCONSTR_MATCH_SIMPLE:
    2391          27 :                 sep = " AND ";
    2392          27 :                 break;
    2393             :             case FKCONSTR_MATCH_FULL:
    2394           8 :                 sep = " OR ";
    2395           8 :                 break;
    2396             :             case FKCONSTR_MATCH_PARTIAL:
    2397           0 :                 ereport(ERROR,
    2398             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2399             :                          errmsg("MATCH PARTIAL not yet implemented")));
    2400             :                 break;
    2401             :             default:
    2402           0 :                 elog(ERROR, "unrecognized confmatchtype: %d",
    2403             :                      riinfo->confmatchtype);
    2404             :                 break;
    2405             :         }
    2406             :     }
    2407          26 :     appendStringInfoChar(&querybuf, ')');
    2408             : 
    2409             :     /*
    2410             :      * Temporarily increase work_mem so that the check query can be executed
    2411             :      * more efficiently.  It seems okay to do this because the query is simple
    2412             :      * enough to not use a multiple of work_mem, and one typically would not
    2413             :      * have many large foreign-key validations happening concurrently.  So
    2414             :      * this seems to meet the criteria for being considered a "maintenance"
    2415             :      * operation, and accordingly we use maintenance_work_mem.
    2416             :      *
    2417             :      * We use the equivalent of a function SET option to allow the setting to
    2418             :      * persist for exactly the duration of the check query.  guc.c also takes
    2419             :      * care of undoing the setting on error.
    2420             :      */
    2421          26 :     save_nestlevel = NewGUCNestLevel();
    2422             : 
    2423          26 :     snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
    2424          26 :     (void) set_config_option("work_mem", workmembuf,
    2425             :                              PGC_USERSET, PGC_S_SESSION,
    2426             :                              GUC_ACTION_SAVE, true, 0, false);
    2427             : 
    2428          26 :     if (SPI_connect() != SPI_OK_CONNECT)
    2429           0 :         elog(ERROR, "SPI_connect failed");
    2430             : 
    2431             :     /*
    2432             :      * Generate the plan.  We don't need to cache it, and there are no
    2433             :      * arguments to the plan.
    2434             :      */
    2435          26 :     qplan = SPI_prepare(querybuf.data, 0, NULL);
    2436             : 
    2437          26 :     if (qplan == NULL)
    2438           0 :         elog(ERROR, "SPI_prepare returned %d for %s",
    2439             :              SPI_result, querybuf.data);
    2440             : 
    2441             :     /*
    2442             :      * Run the plan.  For safety we force a current snapshot to be used. (In
    2443             :      * transaction-snapshot mode, this arguably violates transaction isolation
    2444             :      * rules, but we really haven't got much choice.) We don't need to
    2445             :      * register the snapshot, because SPI_execute_snapshot will see to it. We
    2446             :      * need at most one tuple returned, so pass limit = 1.
    2447             :      */
    2448          26 :     spi_result = SPI_execute_snapshot(qplan,
    2449             :                                       NULL, NULL,
    2450             :                                       GetLatestSnapshot(),
    2451             :                                       InvalidSnapshot,
    2452             :                                       true, false, 1);
    2453             : 
    2454             :     /* Check result */
    2455          26 :     if (spi_result != SPI_OK_SELECT)
    2456           0 :         elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
    2457             : 
    2458             :     /* Did we find a tuple violating the constraint? */
    2459          26 :     if (SPI_processed > 0)
    2460             :     {
    2461           3 :         HeapTuple   tuple = SPI_tuptable->vals[0];
    2462           3 :         TupleDesc   tupdesc = SPI_tuptable->tupdesc;
    2463             :         RI_ConstraintInfo fake_riinfo;
    2464             : 
    2465             :         /*
    2466             :          * The columns to look at in the result tuple are 1..N, not whatever
    2467             :          * they are in the fk_rel.  Hack up riinfo so that the subroutines
    2468             :          * called here will behave properly.
    2469             :          *
    2470             :          * In addition to this, we have to pass the correct tupdesc to
    2471             :          * ri_ReportViolation, overriding its normal habit of using the pk_rel
    2472             :          * or fk_rel's tupdesc.
    2473             :          */
    2474           3 :         memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
    2475           6 :         for (i = 0; i < fake_riinfo.nkeys; i++)
    2476           3 :             fake_riinfo.fk_attnums[i] = i + 1;
    2477             : 
    2478             :         /*
    2479             :          * If it's MATCH FULL, and there are any nulls in the FK keys,
    2480             :          * complain about that rather than the lack of a match.  MATCH FULL
    2481             :          * disallows partially-null FK rows.
    2482             :          */
    2483           5 :         if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
    2484           2 :             ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
    2485           0 :             ereport(ERROR,
    2486             :                     (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
    2487             :                      errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
    2488             :                             RelationGetRelationName(fk_rel),
    2489             :                             NameStr(fake_riinfo.conname)),
    2490             :                      errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
    2491             :                      errtableconstraint(fk_rel,
    2492             :                                         NameStr(fake_riinfo.conname))));
    2493             : 
    2494             :         /*
    2495             :          * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
    2496             :          * query, which isn't true, but will cause it to use
    2497             :          * fake_riinfo.fk_attnums as we need.
    2498             :          */
    2499           3 :         ri_ReportViolation(&fake_riinfo,
    2500             :                            pk_rel, fk_rel,
    2501             :                            tuple, tupdesc,
    2502             :                            RI_PLAN_CHECK_LOOKUPPK, false);
    2503             :     }
    2504             : 
    2505          23 :     if (SPI_finish() != SPI_OK_FINISH)
    2506           0 :         elog(ERROR, "SPI_finish failed");
    2507             : 
    2508             :     /*
    2509             :      * Restore work_mem.
    2510             :      */
    2511          23 :     AtEOXact_GUC(true, save_nestlevel);
    2512             : 
    2513          23 :     return true;
    2514             : }
    2515             : 
    2516             : 
    2517             : /* ----------
    2518             :  * Local functions below
    2519             :  * ----------
    2520             :  */
    2521             : 
    2522             : 
    2523             : /*
    2524             :  * quoteOneName --- safely quote a single SQL name
    2525             :  *
    2526             :  * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
    2527             :  */
    2528             : static void
    2529         706 : quoteOneName(char *buffer, const char *name)
    2530             : {
    2531             :     /* Rather than trying to be smart, just always quote it. */
    2532         706 :     *buffer++ = '"';
    2533        5527 :     while (*name)
    2534             :     {
    2535        4115 :         if (*name == '"')
    2536           0 :             *buffer++ = '"';
    2537        4115 :         *buffer++ = *name++;
    2538             :     }
    2539         706 :     *buffer++ = '"';
    2540         706 :     *buffer = '\0';
    2541         706 : }
    2542             : 
    2543             : /*
    2544             :  * quoteRelationName --- safely quote a fully qualified relation name
    2545             :  *
    2546             :  * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
    2547             :  */
    2548             : static void
    2549         177 : quoteRelationName(char *buffer, Relation rel)
    2550             : {
    2551         177 :     quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
    2552         177 :     buffer += strlen(buffer);
    2553         177 :     *buffer++ = '.';
    2554         177 :     quoteOneName(buffer, RelationGetRelationName(rel));
    2555         177 : }
    2556             : 
    2557             : /*
    2558             :  * ri_GenerateQual --- generate a WHERE clause equating two variables
    2559             :  *
    2560             :  * The idea is to append " sep leftop op rightop" to buf.  The complexity
    2561             :  * comes from needing to be sure that the parser will select the desired
    2562             :  * operator.  We always name the operator using OPERATOR(schema.op) syntax
    2563             :  * (readability isn't a big priority here), so as to avoid search-path
    2564             :  * uncertainties.  We have to emit casts too, if either input isn't already
    2565             :  * the input type of the operator; else we are at the mercy of the parser's
    2566             :  * heuristics for ambiguous-operator resolution.
    2567             :  */
    2568             : static void
    2569         217 : ri_GenerateQual(StringInfo buf,
    2570             :                 const char *sep,
    2571             :                 const char *leftop, Oid leftoptype,
    2572             :                 Oid opoid,
    2573             :                 const char *rightop, Oid rightoptype)
    2574             : {
    2575             :     HeapTuple   opertup;
    2576             :     Form_pg_operator operform;
    2577             :     char       *oprname;
    2578             :     char       *nspname;
    2579             : 
    2580         217 :     opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
    2581         217 :     if (!HeapTupleIsValid(opertup))
    2582           0 :         elog(ERROR, "cache lookup failed for operator %u", opoid);
    2583         217 :     operform = (Form_pg_operator) GETSTRUCT(opertup);
    2584         217 :     Assert(operform->oprkind == 'b');
    2585         217 :     oprname = NameStr(operform->oprname);
    2586             : 
    2587         217 :     nspname = get_namespace_name(operform->oprnamespace);
    2588             : 
    2589         217 :     appendStringInfo(buf, " %s %s", sep, leftop);
    2590         217 :     if (leftoptype != operform->oprleft)
    2591           7 :         ri_add_cast_to(buf, operform->oprleft);
    2592         217 :     appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
    2593         217 :     appendStringInfoString(buf, oprname);
    2594         217 :     appendStringInfo(buf, ") %s", rightop);
    2595         217 :     if (rightoptype != operform->oprright)
    2596          10 :         ri_add_cast_to(buf, operform->oprright);
    2597             : 
    2598         217 :     ReleaseSysCache(opertup);
    2599         217 : }
    2600             : 
    2601             : /*
    2602             :  * Add a cast specification to buf.  We spell out the type name the hard way,
    2603             :  * intentionally not using format_type_be().  This is to avoid corner cases
    2604             :  * for CHARACTER, BIT, and perhaps other types, where specifying the type
    2605             :  * using SQL-standard syntax results in undesirable data truncation.  By
    2606             :  * doing it this way we can be certain that the cast will have default (-1)
    2607             :  * target typmod.
    2608             :  */
    2609             : static void
    2610          17 : ri_add_cast_to(StringInfo buf, Oid typid)
    2611             : {
    2612             :     HeapTuple   typetup;
    2613             :     Form_pg_type typform;
    2614             :     char       *typname;
    2615             :     char       *nspname;
    2616             : 
    2617          17 :     typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    2618          17 :     if (!HeapTupleIsValid(typetup))
    2619           0 :         elog(ERROR, "cache lookup failed for type %u", typid);
    2620          17 :     typform = (Form_pg_type) GETSTRUCT(typetup);
    2621             : 
    2622          17 :     typname = NameStr(typform->typname);
    2623          17 :     nspname = get_namespace_name(typform->typnamespace);
    2624             : 
    2625          17 :     appendStringInfo(buf, "::%s.%s",
    2626             :                      quote_identifier(nspname), quote_identifier(typname));
    2627             : 
    2628          17 :     ReleaseSysCache(typetup);
    2629          17 : }
    2630             : 
    2631             : /*
    2632             :  * ri_GenerateQualCollation --- add a COLLATE spec to a WHERE clause
    2633             :  *
    2634             :  * At present, we intentionally do not use this function for RI queries that
    2635             :  * compare a variable to a $n parameter.  Since parameter symbols always have
    2636             :  * default collation, the effect will be to use the variable's collation.
    2637             :  * Now that is only strictly correct when testing the referenced column, since
    2638             :  * the SQL standard specifies that RI comparisons should use the referenced
    2639             :  * column's collation.  However, so long as all collations have the same
    2640             :  * notion of equality (which they do, because texteq reduces to bitwise
    2641             :  * equality), there's no visible semantic impact from using the referencing
    2642             :  * column's collation when testing it, and this is a good thing to do because
    2643             :  * it lets us use a normal index on the referencing column.  However, we do
    2644             :  * have to use this function when directly comparing the referencing and
    2645             :  * referenced columns, if they are of different collations; else the parser
    2646             :  * will fail to resolve the collation to use.
    2647             :  */
    2648             : static void
    2649           2 : ri_GenerateQualCollation(StringInfo buf, Oid collation)
    2650             : {
    2651             :     HeapTuple   tp;
    2652             :     Form_pg_collation colltup;
    2653             :     char       *collname;
    2654             :     char        onename[MAX_QUOTED_NAME_LEN];
    2655             : 
    2656             :     /* Nothing to do if it's a noncollatable data type */
    2657           2 :     if (!OidIsValid(collation))
    2658           2 :         return;
    2659             : 
    2660           2 :     tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
    2661           2 :     if (!HeapTupleIsValid(tp))
    2662           0 :         elog(ERROR, "cache lookup failed for collation %u", collation);
    2663           2 :     colltup = (Form_pg_collation) GETSTRUCT(tp);
    2664           2 :     collname = NameStr(colltup->collname);
    2665             : 
    2666             :     /*
    2667             :      * We qualify the name always, for simplicity and to ensure the query is
    2668             :      * not search-path-dependent.
    2669             :      */
    2670           2 :     quoteOneName(onename, get_namespace_name(colltup->collnamespace));
    2671           2 :     appendStringInfo(buf, " COLLATE %s", onename);
    2672           2 :     quoteOneName(onename, collname);
    2673           2 :     appendStringInfo(buf, ".%s", onename);
    2674             : 
    2675           2 :     ReleaseSysCache(tp);
    2676             : }
    2677             : 
    2678             : /* ----------
    2679             :  * ri_BuildQueryKey -
    2680             :  *
    2681             :  *  Construct a hashtable key for a prepared SPI plan of an FK constraint.
    2682             :  *
    2683             :  *      key: output argument, *key is filled in based on the other arguments
    2684             :  *      riinfo: info from pg_constraint entry
    2685             :  *      constr_queryno: an internal number identifying the query type
    2686             :  *          (see RI_PLAN_XXX constants at head of file)
    2687             :  * ----------
    2688             :  */
    2689             : static void
    2690         387 : ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
    2691             :                  int32 constr_queryno)
    2692             : {
    2693             :     /*
    2694             :      * We assume struct RI_QueryKey contains no padding bytes, else we'd need
    2695             :      * to use memset to clear them.
    2696             :      */
    2697         387 :     key->constr_id = riinfo->constraint_id;
    2698         387 :     key->constr_queryno = constr_queryno;
    2699         387 : }
    2700             : 
    2701             : /*
    2702             :  * Check that RI trigger function was called in expected context
    2703             :  */
    2704             : static void
    2705         380 : ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
    2706             : {
    2707         380 :     TriggerData *trigdata = (TriggerData *) fcinfo->context;
    2708             : 
    2709         380 :     if (!CALLED_AS_TRIGGER(fcinfo))
    2710           0 :         ereport(ERROR,
    2711             :                 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    2712             :                  errmsg("function \"%s\" was not called by trigger manager", funcname)));
    2713             : 
    2714             :     /*
    2715             :      * Check proper event
    2716             :      */
    2717         760 :     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
    2718         380 :         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
    2719           0 :         ereport(ERROR,
    2720             :                 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    2721             :                  errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
    2722             : 
    2723         380 :     switch (tgkind)
    2724             :     {
    2725             :         case RI_TRIGTYPE_INSERT:
    2726         248 :             if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
    2727           0 :                 ereport(ERROR,
    2728             :                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    2729             :                          errmsg("function \"%s\" must be fired for INSERT", funcname)));
    2730         248 :             break;
    2731             :         case RI_TRIGTYPE_UPDATE:
    2732          82 :             if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
    2733           0 :                 ereport(ERROR,
    2734             :                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    2735             :                          errmsg("function \"%s\" must be fired for UPDATE", funcname)));
    2736          82 :             break;
    2737             :         case RI_TRIGTYPE_DELETE:
    2738          50 :             if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
    2739           0 :                 ereport(ERROR,
    2740             :                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
    2741             :                          errmsg("function \"%s\" must be fired for DELETE", funcname)));
    2742          50 :             break;
    2743             :     }
    2744         380 : }
    2745             : 
    2746             : 
    2747             : /*
    2748             :  * Fetch the RI_ConstraintInfo struct for the trigger's FK constraint.
    2749             :  */
    2750             : static const RI_ConstraintInfo *
    2751         539 : ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
    2752             : {
    2753         539 :     Oid         constraintOid = trigger->tgconstraint;
    2754             :     const RI_ConstraintInfo *riinfo;
    2755             : 
    2756             :     /*
    2757             :      * Check that the FK constraint's OID is available; it might not be if
    2758             :      * we've been invoked via an ordinary trigger or an old-style "constraint
    2759             :      * trigger".
    2760             :      */
    2761         539 :     if (!OidIsValid(constraintOid))
    2762           0 :         ereport(ERROR,
    2763             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    2764             :                  errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
    2765             :                         trigger->tgname, RelationGetRelationName(trig_rel)),
    2766             :                  errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
    2767             : 
    2768             :     /* Find or create a hashtable entry for the constraint */
    2769         539 :     riinfo = ri_LoadConstraintInfo(constraintOid);
    2770             : 
    2771             :     /* Do some easy cross-checks against the trigger call data */
    2772         539 :     if (rel_is_pk)
    2773             :     {
    2774         308 :         if (riinfo->fk_relid != trigger->tgconstrrelid ||
    2775         154 :             riinfo->pk_relid != RelationGetRelid(trig_rel))
    2776           0 :             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
    2777             :                  trigger->tgname, RelationGetRelationName(trig_rel));
    2778             :     }
    2779             :     else
    2780             :     {
    2781         770 :         if (riinfo->fk_relid != RelationGetRelid(trig_rel) ||
    2782         385 :             riinfo->pk_relid != trigger->tgconstrrelid)
    2783           0 :             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
    2784             :                  trigger->tgname, RelationGetRelationName(trig_rel));
    2785             :     }
    2786             : 
    2787         539 :     return riinfo;
    2788             : }
    2789             : 
    2790             : /*
    2791             :  * Fetch or create the RI_ConstraintInfo struct for an FK constraint.
    2792             :  */
    2793             : static const RI_ConstraintInfo *
    2794         539 : ri_LoadConstraintInfo(Oid constraintOid)
    2795             : {
    2796             :     RI_ConstraintInfo *riinfo;
    2797             :     bool        found;
    2798             :     HeapTuple   tup;
    2799             :     Form_pg_constraint conForm;
    2800             :     Datum       adatum;
    2801             :     bool        isNull;
    2802             :     ArrayType  *arr;
    2803             :     int         numkeys;
    2804             : 
    2805             :     /*
    2806             :      * On the first call initialize the hashtable
    2807             :      */
    2808         539 :     if (!ri_constraint_cache)
    2809          14 :         ri_InitHashTables();
    2810             : 
    2811             :     /*
    2812             :      * Find or create a hash entry.  If we find a valid one, just return it.
    2813             :      */
    2814         539 :     riinfo = (RI_ConstraintInfo *) hash_search(ri_constraint_cache,
    2815             :                                                (void *) &constraintOid,
    2816             :                                                HASH_ENTER, &found);
    2817         539 :     if (!found)
    2818          75 :         riinfo->valid = false;
    2819         464 :     else if (riinfo->valid)
    2820         460 :         return riinfo;
    2821             : 
    2822             :     /*
    2823             :      * Fetch the pg_constraint row so we can fill in the entry.
    2824             :      */
    2825          79 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    2826          79 :     if (!HeapTupleIsValid(tup)) /* should not happen */
    2827           0 :         elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
    2828          79 :     conForm = (Form_pg_constraint) GETSTRUCT(tup);
    2829             : 
    2830          79 :     if (conForm->contype != CONSTRAINT_FOREIGN) /* should not happen */
    2831           0 :         elog(ERROR, "constraint %u is not a foreign key constraint",
    2832             :              constraintOid);
    2833             : 
    2834             :     /* And extract data */
    2835          79 :     Assert(riinfo->constraint_id == constraintOid);
    2836          79 :     riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID,
    2837             :                                                  ObjectIdGetDatum(constraintOid));
    2838          79 :     memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
    2839          79 :     riinfo->pk_relid = conForm->confrelid;
    2840          79 :     riinfo->fk_relid = conForm->conrelid;
    2841          79 :     riinfo->confupdtype = conForm->confupdtype;
    2842          79 :     riinfo->confdeltype = conForm->confdeltype;
    2843          79 :     riinfo->confmatchtype = conForm->confmatchtype;
    2844             : 
    2845             :     /*
    2846             :      * We expect the arrays to be 1-D arrays of the right types; verify that.
    2847             :      * We don't need to use deconstruct_array() since the array data is just
    2848             :      * going to look like a C array of values.
    2849             :      */
    2850          79 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    2851             :                              Anum_pg_constraint_conkey, &isNull);
    2852          79 :     if (isNull)
    2853           0 :         elog(ERROR, "null conkey for constraint %u", constraintOid);
    2854          79 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    2855         158 :     if (ARR_NDIM(arr) != 1 ||
    2856         158 :         ARR_HASNULL(arr) ||
    2857          79 :         ARR_ELEMTYPE(arr) != INT2OID)
    2858           0 :         elog(ERROR, "conkey is not a 1-D smallint array");
    2859          79 :     numkeys = ARR_DIMS(arr)[0];
    2860          79 :     if (numkeys <= 0 || numkeys > RI_MAX_NUMKEYS)
    2861           0 :         elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
    2862          79 :     riinfo->nkeys = numkeys;
    2863          79 :     memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
    2864          79 :     if ((Pointer) arr != DatumGetPointer(adatum))
    2865          79 :         pfree(arr);             /* free de-toasted copy, if any */
    2866             : 
    2867          79 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    2868             :                              Anum_pg_constraint_confkey, &isNull);
    2869          79 :     if (isNull)
    2870           0 :         elog(ERROR, "null confkey for constraint %u", constraintOid);
    2871          79 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    2872         158 :     if (ARR_NDIM(arr) != 1 ||
    2873         158 :         ARR_DIMS(arr)[0] != numkeys ||
    2874         158 :         ARR_HASNULL(arr) ||
    2875          79 :         ARR_ELEMTYPE(arr) != INT2OID)
    2876           0 :         elog(ERROR, "confkey is not a 1-D smallint array");
    2877          79 :     memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
    2878          79 :     if ((Pointer) arr != DatumGetPointer(adatum))
    2879          79 :         pfree(arr);             /* free de-toasted copy, if any */
    2880             : 
    2881          79 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    2882             :                              Anum_pg_constraint_conpfeqop, &isNull);
    2883          79 :     if (isNull)
    2884           0 :         elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
    2885          79 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    2886             :     /* see TryReuseForeignKey if you change the test below */
    2887         158 :     if (ARR_NDIM(arr) != 1 ||
    2888         158 :         ARR_DIMS(arr)[0] != numkeys ||
    2889         158 :         ARR_HASNULL(arr) ||
    2890          79 :         ARR_ELEMTYPE(arr) != OIDOID)
    2891           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
    2892          79 :     memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    2893          79 :     if ((Pointer) arr != DatumGetPointer(adatum))
    2894          79 :         pfree(arr);             /* free de-toasted copy, if any */
    2895             : 
    2896          79 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    2897             :                              Anum_pg_constraint_conppeqop, &isNull);
    2898          79 :     if (isNull)
    2899           0 :         elog(ERROR, "null conppeqop for constraint %u", constraintOid);
    2900          79 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    2901         158 :     if (ARR_NDIM(arr) != 1 ||
    2902         158 :         ARR_DIMS(arr)[0] != numkeys ||
    2903         158 :         ARR_HASNULL(arr) ||
    2904          79 :         ARR_ELEMTYPE(arr) != OIDOID)
    2905           0 :         elog(ERROR, "conppeqop is not a 1-D Oid array");
    2906          79 :     memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    2907          79 :     if ((Pointer) arr != DatumGetPointer(adatum))
    2908          79 :         pfree(arr);             /* free de-toasted copy, if any */
    2909             : 
    2910          79 :     adatum = SysCacheGetAttr(CONSTROID, tup,
    2911             :                              Anum_pg_constraint_conffeqop, &isNull);
    2912          79 :     if (isNull)
    2913           0 :         elog(ERROR, "null conffeqop for constraint %u", constraintOid);
    2914          79 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
    2915         158 :     if (ARR_NDIM(arr) != 1 ||
    2916         158 :         ARR_DIMS(arr)[0] != numkeys ||
    2917         158 :         ARR_HASNULL(arr) ||
    2918          79 :         ARR_ELEMTYPE(arr) != OIDOID)
    2919           0 :         elog(ERROR, "conffeqop is not a 1-D Oid array");
    2920          79 :     memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
    2921          79 :     if ((Pointer) arr != DatumGetPointer(adatum))
    2922          79 :         pfree(arr);             /* free de-toasted copy, if any */
    2923             : 
    2924          79 :     ReleaseSysCache(tup);
    2925             : 
    2926             :     /*
    2927             :      * For efficient processing of invalidation messages below, we keep a
    2928             :      * doubly-linked list, and a count, of all currently valid entries.
    2929             :      */
    2930          79 :     dlist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link);
    2931          79 :     ri_constraint_cache_valid_count++;
    2932             : 
    2933          79 :     riinfo->valid = true;
    2934             : 
    2935          79 :     return riinfo;
    2936             : }
    2937             : 
    2938             : /*
    2939             :  * Callback for pg_constraint inval events
    2940             :  *
    2941             :  * While most syscache callbacks just flush all their entries, pg_constraint
    2942             :  * gets enough update traffic that it's probably worth being smarter.
    2943             :  * Invalidate any ri_constraint_cache entry associated with the syscache
    2944             :  * entry with the specified hash value, or all entries if hashvalue == 0.
    2945             :  *
    2946             :  * Note: at the time a cache invalidation message is processed there may be
    2947             :  * active references to the cache.  Because of this we never remove entries
    2948             :  * from the cache, but only mark them invalid, which is harmless to active
    2949             :  * uses.  (Any query using an entry should hold a lock sufficient to keep that
    2950             :  * data from changing under it --- but we may get cache flushes anyway.)
    2951             :  */
    2952             : static void
    2953        1807 : InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
    2954             : {
    2955             :     dlist_mutable_iter iter;
    2956             : 
    2957        1807 :     Assert(ri_constraint_cache != NULL);
    2958             : 
    2959             :     /*
    2960             :      * If the list of currently valid entries gets excessively large, we mark
    2961             :      * them all invalid so we can empty the list.  This arrangement avoids
    2962             :      * O(N^2) behavior in situations where a session touches many foreign keys
    2963             :      * and also does many ALTER TABLEs, such as a restore from pg_dump.
    2964             :      */
    2965        1807 :     if (ri_constraint_cache_valid_count > 1000)
    2966           0 :         hashvalue = 0;          /* pretend it's a cache reset */
    2967             : 
    2968        2680 :     dlist_foreach_modify(iter, &ri_constraint_cache_valid_list)
    2969             :     {
    2970         873 :         RI_ConstraintInfo *riinfo = dlist_container(RI_ConstraintInfo,
    2971             :                                                     valid_link, iter.cur);
    2972             : 
    2973         873 :         if (hashvalue == 0 || riinfo->oidHashValue == hashvalue)
    2974             :         {
    2975          75 :             riinfo->valid = false;
    2976             :             /* Remove invalidated entries from the list, too */
    2977          75 :             dlist_delete(iter.cur);
    2978          75 :             ri_constraint_cache_valid_count--;
    2979             :         }
    2980             :     }
    2981        1807 : }
    2982             : 
    2983             : 
    2984             : /*
    2985             :  * Prepare execution plan for a query to enforce an RI restriction
    2986             :  *
    2987             :  * If cache_plan is true, the plan is saved into our plan hashtable
    2988             :  * so that we don't need to plan it again.
    2989             :  */
    2990             : static SPIPlanPtr
    2991         125 : ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
    2992             :              RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
    2993             :              bool cache_plan)
    2994             : {
    2995             :     SPIPlanPtr  qplan;
    2996             :     Relation    query_rel;
    2997             :     Oid         save_userid;
    2998             :     int         save_sec_context;
    2999             : 
    3000             :     /*
    3001             :      * Use the query type code to determine whether the query is run against
    3002             :      * the PK or FK table; we'll do the check as that table's owner
    3003             :      */
    3004         125 :     if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
    3005          78 :         query_rel = pk_rel;
    3006             :     else
    3007          47 :         query_rel = fk_rel;
    3008             : 
    3009             :     /* Switch to proper UID to perform check as */
    3010         125 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
    3011         125 :     SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
    3012             :                            save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
    3013             :                            SECURITY_NOFORCE_RLS);
    3014             : 
    3015             :     /* Create the plan */
    3016         125 :     qplan = SPI_prepare(querystr, nargs, argtypes);
    3017             : 
    3018         125 :     if (qplan == NULL)
    3019           0 :         elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
    3020             : 
    3021             :     /* Restore UID and security context */
    3022         125 :     SetUserIdAndSecContext(save_userid, save_sec_context);
    3023             : 
    3024             :     /* Save the plan if requested */
    3025         125 :     if (cache_plan)
    3026             :     {
    3027         125 :         SPI_keepplan(qplan);
    3028         125 :         ri_HashPreparedPlan(qkey, qplan);
    3029             :     }
    3030             : 
    3031         125 :     return qplan;
    3032             : }
    3033             : 
    3034             : /*
    3035             :  * Perform a query to enforce an RI restriction
    3036             :  */
    3037             : static bool
    3038         387 : ri_PerformCheck(const RI_ConstraintInfo *riinfo,
    3039             :                 RI_QueryKey *qkey, SPIPlanPtr qplan,
    3040             :                 Relation fk_rel, Relation pk_rel,
    3041             :                 HeapTuple old_tuple, HeapTuple new_tuple,
    3042             :                 bool detectNewRows, int expect_OK)
    3043             : {
    3044             :     Relation    query_rel,
    3045             :                 source_rel;
    3046             :     bool        source_is_pk;
    3047             :     Snapshot    test_snapshot;
    3048             :     Snapshot    crosscheck_snapshot;
    3049             :     int         limit;
    3050             :     int         spi_result;
    3051             :     Oid         save_userid;
    3052             :     int         save_sec_context;
    3053             :     Datum       vals[RI_MAX_NUMKEYS * 2];
    3054             :     char        nulls[RI_MAX_NUMKEYS * 2];
    3055             : 
    3056             :     /*
    3057             :      * Use the query type code to determine whether the query is run against
    3058             :      * the PK or FK table; we'll do the check as that table's owner
    3059             :      */
    3060         387 :     if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
    3061         292 :         query_rel = pk_rel;
    3062             :     else
    3063          95 :         query_rel = fk_rel;
    3064             : 
    3065             :     /*
    3066             :      * The values for the query are taken from the table on which the trigger
    3067             :      * is called - it is normally the other one with respect to query_rel. An
    3068             :      * exception is ri_Check_Pk_Match(), which uses the PK table for both (and
    3069             :      * sets queryno to RI_PLAN_CHECK_LOOKUPPK_FROM_PK).  We might eventually
    3070             :      * need some less klugy way to determine this.
    3071             :      */
    3072         387 :     if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
    3073             :     {
    3074         239 :         source_rel = fk_rel;
    3075         239 :         source_is_pk = false;
    3076             :     }
    3077             :     else
    3078             :     {
    3079         148 :         source_rel = pk_rel;
    3080         148 :         source_is_pk = true;
    3081             :     }
    3082             : 
    3083             :     /* Extract the parameters to be passed into the query */
    3084         387 :     if (new_tuple)
    3085             :     {
    3086         249 :         ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
    3087             :                          vals, nulls);
    3088         249 :         if (old_tuple)
    3089          30 :             ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
    3090          30 :                              vals + riinfo->nkeys, nulls + riinfo->nkeys);
    3091             :     }
    3092             :     else
    3093             :     {
    3094         138 :         ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
    3095             :                          vals, nulls);
    3096             :     }
    3097             : 
    3098             :     /*
    3099             :      * In READ COMMITTED mode, we just need to use an up-to-date regular
    3100             :      * snapshot, and we will see all rows that could be interesting. But in
    3101             :      * transaction-snapshot mode, we can't change the transaction snapshot. If
    3102             :      * the caller passes detectNewRows == false then it's okay to do the query
    3103             :      * with the transaction snapshot; otherwise we use a current snapshot, and
    3104             :      * tell the executor to error out if it finds any rows under the current
    3105             :      * snapshot that wouldn't be visible per the transaction snapshot.  Note
    3106             :      * that SPI_execute_snapshot will register the snapshots, so we don't need
    3107             :      * to bother here.
    3108             :      */
    3109         387 :     if (IsolationUsesXactSnapshot() && detectNewRows)
    3110             :     {
    3111           0 :         CommandCounterIncrement();  /* be sure all my own work is visible */
    3112           0 :         test_snapshot = GetLatestSnapshot();
    3113           0 :         crosscheck_snapshot = GetTransactionSnapshot();
    3114             :     }
    3115             :     else
    3116             :     {
    3117             :         /* the default SPI behavior is okay */
    3118         387 :         test_snapshot = InvalidSnapshot;
    3119         387 :         crosscheck_snapshot = InvalidSnapshot;
    3120             :     }
    3121             : 
    3122             :     /*
    3123             :      * If this is a select query (e.g., for a 'no action' or 'restrict'
    3124             :      * trigger), we only need to see if there is a single row in the table,
    3125             :      * matching the key.  Otherwise, limit = 0 - because we want the query to
    3126             :      * affect ALL the matching rows.
    3127             :      */
    3128         387 :     limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
    3129             : 
    3130             :     /* Switch to proper UID to perform check as */
    3131         387 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
    3132         387 :     SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
    3133             :                            save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
    3134             :                            SECURITY_NOFORCE_RLS);
    3135             : 
    3136             :     /* Finally we can run the query. */
    3137         387 :     spi_result = SPI_execute_snapshot(qplan,
    3138             :                                       vals, nulls,
    3139             :                                       test_snapshot, crosscheck_snapshot,
    3140             :                                       false, false, limit);
    3141             : 
    3142             :     /* Restore UID and security context */
    3143         387 :     SetUserIdAndSecContext(save_userid, save_sec_context);
    3144             : 
    3145             :     /* Check result */
    3146         387 :     if (spi_result < 0)
    3147           0 :         elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
    3148             : 
    3149         387 :     if (expect_OK >= 0 && spi_result != expect_OK)
    3150           0 :         ri_ReportViolation(riinfo,
    3151             :                            pk_rel, fk_rel,
    3152             :                            new_tuple ? new_tuple : old_tuple,
    3153             :                            NULL,
    3154             :                            qkey->constr_queryno, true);
    3155             : 
    3156             :     /* XXX wouldn't it be clearer to do this part at the caller? */
    3157         387 :     if (qkey->constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK &&
    3158         292 :         expect_OK == SPI_OK_SELECT &&
    3159         292 :         (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
    3160          62 :         ri_ReportViolation(riinfo,
    3161             :                            pk_rel, fk_rel,
    3162             :                            new_tuple ? new_tuple : old_tuple,
    3163             :                            NULL,
    3164             :                            qkey->constr_queryno, false);
    3165             : 
    3166         325 :     return SPI_processed != 0;
    3167             : }
    3168             : 
    3169             : /*
    3170             :  * Extract fields from a tuple into Datum/nulls arrays
    3171             :  */
    3172             : static void
    3173         397 : ri_ExtractValues(Relation rel, HeapTuple tup,
    3174             :                  const RI_ConstraintInfo *riinfo, bool rel_is_pk,
    3175             :                  Datum *vals, char *nulls)
    3176             : {
    3177         397 :     TupleDesc   tupdesc = rel->rd_att;
    3178             :     const int16 *attnums;
    3179             :     int         i;
    3180             :     bool        isnull;
    3181             : 
    3182         397 :     if (rel_is_pk)
    3183         158 :         attnums = riinfo->pk_attnums;
    3184             :     else
    3185         239 :         attnums = riinfo->fk_attnums;
    3186             : 
    3187         973 :     for (i = 0; i < riinfo->nkeys; i++)
    3188             :     {
    3189         576 :         vals[i] = heap_getattr(tup, attnums[i], tupdesc,
    3190             :                                &isnull);
    3191         576 :         nulls[i] = isnull ? 'n' : ' ';
    3192             :     }
    3193         397 : }
    3194             : 
    3195             : /*
    3196             :  * Produce an error report
    3197             :  *
    3198             :  * If the failed constraint was on insert/update to the FK table,
    3199             :  * we want the key names and values extracted from there, and the error
    3200             :  * message to look like 'key blah is not present in PK'.
    3201             :  * Otherwise, the attr names and values come from the PK table and the
    3202             :  * message looks like 'key blah is still referenced from FK'.
    3203             :  */
    3204             : static void
    3205          65 : ri_ReportViolation(const RI_ConstraintInfo *riinfo,
    3206             :                    Relation pk_rel, Relation fk_rel,
    3207             :                    HeapTuple violator, TupleDesc tupdesc,
    3208             :                    int queryno, bool spi_err)
    3209             : {
    3210             :     StringInfoData key_names;
    3211             :     StringInfoData key_values;
    3212             :     bool        onfk;
    3213             :     const int16 *attnums;
    3214             :     int         idx;
    3215             :     Oid         rel_oid;
    3216             :     AclResult   aclresult;
    3217          65 :     bool        has_perm = true;
    3218             : 
    3219          65 :     if (spi_err)
    3220           0 :         ereport(ERROR,
    3221             :                 (errcode(ERRCODE_INTERNAL_ERROR),
    3222             :                  errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
    3223             :                         RelationGetRelationName(pk_rel),
    3224             :                         NameStr(riinfo->conname),
    3225             :                         RelationGetRelationName(fk_rel)),
    3226             :                  errhint("This is most likely due to a rule having rewritten the query.")));
    3227             : 
    3228             :     /*
    3229             :      * Determine which relation to complain about.  If tupdesc wasn't passed
    3230             :      * by caller, assume the violator tuple came from there.
    3231             :      */
    3232          65 :     onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
    3233          65 :     if (onfk)
    3234             :     {
    3235          47 :         attnums = riinfo->fk_attnums;
    3236          47 :         rel_oid = fk_rel->rd_id;
    3237          47 :         if (tupdesc == NULL)
    3238          44 :             tupdesc = fk_rel->rd_att;
    3239             :     }
    3240             :     else
    3241             :     {
    3242          18 :         attnums = riinfo->pk_attnums;
    3243          18 :         rel_oid = pk_rel->rd_id;
    3244          18 :         if (tupdesc == NULL)
    3245          18 :             tupdesc = pk_rel->rd_att;
    3246             :     }
    3247             : 
    3248             :     /*
    3249             :      * Check permissions- if the user does not have access to view the data in
    3250             :      * any of the key columns then we don't include the errdetail() below.
    3251             :      *
    3252             :      * Check if RLS is enabled on the relation first.  If so, we don't return
    3253             :      * any specifics to avoid leaking data.
    3254             :      *
    3255             :      * Check table-level permissions next and, failing that, column-level
    3256             :      * privileges.
    3257             :      */
    3258             : 
    3259          65 :     if (check_enable_rls(rel_oid, InvalidOid, true) != RLS_ENABLED)
    3260             :     {
    3261          64 :         aclresult = pg_class_aclcheck(rel_oid, GetUserId(), ACL_SELECT);
    3262          64 :         if (aclresult != ACLCHECK_OK)
    3263             :         {
    3264             :             /* Try for column-level permissions */
    3265           0 :             for (idx = 0; idx < riinfo->nkeys; idx++)
    3266             :             {
    3267           0 :                 aclresult = pg_attribute_aclcheck(rel_oid, attnums[idx],
    3268             :                                                   GetUserId(),
    3269             :                                                   ACL_SELECT);
    3270             : 
    3271             :                 /* No access to the key */
    3272           0 :                 if (aclresult != ACLCHECK_OK)
    3273             :                 {
    3274           0 :                     has_perm = false;
    3275           0 :                     break;
    3276             :                 }
    3277             :             }
    3278             :         }
    3279             :     }
    3280             :     else
    3281           1 :         has_perm = false;
    3282             : 
    3283          65 :     if (has_perm)
    3284             :     {
    3285             :         /* Get printable versions of the keys involved */
    3286          64 :         initStringInfo(&key_names);
    3287          64 :         initStringInfo(&key_values);
    3288         158 :         for (idx = 0; idx < riinfo->nkeys; idx++)
    3289             :         {
    3290          94 :             int         fnum = attnums[idx];
    3291             :             char       *name,
    3292             :                        *val;
    3293             : 
    3294          94 :             name = SPI_fname(tupdesc, fnum);
    3295          94 :             val = SPI_getvalue(violator, tupdesc, fnum);
    3296          94 :             if (!val)
    3297           0 :                 val = "null";
    3298             : 
    3299          94 :             if (idx > 0)
    3300             :             {
    3301          30 :                 appendStringInfoString(&key_names, ", ");
    3302          30 :                 appendStringInfoString(&key_values, ", ");
    3303             :             }
    3304          94 :             appendStringInfoString(&key_names, name);
    3305          94 :             appendStringInfoString(&key_values, val);
    3306             :         }
    3307             :     }
    3308             : 
    3309          65 :     if (onfk)
    3310          47 :         ereport(ERROR,
    3311             :                 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
    3312             :                  errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
    3313             :                         RelationGetRelationName(fk_rel),
    3314             :                         NameStr(riinfo->conname)),
    3315             :                  has_perm ?
    3316             :                  errdetail("Key (%s)=(%s) is not present in table \"%s\".",
    3317             :                            key_names.data, key_values.data,
    3318             :                            RelationGetRelationName(pk_rel)) :
    3319             :                  errdetail("Key is not present in table \"%s\".",
    3320             :                            RelationGetRelationName(pk_rel)),
    3321             :                  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
    3322             :     else
    3323          18 :         ereport(ERROR,
    3324             :                 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
    3325             :                  errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
    3326             :                         RelationGetRelationName(pk_rel),
    3327             :                         NameStr(riinfo->conname),
    3328             :                         RelationGetRelationName(fk_rel)),
    3329             :                  has_perm ?
    3330             :                  errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
    3331             :                            key_names.data, key_values.data,
    3332             :                            RelationGetRelationName(fk_rel)) :
    3333             :                  errdetail("Key is still referenced from table \"%s\".",
    3334             :                            RelationGetRelationName(fk_rel)),
    3335             :                  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
    3336             : }
    3337             : 
    3338             : 
    3339             : /* ----------
    3340             :  * ri_NullCheck -
    3341             :  *
    3342             :  *  Determine the NULL state of all key values in a tuple
    3343             :  *
    3344             :  *  Returns one of RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL or RI_KEYS_SOME_NULL.
    3345             :  * ----------
    3346             :  */
    3347             : static int
    3348         558 : ri_NullCheck(HeapTuple tup,
    3349             :              const RI_ConstraintInfo *riinfo, bool rel_is_pk)
    3350             : {
    3351             :     const int16 *attnums;
    3352             :     int         i;
    3353         558 :     bool        allnull = true;
    3354         558 :     bool        nonenull = true;
    3355             : 
    3356         558 :     if (rel_is_pk)
    3357         207 :         attnums = riinfo->pk_attnums;
    3358             :     else
    3359         351 :         attnums = riinfo->fk_attnums;
    3360             : 
    3361        1382 :     for (i = 0; i < riinfo->nkeys; i++)
    3362             :     {
    3363         824 :         if (heap_attisnull(tup, attnums[i]))
    3364          64 :             nonenull = false;
    3365             :         else
    3366         760 :             allnull = false;
    3367             :     }
    3368             : 
    3369         558 :     if (allnull)
    3370          34 :         return RI_KEYS_ALL_NULL;
    3371             : 
    3372         524 :     if (nonenull)
    3373         503 :         return RI_KEYS_NONE_NULL;
    3374             : 
    3375          21 :     return RI_KEYS_SOME_NULL;
    3376             : }
    3377             : 
    3378             : 
    3379             : /* ----------
    3380             :  * ri_InitHashTables -
    3381             :  *
    3382             :  *  Initialize our internal hash tables.
    3383             :  * ----------
    3384             :  */
    3385             : static void
    3386          14 : ri_InitHashTables(void)
    3387             : {
    3388             :     HASHCTL     ctl;
    3389             : 
    3390          14 :     memset(&ctl, 0, sizeof(ctl));
    3391          14 :     ctl.keysize = sizeof(Oid);
    3392          14 :     ctl.entrysize = sizeof(RI_ConstraintInfo);
    3393          14 :     ri_constraint_cache = hash_create("RI constraint cache",
    3394             :                                       RI_INIT_CONSTRAINTHASHSIZE,
    3395             :                                       &ctl, HASH_ELEM | HASH_BLOBS);
    3396             : 
    3397             :     /* Arrange to flush cache on pg_constraint changes */
    3398          14 :     CacheRegisterSyscacheCallback(CONSTROID,
    3399             :                                   InvalidateConstraintCacheCallBack,
    3400             :                                   (Datum) 0);
    3401             : 
    3402          14 :     memset(&ctl, 0, sizeof(ctl));
    3403          14 :     ctl.keysize = sizeof(RI_QueryKey);
    3404          14 :     ctl.entrysize = sizeof(RI_QueryHashEntry);
    3405          14 :     ri_query_cache = hash_create("RI query cache",
    3406             :                                  RI_INIT_QUERYHASHSIZE,
    3407             :                                  &ctl, HASH_ELEM | HASH_BLOBS);
    3408             : 
    3409          14 :     memset(&ctl, 0, sizeof(ctl));
    3410          14 :     ctl.keysize = sizeof(RI_CompareKey);
    3411          14 :     ctl.entrysize = sizeof(RI_CompareHashEntry);
    3412          14 :     ri_compare_cache = hash_create("RI compare cache",
    3413             :                                    RI_INIT_QUERYHASHSIZE,
    3414             :                                    &ctl, HASH_ELEM | HASH_BLOBS);
    3415          14 : }
    3416             : 
    3417             : 
    3418             : /* ----------
    3419             :  * ri_FetchPreparedPlan -
    3420             :  *
    3421             :  *  Lookup for a query key in our private hash table of prepared
    3422             :  *  and saved SPI execution plans. Return the plan if found or NULL.
    3423             :  * ----------
    3424             :  */
    3425             : static SPIPlanPtr
    3426         387 : ri_FetchPreparedPlan(RI_QueryKey *key)
    3427             : {
    3428             :     RI_QueryHashEntry *entry;
    3429             :     SPIPlanPtr  plan;
    3430             : 
    3431             :     /*
    3432             :      * On the first call initialize the hashtable
    3433             :      */
    3434         387 :     if (!ri_query_cache)
    3435           0 :         ri_InitHashTables();
    3436             : 
    3437             :     /*
    3438             :      * Lookup for the key
    3439             :      */
    3440         387 :     entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
    3441             :                                               (void *) key,
    3442             :                                               HASH_FIND, NULL);
    3443         387 :     if (entry == NULL)
    3444         111 :         return NULL;
    3445             : 
    3446             :     /*
    3447             :      * Check whether the plan is still valid.  If it isn't, we don't want to
    3448             :      * simply rely on plancache.c to regenerate it; rather we should start
    3449             :      * from scratch and rebuild the query text too.  This is to cover cases
    3450             :      * such as table/column renames.  We depend on the plancache machinery to
    3451             :      * detect possible invalidations, though.
    3452             :      *
    3453             :      * CAUTION: this check is only trustworthy if the caller has already
    3454             :      * locked both FK and PK rels.
    3455             :      */
    3456         276 :     plan = entry->plan;
    3457         276 :     if (plan && SPI_plan_is_valid(plan))
    3458         262 :         return plan;
    3459             : 
    3460             :     /*
    3461             :      * Otherwise we might as well flush the cached plan now, to free a little
    3462             :      * memory space before we make a new one.
    3463             :      */
    3464          14 :     entry->plan = NULL;
    3465          14 :     if (plan)
    3466          14 :         SPI_freeplan(plan);
    3467             : 
    3468          14 :     return NULL;
    3469             : }
    3470             : 
    3471             : 
    3472             : /* ----------
    3473             :  * ri_HashPreparedPlan -
    3474             :  *
    3475             :  *  Add another plan to our private SPI query plan hashtable.
    3476             :  * ----------
    3477             :  */
    3478             : static void
    3479         125 : ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
    3480             : {
    3481             :     RI_QueryHashEntry *entry;
    3482             :     bool        found;
    3483             : 
    3484             :     /*
    3485             :      * On the first call initialize the hashtable
    3486             :      */
    3487         125 :     if (!ri_query_cache)
    3488           0 :         ri_InitHashTables();
    3489             : 
    3490             :     /*
    3491             :      * Add the new plan.  We might be overwriting an entry previously found
    3492             :      * invalid by ri_FetchPreparedPlan.
    3493             :      */
    3494         125 :     entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
    3495             :                                               (void *) key,
    3496             :                                               HASH_ENTER, &found);
    3497         125 :     Assert(!found || entry->plan == NULL);
    3498         125 :     entry->plan = plan;
    3499         125 : }
    3500             : 
    3501             : 
    3502             : /* ----------
    3503             :  * ri_KeysEqual -
    3504             :  *
    3505             :  *  Check if all key values in OLD and NEW are equal.
    3506             :  *
    3507             :  *  Note: at some point we might wish to redefine this as checking for
    3508             :  *  "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be
    3509             :  *  considered equal.  Currently there is no need since all callers have
    3510             :  *  previously found at least one of the rows to contain no nulls.
    3511             :  * ----------
    3512             :  */
    3513             : static bool
    3514         144 : ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
    3515             :              const RI_ConstraintInfo *riinfo, bool rel_is_pk)
    3516             : {
    3517         144 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    3518             :     const int16 *attnums;
    3519             :     const Oid  *eq_oprs;
    3520             :     int         i;
    3521             : 
    3522         144 :     if (rel_is_pk)
    3523             :     {
    3524         104 :         attnums = riinfo->pk_attnums;
    3525         104 :         eq_oprs = riinfo->pp_eq_oprs;
    3526             :     }
    3527             :     else
    3528             :     {
    3529          40 :         attnums = riinfo->fk_attnums;
    3530          40 :         eq_oprs = riinfo->ff_eq_oprs;
    3531             :     }
    3532             : 
    3533         398 :     for (i = 0; i < riinfo->nkeys; i++)
    3534             :     {
    3535             :         Datum       oldvalue;
    3536             :         Datum       newvalue;
    3537             :         bool        isnull;
    3538             : 
    3539             :         /*
    3540             :          * Get one attribute's oldvalue. If it is NULL - they're not equal.
    3541             :          */
    3542         168 :         oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
    3543         168 :         if (isnull)
    3544         116 :             return false;
    3545             : 
    3546             :         /*
    3547             :          * Get one attribute's newvalue. If it is NULL - they're not equal.
    3548             :          */
    3549         165 :         newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
    3550         165 :         if (isnull)
    3551           0 :             return false;
    3552             : 
    3553             :         /*
    3554             :          * Compare them with the appropriate equality operator.
    3555             :          */
    3556         165 :         if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
    3557             :                                 oldvalue, newvalue))
    3558         110 :             return false;
    3559             :     }
    3560             : 
    3561          31 :     return true;
    3562             : }
    3563             : 
    3564             : 
    3565             : /* ----------
    3566             :  * ri_AttributesEqual -
    3567             :  *
    3568             :  *  Call the appropriate equality comparison operator for two values.
    3569             :  *
    3570             :  *  NB: we have already checked that neither value is null.
    3571             :  * ----------
    3572             :  */
    3573             : static bool
    3574         165 : ri_AttributesEqual(Oid eq_opr, Oid typeid,
    3575             :                    Datum oldvalue, Datum newvalue)
    3576             : {
    3577         165 :     RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
    3578             : 
    3579             :     /* Do we need to cast the values? */
    3580         165 :     if (OidIsValid(entry->cast_func_finfo.fn_oid))
    3581             :     {
    3582           2 :         oldvalue = FunctionCall3(&entry->cast_func_finfo,
    3583             :                                  oldvalue,
    3584             :                                  Int32GetDatum(-1), /* typmod */
    3585             :                                  BoolGetDatum(false));  /* implicit coercion */
    3586           2 :         newvalue = FunctionCall3(&entry->cast_func_finfo,
    3587             :                                  newvalue,
    3588             :                                  Int32GetDatum(-1), /* typmod */
    3589             :                                  BoolGetDatum(false));  /* implicit coercion */
    3590             :     }
    3591             : 
    3592             :     /*
    3593             :      * Apply the comparison operator.  We assume it doesn't care about
    3594             :      * collations.
    3595             :      */
    3596         165 :     return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
    3597             :                                       oldvalue, newvalue));
    3598             : }
    3599             : 
    3600             : /* ----------
    3601             :  * ri_HashCompareOp -
    3602             :  *
    3603             :  *  See if we know how to compare two values, and create a new hash entry
    3604             :  *  if not.
    3605             :  * ----------
    3606             :  */
    3607             : static RI_CompareHashEntry *
    3608         165 : ri_HashCompareOp(Oid eq_opr, Oid typeid)
    3609             : {
    3610             :     RI_CompareKey key;
    3611             :     RI_CompareHashEntry *entry;
    3612             :     bool        found;
    3613             : 
    3614             :     /*
    3615             :      * On the first call initialize the hashtable
    3616             :      */
    3617         165 :     if (!ri_compare_cache)
    3618           0 :         ri_InitHashTables();
    3619             : 
    3620             :     /*
    3621             :      * Find or create a hash entry.  Note we're assuming RI_CompareKey
    3622             :      * contains no struct padding.
    3623             :      */
    3624         165 :     key.eq_opr = eq_opr;
    3625         165 :     key.typeid = typeid;
    3626         165 :     entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
    3627             :                                                 (void *) &key,
    3628             :                                                 HASH_ENTER, &found);
    3629         165 :     if (!found)
    3630           5 :         entry->valid = false;
    3631             : 
    3632             :     /*
    3633             :      * If not already initialized, do so.  Since we'll keep this hash entry
    3634             :      * for the life of the backend, put any subsidiary info for the function
    3635             :      * cache structs into TopMemoryContext.
    3636             :      */
    3637         165 :     if (!entry->valid)
    3638             :     {
    3639             :         Oid         lefttype,
    3640             :                     righttype,
    3641             :                     castfunc;
    3642             :         CoercionPathType pathtype;
    3643             : 
    3644             :         /* We always need to know how to call the equality operator */
    3645           5 :         fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
    3646             :                       TopMemoryContext);
    3647             : 
    3648             :         /*
    3649             :          * If we chose to use a cast from FK to PK type, we may have to apply
    3650             :          * the cast function to get to the operator's input type.
    3651             :          *
    3652             :          * XXX eventually it would be good to support array-coercion cases
    3653             :          * here and in ri_AttributesEqual().  At the moment there is no point
    3654             :          * because cases involving nonidentical array types will be rejected
    3655             :          * at constraint creation time.
    3656             :          *
    3657             :          * XXX perhaps also consider supporting CoerceViaIO?  No need at the
    3658             :          * moment since that will never be generated for implicit coercions.
    3659             :          */
    3660           5 :         op_input_types(eq_opr, &lefttype, &righttype);
    3661           5 :         Assert(lefttype == righttype);
    3662           5 :         if (typeid == lefttype)
    3663           4 :             castfunc = InvalidOid;  /* simplest case */
    3664             :         else
    3665             :         {
    3666           1 :             pathtype = find_coercion_pathway(lefttype, typeid,
    3667             :                                              COERCION_IMPLICIT,
    3668             :                                              &castfunc);
    3669           1 :             if (pathtype != COERCION_PATH_FUNC &&
    3670             :                 pathtype != COERCION_PATH_RELABELTYPE)
    3671             :             {
    3672             :                 /*
    3673             :                  * The declared input type of the eq_opr might be a
    3674             :                  * polymorphic type such as ANYARRAY or ANYENUM, or other
    3675             :                  * special cases such as RECORD; find_coercion_pathway
    3676             :                  * currently doesn't subsume these special cases.
    3677             :                  */
    3678           0 :                 if (!IsBinaryCoercible(typeid, lefttype))
    3679           0 :                     elog(ERROR, "no conversion function from %s to %s",
    3680             :                          format_type_be(typeid),
    3681             :                          format_type_be(lefttype));
    3682             :             }
    3683             :         }
    3684           5 :         if (OidIsValid(castfunc))
    3685           1 :             fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
    3686             :                           TopMemoryContext);
    3687             :         else
    3688           4 :             entry->cast_func_finfo.fn_oid = InvalidOid;
    3689           5 :         entry->valid = true;
    3690             :     }
    3691             : 
    3692         165 :     return entry;
    3693             : }
    3694             : 
    3695             : 
    3696             : /*
    3697             :  * Given a trigger function OID, determine whether it is an RI trigger,
    3698             :  * and if so whether it is attached to PK or FK relation.
    3699             :  */
    3700             : int
    3701         429 : RI_FKey_trigger_type(Oid tgfoid)
    3702             : {
    3703         429 :     switch (tgfoid)
    3704             :     {
    3705             :         case F_RI_FKEY_CASCADE_DEL:
    3706             :         case F_RI_FKEY_CASCADE_UPD:
    3707             :         case F_RI_FKEY_RESTRICT_DEL:
    3708             :         case F_RI_FKEY_RESTRICT_UPD:
    3709             :         case F_RI_FKEY_SETNULL_DEL:
    3710             :         case F_RI_FKEY_SETNULL_UPD:
    3711             :         case F_RI_FKEY_SETDEFAULT_DEL:
    3712             :         case F_RI_FKEY_SETDEFAULT_UPD:
    3713             :         case F_RI_FKEY_NOACTION_DEL:
    3714             :         case F_RI_FKEY_NOACTION_UPD:
    3715          56 :             return RI_TRIGGER_PK;
    3716             : 
    3717             :         case F_RI_FKEY_CHECK_INS:
    3718             :         case F_RI_FKEY_CHECK_UPD:
    3719          77 :             return RI_TRIGGER_FK;
    3720             :     }
    3721             : 
    3722         296 :     return RI_TRIGGER_NONE;
    3723             : }

Generated by: LCOV version 1.11