Header And Logo

PostgreSQL
| The world's most advanced open source database.

ri_triggers.c

Go to the documentation of this file.
00001 /* ----------
00002  * ri_triggers.c
00003  *
00004  *  Generic trigger procedures for referential integrity constraint
00005  *  checks.
00006  *
00007  *  Note about memory management: the private hashtables kept here live
00008  *  across query and transaction boundaries, in fact they live as long as
00009  *  the backend does.  This works because the hashtable structures
00010  *  themselves are allocated by dynahash.c in its permanent DynaHashCxt,
00011  *  and the SPI plans they point to are saved using SPI_keepplan().
00012  *  There is not currently any provision for throwing away a no-longer-needed
00013  *  plan --- consider improving this someday.
00014  *
00015  *
00016  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00017  *
00018  * src/backend/utils/adt/ri_triggers.c
00019  *
00020  * ----------
00021  */
00022 
00023 
00024 /* ----------
00025  * Internal TODO:
00026  *
00027  *      Add MATCH PARTIAL logic.
00028  * ----------
00029  */
00030 
00031 #include "postgres.h"
00032 
00033 #include "access/htup_details.h"
00034 #include "access/sysattr.h"
00035 #include "access/xact.h"
00036 #include "catalog/pg_collation.h"
00037 #include "catalog/pg_constraint.h"
00038 #include "catalog/pg_operator.h"
00039 #include "catalog/pg_type.h"
00040 #include "commands/trigger.h"
00041 #include "executor/executor.h"
00042 #include "executor/spi.h"
00043 #include "parser/parse_coerce.h"
00044 #include "parser/parse_relation.h"
00045 #include "miscadmin.h"
00046 #include "utils/builtins.h"
00047 #include "utils/fmgroids.h"
00048 #include "utils/guc.h"
00049 #include "utils/inval.h"
00050 #include "utils/lsyscache.h"
00051 #include "utils/memutils.h"
00052 #include "utils/rel.h"
00053 #include "utils/snapmgr.h"
00054 #include "utils/syscache.h"
00055 #include "utils/tqual.h"
00056 
00057 
00058 /* ----------
00059  * Local definitions
00060  * ----------
00061  */
00062 
00063 #define RI_MAX_NUMKEYS                  INDEX_MAX_KEYS
00064 
00065 #define RI_INIT_CONSTRAINTHASHSIZE      64
00066 #define RI_INIT_QUERYHASHSIZE           (RI_INIT_CONSTRAINTHASHSIZE * 4)
00067 
00068 #define RI_KEYS_ALL_NULL                0
00069 #define RI_KEYS_SOME_NULL               1
00070 #define RI_KEYS_NONE_NULL               2
00071 
00072 /* RI query type codes */
00073 /* these queries are executed against the PK (referenced) table: */
00074 #define RI_PLAN_CHECK_LOOKUPPK          1
00075 #define RI_PLAN_CHECK_LOOKUPPK_FROM_PK  2
00076 #define RI_PLAN_LAST_ON_PK              RI_PLAN_CHECK_LOOKUPPK_FROM_PK
00077 /* these queries are executed against the FK (referencing) table: */
00078 #define RI_PLAN_CASCADE_DEL_DODELETE    3
00079 #define RI_PLAN_CASCADE_UPD_DOUPDATE    4
00080 #define RI_PLAN_RESTRICT_DEL_CHECKREF   5
00081 #define RI_PLAN_RESTRICT_UPD_CHECKREF   6
00082 #define RI_PLAN_SETNULL_DEL_DOUPDATE    7
00083 #define RI_PLAN_SETNULL_UPD_DOUPDATE    8
00084 #define RI_PLAN_SETDEFAULT_DEL_DOUPDATE 9
00085 #define RI_PLAN_SETDEFAULT_UPD_DOUPDATE 10
00086 
00087 #define MAX_QUOTED_NAME_LEN  (NAMEDATALEN*2+3)
00088 #define MAX_QUOTED_REL_NAME_LEN  (MAX_QUOTED_NAME_LEN*2)
00089 
00090 #define RIAttName(rel, attnum)  NameStr(*attnumAttName(rel, attnum))
00091 #define RIAttType(rel, attnum)  attnumTypeId(rel, attnum)
00092 #define RIAttCollation(rel, attnum) attnumCollationId(rel, attnum)
00093 
00094 #define RI_TRIGTYPE_INSERT 1
00095 #define RI_TRIGTYPE_UPDATE 2
00096 #define RI_TRIGTYPE_DELETE 3
00097 
00098 
00099 /* ----------
00100  * RI_ConstraintInfo
00101  *
00102  *  Information extracted from an FK pg_constraint entry.  This is cached in
00103  *  ri_constraint_cache.
00104  * ----------
00105  */
00106 typedef struct RI_ConstraintInfo
00107 {
00108     Oid         constraint_id;  /* OID of pg_constraint entry (hash key) */
00109     bool        valid;          /* successfully initialized? */
00110     uint32      oidHashValue;   /* hash value of pg_constraint OID */
00111     NameData    conname;        /* name of the FK constraint */
00112     Oid         pk_relid;       /* referenced relation */
00113     Oid         fk_relid;       /* referencing relation */
00114     char        confupdtype;    /* foreign key's ON UPDATE action */
00115     char        confdeltype;    /* foreign key's ON DELETE action */
00116     char        confmatchtype;  /* foreign key's match type */
00117     int         nkeys;          /* number of key columns */
00118     int16       pk_attnums[RI_MAX_NUMKEYS];     /* attnums of referenced cols */
00119     int16       fk_attnums[RI_MAX_NUMKEYS];     /* attnums of referencing cols */
00120     Oid         pf_eq_oprs[RI_MAX_NUMKEYS];     /* equality operators (PK =
00121                                                  * FK) */
00122     Oid         pp_eq_oprs[RI_MAX_NUMKEYS];     /* equality operators (PK =
00123                                                  * PK) */
00124     Oid         ff_eq_oprs[RI_MAX_NUMKEYS];     /* equality operators (FK =
00125                                                  * FK) */
00126 } RI_ConstraintInfo;
00127 
00128 
00129 /* ----------
00130  * RI_QueryKey
00131  *
00132  *  The key identifying a prepared SPI plan in our query hashtable
00133  * ----------
00134  */
00135 typedef struct RI_QueryKey
00136 {
00137     Oid         constr_id;      /* OID of pg_constraint entry */
00138     int32       constr_queryno; /* query type ID, see RI_PLAN_XXX above */
00139 } RI_QueryKey;
00140 
00141 
00142 /* ----------
00143  * RI_QueryHashEntry
00144  * ----------
00145  */
00146 typedef struct RI_QueryHashEntry
00147 {
00148     RI_QueryKey key;
00149     SPIPlanPtr  plan;
00150 } RI_QueryHashEntry;
00151 
00152 
00153 /* ----------
00154  * RI_CompareKey
00155  *
00156  *  The key identifying an entry showing how to compare two values
00157  * ----------
00158  */
00159 typedef struct RI_CompareKey
00160 {
00161     Oid         eq_opr;         /* the equality operator to apply */
00162     Oid         typeid;         /* the data type to apply it to */
00163 } RI_CompareKey;
00164 
00165 
00166 /* ----------
00167  * RI_CompareHashEntry
00168  * ----------
00169  */
00170 typedef struct RI_CompareHashEntry
00171 {
00172     RI_CompareKey key;
00173     bool        valid;          /* successfully initialized? */
00174     FmgrInfo    eq_opr_finfo;   /* call info for equality fn */
00175     FmgrInfo    cast_func_finfo;    /* in case we must coerce input */
00176 } RI_CompareHashEntry;
00177 
00178 
00179 /* ----------
00180  * Local data
00181  * ----------
00182  */
00183 static HTAB *ri_constraint_cache = NULL;
00184 static HTAB *ri_query_cache = NULL;
00185 static HTAB *ri_compare_cache = NULL;
00186 
00187 
00188 /* ----------
00189  * Local function prototypes
00190  * ----------
00191  */
00192 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
00193                   HeapTuple old_row,
00194                   const RI_ConstraintInfo *riinfo);
00195 static Datum ri_restrict_del(TriggerData *trigdata, bool is_no_action);
00196 static Datum ri_restrict_upd(TriggerData *trigdata, bool is_no_action);
00197 static void quoteOneName(char *buffer, const char *name);
00198 static void quoteRelationName(char *buffer, Relation rel);
00199 static void ri_GenerateQual(StringInfo buf,
00200                 const char *sep,
00201                 const char *leftop, Oid leftoptype,
00202                 Oid opoid,
00203                 const char *rightop, Oid rightoptype);
00204 static void ri_add_cast_to(StringInfo buf, Oid typid);
00205 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
00206 static int ri_NullCheck(HeapTuple tup,
00207              const RI_ConstraintInfo *riinfo, bool rel_is_pk);
00208 static void ri_BuildQueryKey(RI_QueryKey *key,
00209                  const RI_ConstraintInfo *riinfo,
00210                  int32 constr_queryno);
00211 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
00212              const RI_ConstraintInfo *riinfo, bool rel_is_pk);
00213 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
00214                    Datum oldvalue, Datum newvalue);
00215 
00216 static void ri_InitHashTables(void);
00217 static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
00218 static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
00219 static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
00220 static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
00221 
00222 static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
00223                 int tgkind);
00224 static const RI_ConstraintInfo *ri_FetchConstraintInfo(Trigger *trigger,
00225                        Relation trig_rel, bool rel_is_pk);
00226 static const RI_ConstraintInfo *ri_LoadConstraintInfo(Oid constraintOid);
00227 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
00228              RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
00229              bool cache_plan);
00230 static bool ri_PerformCheck(const RI_ConstraintInfo *riinfo,
00231                 RI_QueryKey *qkey, SPIPlanPtr qplan,
00232                 Relation fk_rel, Relation pk_rel,
00233                 HeapTuple old_tuple, HeapTuple new_tuple,
00234                 bool detectNewRows, int expect_OK);
00235 static void ri_ExtractValues(Relation rel, HeapTuple tup,
00236                  const RI_ConstraintInfo *riinfo, bool rel_is_pk,
00237                  Datum *vals, char *nulls);
00238 static void ri_ReportViolation(const RI_ConstraintInfo *riinfo,
00239                    Relation pk_rel, Relation fk_rel,
00240                    HeapTuple violator, TupleDesc tupdesc,
00241                    int queryno, bool spi_err);
00242 
00243 
00244 /* ----------
00245  * RI_FKey_check -
00246  *
00247  *  Check foreign key existence (combined for INSERT and UPDATE).
00248  * ----------
00249  */
00250 static Datum
00251 RI_FKey_check(TriggerData *trigdata)
00252 {
00253     const RI_ConstraintInfo *riinfo;
00254     Relation    fk_rel;
00255     Relation    pk_rel;
00256     HeapTuple   new_row;
00257     Buffer      new_row_buf;
00258     RI_QueryKey qkey;
00259     SPIPlanPtr  qplan;
00260     int         i;
00261 
00262     /*
00263      * Get arguments.
00264      */
00265     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00266                                     trigdata->tg_relation, false);
00267 
00268     if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00269     {
00270         new_row = trigdata->tg_newtuple;
00271         new_row_buf = trigdata->tg_newtuplebuf;
00272     }
00273     else
00274     {
00275         new_row = trigdata->tg_trigtuple;
00276         new_row_buf = trigdata->tg_trigtuplebuf;
00277     }
00278 
00279     /*
00280      * We should not even consider checking the row if it is no longer valid,
00281      * since it was either deleted (so the deferred check should be skipped)
00282      * or updated (in which case only the latest version of the row should be
00283      * checked).  Test its liveness according to SnapshotSelf.
00284      *
00285      * NOTE: The normal coding rule is that one must acquire the buffer
00286      * content lock to call HeapTupleSatisfiesVisibility.  We can skip that
00287      * here because we know that AfterTriggerExecute just fetched the tuple
00288      * successfully, so there cannot be a VACUUM compaction in progress on the
00289      * page (either heap_fetch would have waited for the VACUUM, or the
00290      * VACUUM's LockBufferForCleanup would be waiting for us to drop pin). And
00291      * since this is a row inserted by our open transaction, no one else can
00292      * be entitled to change its xmin/xmax.
00293      */
00294     Assert(new_row_buf != InvalidBuffer);
00295     if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
00296         return PointerGetDatum(NULL);
00297 
00298     /*
00299      * Get the relation descriptors of the FK and PK tables.
00300      *
00301      * pk_rel is opened in RowShareLock mode since that's what our eventual
00302      * SELECT FOR KEY SHARE will get on it.
00303      */
00304     fk_rel = trigdata->tg_relation;
00305     pk_rel = heap_open(riinfo->pk_relid, RowShareLock);
00306 
00307     if (riinfo->confmatchtype == FKCONSTR_MATCH_PARTIAL)
00308         ereport(ERROR,
00309                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00310                  errmsg("MATCH PARTIAL not yet implemented")));
00311 
00312     switch (ri_NullCheck(new_row, riinfo, false))
00313     {
00314         case RI_KEYS_ALL_NULL:
00315 
00316             /*
00317              * No further check needed - an all-NULL key passes every type of
00318              * foreign key constraint.
00319              */
00320             heap_close(pk_rel, RowShareLock);
00321             return PointerGetDatum(NULL);
00322 
00323         case RI_KEYS_SOME_NULL:
00324 
00325             /*
00326              * This is the only case that differs between the three kinds of
00327              * MATCH.
00328              */
00329             switch (riinfo->confmatchtype)
00330             {
00331                 case FKCONSTR_MATCH_FULL:
00332 
00333                     /*
00334                      * Not allowed - MATCH FULL says either all or none of the
00335                      * attributes can be NULLs
00336                      */
00337                     ereport(ERROR,
00338                             (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
00339                              errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
00340                                     RelationGetRelationName(fk_rel),
00341                                     NameStr(riinfo->conname)),
00342                              errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
00343                              errtableconstraint(fk_rel,
00344                                                 NameStr(riinfo->conname))));
00345                     heap_close(pk_rel, RowShareLock);
00346                     return PointerGetDatum(NULL);
00347 
00348                 case FKCONSTR_MATCH_SIMPLE:
00349 
00350                     /*
00351                      * MATCH SIMPLE - if ANY column is null, the key passes
00352                      * the constraint.
00353                      */
00354                     heap_close(pk_rel, RowShareLock);
00355                     return PointerGetDatum(NULL);
00356 
00357                 case FKCONSTR_MATCH_PARTIAL:
00358 
00359                     /*
00360                      * MATCH PARTIAL - all non-null columns must match. (not
00361                      * implemented, can be done by modifying the query below
00362                      * to only include non-null columns, or by writing a
00363                      * special version here)
00364                      */
00365                     ereport(ERROR,
00366                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00367                              errmsg("MATCH PARTIAL not yet implemented")));
00368                     heap_close(pk_rel, RowShareLock);
00369                     return PointerGetDatum(NULL);
00370 
00371                 default:
00372                     elog(ERROR, "unrecognized confmatchtype: %d",
00373                          riinfo->confmatchtype);
00374                     break;
00375             }
00376 
00377         case RI_KEYS_NONE_NULL:
00378 
00379             /*
00380              * Have a full qualified key - continue below for all three kinds
00381              * of MATCH.
00382              */
00383             break;
00384     }
00385 
00386     if (SPI_connect() != SPI_OK_CONNECT)
00387         elog(ERROR, "SPI_connect failed");
00388 
00389     /*
00390      * Fetch or prepare a saved plan for the real check
00391      */
00392     ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
00393 
00394     if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00395     {
00396         StringInfoData querybuf;
00397         char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
00398         char        attname[MAX_QUOTED_NAME_LEN];
00399         char        paramname[16];
00400         const char *querysep;
00401         Oid         queryoids[RI_MAX_NUMKEYS];
00402 
00403         /* ----------
00404          * The query string built is
00405          *  SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
00406          *         FOR KEY SHARE OF x
00407          * The type id's for the $ parameters are those of the
00408          * corresponding FK attributes.
00409          * ----------
00410          */
00411         initStringInfo(&querybuf);
00412         quoteRelationName(pkrelname, pk_rel);
00413         appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
00414         querysep = "WHERE";
00415         for (i = 0; i < riinfo->nkeys; i++)
00416         {
00417             Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00418             Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00419 
00420             quoteOneName(attname,
00421                          RIAttName(pk_rel, riinfo->pk_attnums[i]));
00422             sprintf(paramname, "$%d", i + 1);
00423             ri_GenerateQual(&querybuf, querysep,
00424                             attname, pk_type,
00425                             riinfo->pf_eq_oprs[i],
00426                             paramname, fk_type);
00427             querysep = "AND";
00428             queryoids[i] = fk_type;
00429         }
00430         appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00431 
00432         /* Prepare and save the plan */
00433         qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00434                              &qkey, fk_rel, pk_rel, true);
00435     }
00436 
00437     /*
00438      * Now check that foreign key exists in PK table
00439      */
00440     ri_PerformCheck(riinfo, &qkey, qplan,
00441                     fk_rel, pk_rel,
00442                     NULL, new_row,
00443                     false,
00444                     SPI_OK_SELECT);
00445 
00446     if (SPI_finish() != SPI_OK_FINISH)
00447         elog(ERROR, "SPI_finish failed");
00448 
00449     heap_close(pk_rel, RowShareLock);
00450 
00451     return PointerGetDatum(NULL);
00452 }
00453 
00454 
00455 /* ----------
00456  * RI_FKey_check_ins -
00457  *
00458  *  Check foreign key existence at insert event on FK table.
00459  * ----------
00460  */
00461 Datum
00462 RI_FKey_check_ins(PG_FUNCTION_ARGS)
00463 {
00464     /*
00465      * Check that this is a valid trigger call on the right time and event.
00466      */
00467     ri_CheckTrigger(fcinfo, "RI_FKey_check_ins", RI_TRIGTYPE_INSERT);
00468 
00469     /*
00470      * Share code with UPDATE case.
00471      */
00472     return RI_FKey_check((TriggerData *) fcinfo->context);
00473 }
00474 
00475 
00476 /* ----------
00477  * RI_FKey_check_upd -
00478  *
00479  *  Check foreign key existence at update event on FK table.
00480  * ----------
00481  */
00482 Datum
00483 RI_FKey_check_upd(PG_FUNCTION_ARGS)
00484 {
00485     /*
00486      * Check that this is a valid trigger call on the right time and event.
00487      */
00488     ri_CheckTrigger(fcinfo, "RI_FKey_check_upd", RI_TRIGTYPE_UPDATE);
00489 
00490     /*
00491      * Share code with INSERT case.
00492      */
00493     return RI_FKey_check((TriggerData *) fcinfo->context);
00494 }
00495 
00496 
00497 /* ----------
00498  * ri_Check_Pk_Match
00499  *
00500  * Check to see if another PK row has been created that provides the same
00501  * key values as the "old_row" that's been modified or deleted in our trigger
00502  * event.  Returns true if a match is found in the PK table.
00503  *
00504  * We assume the caller checked that the old_row contains no NULL key values,
00505  * since otherwise a match is impossible.
00506  * ----------
00507  */
00508 static bool
00509 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
00510                   HeapTuple old_row,
00511                   const RI_ConstraintInfo *riinfo)
00512 {
00513     SPIPlanPtr  qplan;
00514     RI_QueryKey qkey;
00515     int         i;
00516     bool        result;
00517 
00518     /* Only called for non-null rows */
00519     Assert(ri_NullCheck(old_row, riinfo, true) == RI_KEYS_NONE_NULL);
00520 
00521     if (SPI_connect() != SPI_OK_CONNECT)
00522         elog(ERROR, "SPI_connect failed");
00523 
00524     /*
00525      * Fetch or prepare a saved plan for checking PK table with values coming
00526      * from a PK row
00527      */
00528     ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK_FROM_PK);
00529 
00530     if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00531     {
00532         StringInfoData querybuf;
00533         char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
00534         char        attname[MAX_QUOTED_NAME_LEN];
00535         char        paramname[16];
00536         const char *querysep;
00537         Oid         queryoids[RI_MAX_NUMKEYS];
00538 
00539         /* ----------
00540          * The query string built is
00541          *  SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...]
00542          *         FOR KEY SHARE OF x
00543          * The type id's for the $ parameters are those of the
00544          * PK attributes themselves.
00545          * ----------
00546          */
00547         initStringInfo(&querybuf);
00548         quoteRelationName(pkrelname, pk_rel);
00549         appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
00550         querysep = "WHERE";
00551         for (i = 0; i < riinfo->nkeys; i++)
00552         {
00553             Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00554 
00555             quoteOneName(attname,
00556                          RIAttName(pk_rel, riinfo->pk_attnums[i]));
00557             sprintf(paramname, "$%d", i + 1);
00558             ri_GenerateQual(&querybuf, querysep,
00559                             attname, pk_type,
00560                             riinfo->pp_eq_oprs[i],
00561                             paramname, pk_type);
00562             querysep = "AND";
00563             queryoids[i] = pk_type;
00564         }
00565         appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00566 
00567         /* Prepare and save the plan */
00568         qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00569                              &qkey, fk_rel, pk_rel, true);
00570     }
00571 
00572     /*
00573      * We have a plan now. Run it.
00574      */
00575     result = ri_PerformCheck(riinfo, &qkey, qplan,
00576                              fk_rel, pk_rel,
00577                              old_row, NULL,
00578                              true,      /* treat like update */
00579                              SPI_OK_SELECT);
00580 
00581     if (SPI_finish() != SPI_OK_FINISH)
00582         elog(ERROR, "SPI_finish failed");
00583 
00584     return result;
00585 }
00586 
00587 
00588 /* ----------
00589  * RI_FKey_noaction_del -
00590  *
00591  *  Give an error and roll back the current transaction if the
00592  *  delete has resulted in a violation of the given referential
00593  *  integrity constraint.
00594  * ----------
00595  */
00596 Datum
00597 RI_FKey_noaction_del(PG_FUNCTION_ARGS)
00598 {
00599     /*
00600      * Check that this is a valid trigger call on the right time and event.
00601      */
00602     ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
00603 
00604     /*
00605      * Share code with RESTRICT case.
00606      */
00607     return ri_restrict_del((TriggerData *) fcinfo->context, true);
00608 }
00609 
00610 /* ----------
00611  * RI_FKey_restrict_del -
00612  *
00613  *  Restrict delete from PK table to rows unreferenced by foreign key.
00614  *
00615  *  The SQL standard intends that this referential action occur exactly when
00616  *  the delete is performed, rather than after.  This appears to be
00617  *  the only difference between "NO ACTION" and "RESTRICT".  In Postgres
00618  *  we still implement this as an AFTER trigger, but it's non-deferrable.
00619  * ----------
00620  */
00621 Datum
00622 RI_FKey_restrict_del(PG_FUNCTION_ARGS)
00623 {
00624     /*
00625      * Check that this is a valid trigger call on the right time and event.
00626      */
00627     ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
00628 
00629     /*
00630      * Share code with NO ACTION case.
00631      */
00632     return ri_restrict_del((TriggerData *) fcinfo->context, false);
00633 }
00634 
00635 /* ----------
00636  * ri_restrict_del -
00637  *
00638  *  Common code for ON DELETE RESTRICT and ON DELETE NO ACTION.
00639  * ----------
00640  */
00641 static Datum
00642 ri_restrict_del(TriggerData *trigdata, bool is_no_action)
00643 {
00644     const RI_ConstraintInfo *riinfo;
00645     Relation    fk_rel;
00646     Relation    pk_rel;
00647     HeapTuple   old_row;
00648     RI_QueryKey qkey;
00649     SPIPlanPtr  qplan;
00650     int         i;
00651 
00652     /*
00653      * Get arguments.
00654      */
00655     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00656                                     trigdata->tg_relation, true);
00657 
00658     /*
00659      * Get the relation descriptors of the FK and PK tables and the old tuple.
00660      *
00661      * fk_rel is opened in RowShareLock mode since that's what our eventual
00662      * SELECT FOR KEY SHARE will get on it.
00663      */
00664     fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
00665     pk_rel = trigdata->tg_relation;
00666     old_row = trigdata->tg_trigtuple;
00667 
00668     switch (riinfo->confmatchtype)
00669     {
00670             /* ----------
00671              * SQL:2008 15.17 <Execution of referential actions>
00672              *  General rules 9) a) iv):
00673              *      MATCH SIMPLE/FULL
00674              *          ... ON DELETE RESTRICT
00675              * ----------
00676              */
00677         case FKCONSTR_MATCH_SIMPLE:
00678         case FKCONSTR_MATCH_FULL:
00679             switch (ri_NullCheck(old_row, riinfo, true))
00680             {
00681                 case RI_KEYS_ALL_NULL:
00682                 case RI_KEYS_SOME_NULL:
00683 
00684                     /*
00685                      * No check needed - there cannot be any reference to old
00686                      * key if it contains a NULL
00687                      */
00688                     heap_close(fk_rel, RowShareLock);
00689                     return PointerGetDatum(NULL);
00690 
00691                 case RI_KEYS_NONE_NULL:
00692 
00693                     /*
00694                      * Have a full qualified key - continue below
00695                      */
00696                     break;
00697             }
00698 
00699             /*
00700              * If another PK row now exists providing the old key values,
00701              * we should not do anything.  However, this check should only be
00702              * made in the NO ACTION case; in RESTRICT cases we don't wish to
00703              * allow another row to be substituted.
00704              */
00705             if (is_no_action &&
00706                 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
00707             {
00708                 heap_close(fk_rel, RowShareLock);
00709                 return PointerGetDatum(NULL);
00710             }
00711 
00712             if (SPI_connect() != SPI_OK_CONNECT)
00713                 elog(ERROR, "SPI_connect failed");
00714 
00715             /*
00716              * Fetch or prepare a saved plan for the restrict delete lookup
00717              */
00718             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_DEL_CHECKREF);
00719 
00720             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00721             {
00722                 StringInfoData querybuf;
00723                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
00724                 char        attname[MAX_QUOTED_NAME_LEN];
00725                 char        paramname[16];
00726                 const char *querysep;
00727                 Oid         queryoids[RI_MAX_NUMKEYS];
00728 
00729                 /* ----------
00730                  * The query string built is
00731                  *  SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...]
00732                  *         FOR KEY SHARE OF x
00733                  * The type id's for the $ parameters are those of the
00734                  * corresponding PK attributes.
00735                  * ----------
00736                  */
00737                 initStringInfo(&querybuf);
00738                 quoteRelationName(fkrelname, fk_rel);
00739                 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
00740                                  fkrelname);
00741                 querysep = "WHERE";
00742                 for (i = 0; i < riinfo->nkeys; i++)
00743                 {
00744                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00745                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00746 
00747                     quoteOneName(attname,
00748                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
00749                     sprintf(paramname, "$%d", i + 1);
00750                     ri_GenerateQual(&querybuf, querysep,
00751                                     paramname, pk_type,
00752                                     riinfo->pf_eq_oprs[i],
00753                                     attname, fk_type);
00754                     querysep = "AND";
00755                     queryoids[i] = pk_type;
00756                 }
00757                 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00758 
00759                 /* Prepare and save the plan */
00760                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00761                                      &qkey, fk_rel, pk_rel, true);
00762             }
00763 
00764             /*
00765              * We have a plan now. Run it to check for existing references.
00766              */
00767             ri_PerformCheck(riinfo, &qkey, qplan,
00768                             fk_rel, pk_rel,
00769                             old_row, NULL,
00770                             true,       /* must detect new rows */
00771                             SPI_OK_SELECT);
00772 
00773             if (SPI_finish() != SPI_OK_FINISH)
00774                 elog(ERROR, "SPI_finish failed");
00775 
00776             heap_close(fk_rel, RowShareLock);
00777 
00778             return PointerGetDatum(NULL);
00779 
00780             /*
00781              * Handle MATCH PARTIAL restrict delete.
00782              */
00783         case FKCONSTR_MATCH_PARTIAL:
00784             ereport(ERROR,
00785                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00786                      errmsg("MATCH PARTIAL not yet implemented")));
00787             return PointerGetDatum(NULL);
00788 
00789         default:
00790             elog(ERROR, "unrecognized confmatchtype: %d",
00791                  riinfo->confmatchtype);
00792             break;
00793     }
00794 
00795     /* Never reached */
00796     return PointerGetDatum(NULL);
00797 }
00798 
00799 
00800 /* ----------
00801  * RI_FKey_noaction_upd -
00802  *
00803  *  Give an error and roll back the current transaction if the
00804  *  update has resulted in a violation of the given referential
00805  *  integrity constraint.
00806  * ----------
00807  */
00808 Datum
00809 RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
00810 {
00811     /*
00812      * Check that this is a valid trigger call on the right time and event.
00813      */
00814     ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
00815 
00816     /*
00817      * Share code with RESTRICT case.
00818      */
00819     return ri_restrict_upd((TriggerData *) fcinfo->context, true);
00820 }
00821 
00822 /* ----------
00823  * RI_FKey_restrict_upd -
00824  *
00825  *  Restrict update of PK to rows unreferenced by foreign key.
00826  *
00827  *  The SQL standard intends that this referential action occur exactly when
00828  *  the update is performed, rather than after.  This appears to be
00829  *  the only difference between "NO ACTION" and "RESTRICT".  In Postgres
00830  *  we still implement this as an AFTER trigger, but it's non-deferrable.
00831  * ----------
00832  */
00833 Datum
00834 RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
00835 {
00836     /*
00837      * Check that this is a valid trigger call on the right time and event.
00838      */
00839     ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
00840 
00841     /*
00842      * Share code with NO ACTION case.
00843      */
00844     return ri_restrict_upd((TriggerData *) fcinfo->context, false);
00845 }
00846 
00847 /* ----------
00848  * ri_restrict_upd -
00849  *
00850  *  Common code for ON UPDATE RESTRICT and ON UPDATE NO ACTION.
00851  * ----------
00852  */
00853 static Datum
00854 ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
00855 {
00856     const RI_ConstraintInfo *riinfo;
00857     Relation    fk_rel;
00858     Relation    pk_rel;
00859     HeapTuple   new_row;
00860     HeapTuple   old_row;
00861     RI_QueryKey qkey;
00862     SPIPlanPtr  qplan;
00863     int         i;
00864 
00865     /*
00866      * Get arguments.
00867      */
00868     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
00869                                     trigdata->tg_relation, true);
00870 
00871     /*
00872      * Get the relation descriptors of the FK and PK tables and the new and
00873      * old tuple.
00874      *
00875      * fk_rel is opened in RowShareLock mode since that's what our eventual
00876      * SELECT FOR KEY SHARE will get on it.
00877      */
00878     fk_rel = heap_open(riinfo->fk_relid, RowShareLock);
00879     pk_rel = trigdata->tg_relation;
00880     new_row = trigdata->tg_newtuple;
00881     old_row = trigdata->tg_trigtuple;
00882 
00883     switch (riinfo->confmatchtype)
00884     {
00885             /* ----------
00886              * SQL:2008 15.17 <Execution of referential actions>
00887              *  General rules 10) a) iv):
00888              *      MATCH SIMPLE/FULL
00889              *          ... ON UPDATE RESTRICT
00890              * ----------
00891              */
00892         case FKCONSTR_MATCH_SIMPLE:
00893         case FKCONSTR_MATCH_FULL:
00894             switch (ri_NullCheck(old_row, riinfo, true))
00895             {
00896                 case RI_KEYS_ALL_NULL:
00897                 case RI_KEYS_SOME_NULL:
00898 
00899                     /*
00900                      * No check needed - there cannot be any reference to old
00901                      * key if it contains a NULL
00902                      */
00903                     heap_close(fk_rel, RowShareLock);
00904                     return PointerGetDatum(NULL);
00905 
00906                 case RI_KEYS_NONE_NULL:
00907 
00908                     /*
00909                      * Have a full qualified key - continue below
00910                      */
00911                     break;
00912             }
00913 
00914             /*
00915              * No need to check anything if old and new keys are equal
00916              */
00917             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
00918             {
00919                 heap_close(fk_rel, RowShareLock);
00920                 return PointerGetDatum(NULL);
00921             }
00922 
00923             /*
00924              * If another PK row now exists providing the old key values,
00925              * we should not do anything.  However, this check should only be
00926              * made in the NO ACTION case; in RESTRICT cases we don't wish to
00927              * allow another row to be substituted.
00928              */
00929             if (is_no_action &&
00930                 ri_Check_Pk_Match(pk_rel, fk_rel, old_row, riinfo))
00931             {
00932                 heap_close(fk_rel, RowShareLock);
00933                 return PointerGetDatum(NULL);
00934             }
00935 
00936             if (SPI_connect() != SPI_OK_CONNECT)
00937                 elog(ERROR, "SPI_connect failed");
00938 
00939             /*
00940              * Fetch or prepare a saved plan for the restrict update lookup
00941              */
00942             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_RESTRICT_UPD_CHECKREF);
00943 
00944             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
00945             {
00946                 StringInfoData querybuf;
00947                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
00948                 char        attname[MAX_QUOTED_NAME_LEN];
00949                 char        paramname[16];
00950                 const char *querysep;
00951                 Oid         queryoids[RI_MAX_NUMKEYS];
00952 
00953                 /* ----------
00954                  * The query string built is
00955                  *  SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
00956                  * The type id's for the $ parameters are those of the
00957                  * corresponding PK attributes.
00958                  * ----------
00959                  */
00960                 initStringInfo(&querybuf);
00961                 quoteRelationName(fkrelname, fk_rel);
00962                 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
00963                                  fkrelname);
00964                 querysep = "WHERE";
00965                 for (i = 0; i < riinfo->nkeys; i++)
00966                 {
00967                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
00968                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
00969 
00970                     quoteOneName(attname,
00971                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
00972                     sprintf(paramname, "$%d", i + 1);
00973                     ri_GenerateQual(&querybuf, querysep,
00974                                     paramname, pk_type,
00975                                     riinfo->pf_eq_oprs[i],
00976                                     attname, fk_type);
00977                     querysep = "AND";
00978                     queryoids[i] = pk_type;
00979                 }
00980                 appendStringInfo(&querybuf, " FOR KEY SHARE OF x");
00981 
00982                 /* Prepare and save the plan */
00983                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
00984                                      &qkey, fk_rel, pk_rel, true);
00985             }
00986 
00987             /*
00988              * We have a plan now. Run it to check for existing references.
00989              */
00990             ri_PerformCheck(riinfo, &qkey, qplan,
00991                             fk_rel, pk_rel,
00992                             old_row, NULL,
00993                             true,       /* must detect new rows */
00994                             SPI_OK_SELECT);
00995 
00996             if (SPI_finish() != SPI_OK_FINISH)
00997                 elog(ERROR, "SPI_finish failed");
00998 
00999             heap_close(fk_rel, RowShareLock);
01000 
01001             return PointerGetDatum(NULL);
01002 
01003             /*
01004              * Handle MATCH PARTIAL restrict update.
01005              */
01006         case FKCONSTR_MATCH_PARTIAL:
01007             ereport(ERROR,
01008                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01009                      errmsg("MATCH PARTIAL not yet implemented")));
01010             return PointerGetDatum(NULL);
01011 
01012         default:
01013             elog(ERROR, "unrecognized confmatchtype: %d",
01014                  riinfo->confmatchtype);
01015             break;
01016     }
01017 
01018     /* Never reached */
01019     return PointerGetDatum(NULL);
01020 }
01021 
01022 
01023 /* ----------
01024  * RI_FKey_cascade_del -
01025  *
01026  *  Cascaded delete foreign key references at delete event on PK table.
01027  * ----------
01028  */
01029 Datum
01030 RI_FKey_cascade_del(PG_FUNCTION_ARGS)
01031 {
01032     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01033     const RI_ConstraintInfo *riinfo;
01034     Relation    fk_rel;
01035     Relation    pk_rel;
01036     HeapTuple   old_row;
01037     RI_QueryKey qkey;
01038     SPIPlanPtr  qplan;
01039     int         i;
01040 
01041     /*
01042      * Check that this is a valid trigger call on the right time and event.
01043      */
01044     ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
01045 
01046     /*
01047      * Get arguments.
01048      */
01049     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01050                                     trigdata->tg_relation, true);
01051 
01052     /*
01053      * Get the relation descriptors of the FK and PK tables and the old tuple.
01054      *
01055      * fk_rel is opened in RowExclusiveLock mode since that's what our
01056      * eventual DELETE will get on it.
01057      */
01058     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01059     pk_rel = trigdata->tg_relation;
01060     old_row = trigdata->tg_trigtuple;
01061 
01062     switch (riinfo->confmatchtype)
01063     {
01064             /* ----------
01065              * SQL:2008 15.17 <Execution of referential actions>
01066              *  General rules 9) a) i):
01067              *      MATCH SIMPLE/FULL
01068              *          ... ON DELETE CASCADE
01069              * ----------
01070              */
01071         case FKCONSTR_MATCH_SIMPLE:
01072         case FKCONSTR_MATCH_FULL:
01073             switch (ri_NullCheck(old_row, riinfo, true))
01074             {
01075                 case RI_KEYS_ALL_NULL:
01076                 case RI_KEYS_SOME_NULL:
01077 
01078                     /*
01079                      * No check needed - there cannot be any reference to old
01080                      * key if it contains a NULL
01081                      */
01082                     heap_close(fk_rel, RowExclusiveLock);
01083                     return PointerGetDatum(NULL);
01084 
01085                 case RI_KEYS_NONE_NULL:
01086 
01087                     /*
01088                      * Have a full qualified key - continue below
01089                      */
01090                     break;
01091             }
01092 
01093             if (SPI_connect() != SPI_OK_CONNECT)
01094                 elog(ERROR, "SPI_connect failed");
01095 
01096             /*
01097              * Fetch or prepare a saved plan for the cascaded delete
01098              */
01099             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_DEL_DODELETE);
01100 
01101             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01102             {
01103                 StringInfoData querybuf;
01104                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01105                 char        attname[MAX_QUOTED_NAME_LEN];
01106                 char        paramname[16];
01107                 const char *querysep;
01108                 Oid         queryoids[RI_MAX_NUMKEYS];
01109 
01110                 /* ----------
01111                  * The query string built is
01112                  *  DELETE FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
01113                  * The type id's for the $ parameters are those of the
01114                  * corresponding PK attributes.
01115                  * ----------
01116                  */
01117                 initStringInfo(&querybuf);
01118                 quoteRelationName(fkrelname, fk_rel);
01119                 appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
01120                 querysep = "WHERE";
01121                 for (i = 0; i < riinfo->nkeys; i++)
01122                 {
01123                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01124                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01125 
01126                     quoteOneName(attname,
01127                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
01128                     sprintf(paramname, "$%d", i + 1);
01129                     ri_GenerateQual(&querybuf, querysep,
01130                                     paramname, pk_type,
01131                                     riinfo->pf_eq_oprs[i],
01132                                     attname, fk_type);
01133                     querysep = "AND";
01134                     queryoids[i] = pk_type;
01135                 }
01136 
01137                 /* Prepare and save the plan */
01138                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01139                                      &qkey, fk_rel, pk_rel, true);
01140             }
01141 
01142             /*
01143              * We have a plan now. Build up the arguments from the key values
01144              * in the deleted PK tuple and delete the referencing rows
01145              */
01146             ri_PerformCheck(riinfo, &qkey, qplan,
01147                             fk_rel, pk_rel,
01148                             old_row, NULL,
01149                             true,       /* must detect new rows */
01150                             SPI_OK_DELETE);
01151 
01152             if (SPI_finish() != SPI_OK_FINISH)
01153                 elog(ERROR, "SPI_finish failed");
01154 
01155             heap_close(fk_rel, RowExclusiveLock);
01156 
01157             return PointerGetDatum(NULL);
01158 
01159             /*
01160              * Handle MATCH PARTIAL cascaded delete.
01161              */
01162         case FKCONSTR_MATCH_PARTIAL:
01163             ereport(ERROR,
01164                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01165                      errmsg("MATCH PARTIAL not yet implemented")));
01166             return PointerGetDatum(NULL);
01167 
01168         default:
01169             elog(ERROR, "unrecognized confmatchtype: %d",
01170                  riinfo->confmatchtype);
01171             break;
01172     }
01173 
01174     /* Never reached */
01175     return PointerGetDatum(NULL);
01176 }
01177 
01178 
01179 /* ----------
01180  * RI_FKey_cascade_upd -
01181  *
01182  *  Cascaded update foreign key references at update event on PK table.
01183  * ----------
01184  */
01185 Datum
01186 RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
01187 {
01188     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01189     const RI_ConstraintInfo *riinfo;
01190     Relation    fk_rel;
01191     Relation    pk_rel;
01192     HeapTuple   new_row;
01193     HeapTuple   old_row;
01194     RI_QueryKey qkey;
01195     SPIPlanPtr  qplan;
01196     int         i;
01197     int         j;
01198 
01199     /*
01200      * Check that this is a valid trigger call on the right time and event.
01201      */
01202     ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
01203 
01204     /*
01205      * Get arguments.
01206      */
01207     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01208                                     trigdata->tg_relation, true);
01209 
01210     /*
01211      * Get the relation descriptors of the FK and PK tables and the new and
01212      * old tuple.
01213      *
01214      * fk_rel is opened in RowExclusiveLock mode since that's what our
01215      * eventual UPDATE will get on it.
01216      */
01217     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01218     pk_rel = trigdata->tg_relation;
01219     new_row = trigdata->tg_newtuple;
01220     old_row = trigdata->tg_trigtuple;
01221 
01222     switch (riinfo->confmatchtype)
01223     {
01224             /* ----------
01225              * SQL:2008 15.17 <Execution of referential actions>
01226              *  General rules 10) a) i):
01227              *      MATCH SIMPLE/FULL
01228              *          ... ON UPDATE CASCADE
01229              * ----------
01230              */
01231         case FKCONSTR_MATCH_SIMPLE:
01232         case FKCONSTR_MATCH_FULL:
01233             switch (ri_NullCheck(old_row, riinfo, true))
01234             {
01235                 case RI_KEYS_ALL_NULL:
01236                 case RI_KEYS_SOME_NULL:
01237 
01238                     /*
01239                      * No check needed - there cannot be any reference to old
01240                      * key if it contains a NULL
01241                      */
01242                     heap_close(fk_rel, RowExclusiveLock);
01243                     return PointerGetDatum(NULL);
01244 
01245                 case RI_KEYS_NONE_NULL:
01246 
01247                     /*
01248                      * Have a full qualified key - continue below
01249                      */
01250                     break;
01251             }
01252 
01253             /*
01254              * No need to do anything if old and new keys are equal
01255              */
01256             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01257             {
01258                 heap_close(fk_rel, RowExclusiveLock);
01259                 return PointerGetDatum(NULL);
01260             }
01261 
01262             if (SPI_connect() != SPI_OK_CONNECT)
01263                 elog(ERROR, "SPI_connect failed");
01264 
01265             /*
01266              * Fetch or prepare a saved plan for the cascaded update
01267              */
01268             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_CASCADE_UPD_DOUPDATE);
01269 
01270             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01271             {
01272                 StringInfoData querybuf;
01273                 StringInfoData qualbuf;
01274                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01275                 char        attname[MAX_QUOTED_NAME_LEN];
01276                 char        paramname[16];
01277                 const char *querysep;
01278                 const char *qualsep;
01279                 Oid         queryoids[RI_MAX_NUMKEYS * 2];
01280 
01281                 /* ----------
01282                  * The query string built is
01283                  *  UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]
01284                  *          WHERE $n = fkatt1 [AND ...]
01285                  * The type id's for the $ parameters are those of the
01286                  * corresponding PK attributes.  Note that we are assuming
01287                  * there is an assignment cast from the PK to the FK type;
01288                  * else the parser will fail.
01289                  * ----------
01290                  */
01291                 initStringInfo(&querybuf);
01292                 initStringInfo(&qualbuf);
01293                 quoteRelationName(fkrelname, fk_rel);
01294                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01295                 querysep = "";
01296                 qualsep = "WHERE";
01297                 for (i = 0, j = riinfo->nkeys; i < riinfo->nkeys; i++, j++)
01298                 {
01299                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01300                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01301 
01302                     quoteOneName(attname,
01303                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
01304                     appendStringInfo(&querybuf,
01305                                      "%s %s = $%d",
01306                                      querysep, attname, i + 1);
01307                     sprintf(paramname, "$%d", j + 1);
01308                     ri_GenerateQual(&qualbuf, qualsep,
01309                                     paramname, pk_type,
01310                                     riinfo->pf_eq_oprs[i],
01311                                     attname, fk_type);
01312                     querysep = ",";
01313                     qualsep = "AND";
01314                     queryoids[i] = pk_type;
01315                     queryoids[j] = pk_type;
01316                 }
01317                 appendStringInfoString(&querybuf, qualbuf.data);
01318 
01319                 /* Prepare and save the plan */
01320                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys * 2, queryoids,
01321                                      &qkey, fk_rel, pk_rel, true);
01322             }
01323 
01324             /*
01325              * We have a plan now. Run it to update the existing references.
01326              */
01327             ri_PerformCheck(riinfo, &qkey, qplan,
01328                             fk_rel, pk_rel,
01329                             old_row, new_row,
01330                             true,       /* must detect new rows */
01331                             SPI_OK_UPDATE);
01332 
01333             if (SPI_finish() != SPI_OK_FINISH)
01334                 elog(ERROR, "SPI_finish failed");
01335 
01336             heap_close(fk_rel, RowExclusiveLock);
01337 
01338             return PointerGetDatum(NULL);
01339 
01340             /*
01341              * Handle MATCH PARTIAL cascade update.
01342              */
01343         case FKCONSTR_MATCH_PARTIAL:
01344             ereport(ERROR,
01345                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01346                      errmsg("MATCH PARTIAL not yet implemented")));
01347             return PointerGetDatum(NULL);
01348 
01349         default:
01350             elog(ERROR, "unrecognized confmatchtype: %d",
01351                  riinfo->confmatchtype);
01352             break;
01353     }
01354 
01355     /* Never reached */
01356     return PointerGetDatum(NULL);
01357 }
01358 
01359 
01360 /* ----------
01361  * RI_FKey_setnull_del -
01362  *
01363  *  Set foreign key references to NULL values at delete event on PK table.
01364  * ----------
01365  */
01366 Datum
01367 RI_FKey_setnull_del(PG_FUNCTION_ARGS)
01368 {
01369     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01370     const RI_ConstraintInfo *riinfo;
01371     Relation    fk_rel;
01372     Relation    pk_rel;
01373     HeapTuple   old_row;
01374     RI_QueryKey qkey;
01375     SPIPlanPtr  qplan;
01376     int         i;
01377 
01378     /*
01379      * Check that this is a valid trigger call on the right time and event.
01380      */
01381     ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
01382 
01383     /*
01384      * Get arguments.
01385      */
01386     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01387                                     trigdata->tg_relation, true);
01388 
01389     /*
01390      * Get the relation descriptors of the FK and PK tables and the old tuple.
01391      *
01392      * fk_rel is opened in RowExclusiveLock mode since that's what our
01393      * eventual UPDATE will get on it.
01394      */
01395     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01396     pk_rel = trigdata->tg_relation;
01397     old_row = trigdata->tg_trigtuple;
01398 
01399     switch (riinfo->confmatchtype)
01400     {
01401             /* ----------
01402              * SQL:2008 15.17 <Execution of referential actions>
01403              *  General rules 9) a) ii):
01404              *      MATCH SIMPLE/FULL
01405              *          ... ON DELETE SET NULL
01406              * ----------
01407              */
01408         case FKCONSTR_MATCH_SIMPLE:
01409         case FKCONSTR_MATCH_FULL:
01410             switch (ri_NullCheck(old_row, riinfo, true))
01411             {
01412                 case RI_KEYS_ALL_NULL:
01413                 case RI_KEYS_SOME_NULL:
01414 
01415                     /*
01416                      * No check needed - there cannot be any reference to old
01417                      * key if it contains a NULL
01418                      */
01419                     heap_close(fk_rel, RowExclusiveLock);
01420                     return PointerGetDatum(NULL);
01421 
01422                 case RI_KEYS_NONE_NULL:
01423 
01424                     /*
01425                      * Have a full qualified key - continue below
01426                      */
01427                     break;
01428             }
01429 
01430             if (SPI_connect() != SPI_OK_CONNECT)
01431                 elog(ERROR, "SPI_connect failed");
01432 
01433             /*
01434              * Fetch or prepare a saved plan for the set null delete operation
01435              */
01436             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_DEL_DOUPDATE);
01437 
01438             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01439             {
01440                 StringInfoData querybuf;
01441                 StringInfoData qualbuf;
01442                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01443                 char        attname[MAX_QUOTED_NAME_LEN];
01444                 char        paramname[16];
01445                 const char *querysep;
01446                 const char *qualsep;
01447                 Oid         queryoids[RI_MAX_NUMKEYS];
01448 
01449                 /* ----------
01450                  * The query string built is
01451                  *  UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
01452                  *          WHERE $1 = fkatt1 [AND ...]
01453                  * The type id's for the $ parameters are those of the
01454                  * corresponding PK attributes.
01455                  * ----------
01456                  */
01457                 initStringInfo(&querybuf);
01458                 initStringInfo(&qualbuf);
01459                 quoteRelationName(fkrelname, fk_rel);
01460                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01461                 querysep = "";
01462                 qualsep = "WHERE";
01463                 for (i = 0; i < riinfo->nkeys; i++)
01464                 {
01465                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01466                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01467 
01468                     quoteOneName(attname,
01469                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
01470                     appendStringInfo(&querybuf,
01471                                      "%s %s = NULL",
01472                                      querysep, attname);
01473                     sprintf(paramname, "$%d", i + 1);
01474                     ri_GenerateQual(&qualbuf, qualsep,
01475                                     paramname, pk_type,
01476                                     riinfo->pf_eq_oprs[i],
01477                                     attname, fk_type);
01478                     querysep = ",";
01479                     qualsep = "AND";
01480                     queryoids[i] = pk_type;
01481                 }
01482                 appendStringInfoString(&querybuf, qualbuf.data);
01483 
01484                 /* Prepare and save the plan */
01485                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01486                                      &qkey, fk_rel, pk_rel, true);
01487             }
01488 
01489             /*
01490              * We have a plan now. Run it to check for existing references.
01491              */
01492             ri_PerformCheck(riinfo, &qkey, qplan,
01493                             fk_rel, pk_rel,
01494                             old_row, NULL,
01495                             true,       /* must detect new rows */
01496                             SPI_OK_UPDATE);
01497 
01498             if (SPI_finish() != SPI_OK_FINISH)
01499                 elog(ERROR, "SPI_finish failed");
01500 
01501             heap_close(fk_rel, RowExclusiveLock);
01502 
01503             return PointerGetDatum(NULL);
01504 
01505             /*
01506              * Handle MATCH PARTIAL set null delete.
01507              */
01508         case FKCONSTR_MATCH_PARTIAL:
01509             ereport(ERROR,
01510                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01511                      errmsg("MATCH PARTIAL not yet implemented")));
01512             return PointerGetDatum(NULL);
01513 
01514         default:
01515             elog(ERROR, "unrecognized confmatchtype: %d",
01516                  riinfo->confmatchtype);
01517             break;
01518     }
01519 
01520     /* Never reached */
01521     return PointerGetDatum(NULL);
01522 }
01523 
01524 
01525 /* ----------
01526  * RI_FKey_setnull_upd -
01527  *
01528  *  Set foreign key references to NULL at update event on PK table.
01529  * ----------
01530  */
01531 Datum
01532 RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
01533 {
01534     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01535     const RI_ConstraintInfo *riinfo;
01536     Relation    fk_rel;
01537     Relation    pk_rel;
01538     HeapTuple   new_row;
01539     HeapTuple   old_row;
01540     RI_QueryKey qkey;
01541     SPIPlanPtr  qplan;
01542     int         i;
01543 
01544     /*
01545      * Check that this is a valid trigger call on the right time and event.
01546      */
01547     ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
01548 
01549     /*
01550      * Get arguments.
01551      */
01552     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01553                                     trigdata->tg_relation, true);
01554 
01555     /*
01556      * Get the relation descriptors of the FK and PK tables and the old tuple.
01557      *
01558      * fk_rel is opened in RowExclusiveLock mode since that's what our
01559      * eventual UPDATE will get on it.
01560      */
01561     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01562     pk_rel = trigdata->tg_relation;
01563     new_row = trigdata->tg_newtuple;
01564     old_row = trigdata->tg_trigtuple;
01565 
01566     switch (riinfo->confmatchtype)
01567     {
01568             /* ----------
01569              * SQL:2008 15.17 <Execution of referential actions>
01570              *  General rules 10) a) ii):
01571              *      MATCH SIMPLE/FULL
01572              *          ... ON UPDATE SET NULL
01573              * ----------
01574              */
01575         case FKCONSTR_MATCH_SIMPLE:
01576         case FKCONSTR_MATCH_FULL:
01577             switch (ri_NullCheck(old_row, riinfo, true))
01578             {
01579                 case RI_KEYS_ALL_NULL:
01580                 case RI_KEYS_SOME_NULL:
01581 
01582                     /*
01583                      * No check needed - there cannot be any reference to old
01584                      * key if it contains a NULL
01585                      */
01586                     heap_close(fk_rel, RowExclusiveLock);
01587                     return PointerGetDatum(NULL);
01588 
01589                 case RI_KEYS_NONE_NULL:
01590 
01591                     /*
01592                      * Have a full qualified key - continue below
01593                      */
01594                     break;
01595             }
01596 
01597             /*
01598              * No need to do anything if old and new keys are equal
01599              */
01600             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01601             {
01602                 heap_close(fk_rel, RowExclusiveLock);
01603                 return PointerGetDatum(NULL);
01604             }
01605 
01606             if (SPI_connect() != SPI_OK_CONNECT)
01607                 elog(ERROR, "SPI_connect failed");
01608 
01609             /*
01610              * Fetch or prepare a saved plan for the set null update operation
01611              */
01612             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETNULL_UPD_DOUPDATE);
01613 
01614             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01615             {
01616                 StringInfoData querybuf;
01617                 StringInfoData qualbuf;
01618                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01619                 char        attname[MAX_QUOTED_NAME_LEN];
01620                 char        paramname[16];
01621                 const char *querysep;
01622                 const char *qualsep;
01623                 Oid         queryoids[RI_MAX_NUMKEYS];
01624 
01625                 /* ----------
01626                  * The query string built is
01627                  *  UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
01628                  *          WHERE $1 = fkatt1 [AND ...]
01629                  * The type id's for the $ parameters are those of the
01630                  * corresponding PK attributes.
01631                  * ----------
01632                  */
01633                 initStringInfo(&querybuf);
01634                 initStringInfo(&qualbuf);
01635                 quoteRelationName(fkrelname, fk_rel);
01636                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01637                 querysep = "";
01638                 qualsep = "WHERE";
01639                 for (i = 0; i < riinfo->nkeys; i++)
01640                 {
01641                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01642                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01643 
01644                     quoteOneName(attname,
01645                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
01646                     appendStringInfo(&querybuf,
01647                                      "%s %s = NULL",
01648                                      querysep, attname);
01649                     sprintf(paramname, "$%d", i + 1);
01650                     ri_GenerateQual(&qualbuf, qualsep,
01651                                     paramname, pk_type,
01652                                     riinfo->pf_eq_oprs[i],
01653                                     attname, fk_type);
01654                     querysep = ",";
01655                     qualsep = "AND";
01656                     queryoids[i] = pk_type;
01657                 }
01658                 appendStringInfoString(&querybuf, qualbuf.data);
01659 
01660                 /* Prepare and save the plan */
01661                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01662                                      &qkey, fk_rel, pk_rel, true);
01663             }
01664 
01665             /*
01666              * We have a plan now. Run it to update the existing references.
01667              */
01668             ri_PerformCheck(riinfo, &qkey, qplan,
01669                             fk_rel, pk_rel,
01670                             old_row, NULL,
01671                             true,       /* must detect new rows */
01672                             SPI_OK_UPDATE);
01673 
01674             if (SPI_finish() != SPI_OK_FINISH)
01675                 elog(ERROR, "SPI_finish failed");
01676 
01677             heap_close(fk_rel, RowExclusiveLock);
01678 
01679             return PointerGetDatum(NULL);
01680 
01681             /*
01682              * Handle MATCH PARTIAL set null update.
01683              */
01684         case FKCONSTR_MATCH_PARTIAL:
01685             ereport(ERROR,
01686                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01687                      errmsg("MATCH PARTIAL not yet implemented")));
01688             return PointerGetDatum(NULL);
01689 
01690         default:
01691             elog(ERROR, "unrecognized confmatchtype: %d",
01692                  riinfo->confmatchtype);
01693             break;
01694     }
01695 
01696     /* Never reached */
01697     return PointerGetDatum(NULL);
01698 }
01699 
01700 
01701 /* ----------
01702  * RI_FKey_setdefault_del -
01703  *
01704  *  Set foreign key references to defaults at delete event on PK table.
01705  * ----------
01706  */
01707 Datum
01708 RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
01709 {
01710     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01711     const RI_ConstraintInfo *riinfo;
01712     Relation    fk_rel;
01713     Relation    pk_rel;
01714     HeapTuple   old_row;
01715     RI_QueryKey qkey;
01716     SPIPlanPtr  qplan;
01717 
01718     /*
01719      * Check that this is a valid trigger call on the right time and event.
01720      */
01721     ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
01722 
01723     /*
01724      * Get arguments.
01725      */
01726     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01727                                     trigdata->tg_relation, true);
01728 
01729     /*
01730      * Get the relation descriptors of the FK and PK tables and the old tuple.
01731      *
01732      * fk_rel is opened in RowExclusiveLock mode since that's what our
01733      * eventual UPDATE will get on it.
01734      */
01735     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01736     pk_rel = trigdata->tg_relation;
01737     old_row = trigdata->tg_trigtuple;
01738 
01739     switch (riinfo->confmatchtype)
01740     {
01741             /* ----------
01742              * SQL:2008 15.17 <Execution of referential actions>
01743              *  General rules 9) a) iii):
01744              *      MATCH SIMPLE/FULL
01745              *          ... ON DELETE SET DEFAULT
01746              * ----------
01747              */
01748         case FKCONSTR_MATCH_SIMPLE:
01749         case FKCONSTR_MATCH_FULL:
01750             switch (ri_NullCheck(old_row, riinfo, true))
01751             {
01752                 case RI_KEYS_ALL_NULL:
01753                 case RI_KEYS_SOME_NULL:
01754 
01755                     /*
01756                      * No check needed - there cannot be any reference to old
01757                      * key if it contains a NULL
01758                      */
01759                     heap_close(fk_rel, RowExclusiveLock);
01760                     return PointerGetDatum(NULL);
01761 
01762                 case RI_KEYS_NONE_NULL:
01763 
01764                     /*
01765                      * Have a full qualified key - continue below
01766                      */
01767                     break;
01768             }
01769 
01770             if (SPI_connect() != SPI_OK_CONNECT)
01771                 elog(ERROR, "SPI_connect failed");
01772 
01773             /*
01774              * Fetch or prepare a saved plan for the set default delete
01775              * operation
01776              */
01777             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_DEL_DOUPDATE);
01778 
01779             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01780             {
01781                 StringInfoData querybuf;
01782                 StringInfoData qualbuf;
01783                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01784                 char        attname[MAX_QUOTED_NAME_LEN];
01785                 char        paramname[16];
01786                 const char *querysep;
01787                 const char *qualsep;
01788                 Oid         queryoids[RI_MAX_NUMKEYS];
01789                 int         i;
01790 
01791                 /* ----------
01792                  * The query string built is
01793                  *  UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
01794                  *          WHERE $1 = fkatt1 [AND ...]
01795                  * The type id's for the $ parameters are those of the
01796                  * corresponding PK attributes.
01797                  * ----------
01798                  */
01799                 initStringInfo(&querybuf);
01800                 initStringInfo(&qualbuf);
01801                 quoteRelationName(fkrelname, fk_rel);
01802                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01803                 querysep = "";
01804                 qualsep = "WHERE";
01805                 for (i = 0; i < riinfo->nkeys; i++)
01806                 {
01807                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01808                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
01809 
01810                     quoteOneName(attname,
01811                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
01812                     appendStringInfo(&querybuf,
01813                                      "%s %s = DEFAULT",
01814                                      querysep, attname);
01815                     sprintf(paramname, "$%d", i + 1);
01816                     ri_GenerateQual(&qualbuf, qualsep,
01817                                     paramname, pk_type,
01818                                     riinfo->pf_eq_oprs[i],
01819                                     attname, fk_type);
01820                     querysep = ",";
01821                     qualsep = "AND";
01822                     queryoids[i] = pk_type;
01823                 }
01824                 appendStringInfoString(&querybuf, qualbuf.data);
01825 
01826                 /* Prepare and save the plan */
01827                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
01828                                      &qkey, fk_rel, pk_rel, true);
01829             }
01830 
01831             /*
01832              * We have a plan now. Run it to update the existing references.
01833              */
01834             ri_PerformCheck(riinfo, &qkey, qplan,
01835                             fk_rel, pk_rel,
01836                             old_row, NULL,
01837                             true,       /* must detect new rows */
01838                             SPI_OK_UPDATE);
01839 
01840             if (SPI_finish() != SPI_OK_FINISH)
01841                 elog(ERROR, "SPI_finish failed");
01842 
01843             heap_close(fk_rel, RowExclusiveLock);
01844 
01845             /*
01846              * If we just deleted the PK row whose key was equal to the FK
01847              * columns' default values, and a referencing row exists in the FK
01848              * table, we would have updated that row to the same values it
01849              * already had --- and RI_FKey_fk_upd_check_required would hence
01850              * believe no check is necessary.  So we need to do another lookup
01851              * now and in case a reference still exists, abort the operation.
01852              * That is already implemented in the NO ACTION trigger, so just
01853              * run it.  (This recheck is only needed in the SET DEFAULT case,
01854              * since CASCADE would remove such rows, while SET NULL is certain
01855              * to result in rows that satisfy the FK constraint.)
01856              */
01857             RI_FKey_noaction_del(fcinfo);
01858 
01859             return PointerGetDatum(NULL);
01860 
01861             /*
01862              * Handle MATCH PARTIAL set default delete.
01863              */
01864         case FKCONSTR_MATCH_PARTIAL:
01865             ereport(ERROR,
01866                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
01867                      errmsg("MATCH PARTIAL not yet implemented")));
01868             return PointerGetDatum(NULL);
01869 
01870         default:
01871             elog(ERROR, "unrecognized confmatchtype: %d",
01872                  riinfo->confmatchtype);
01873             break;
01874     }
01875 
01876     /* Never reached */
01877     return PointerGetDatum(NULL);
01878 }
01879 
01880 
01881 /* ----------
01882  * RI_FKey_setdefault_upd -
01883  *
01884  *  Set foreign key references to defaults at update event on PK table.
01885  * ----------
01886  */
01887 Datum
01888 RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
01889 {
01890     TriggerData *trigdata = (TriggerData *) fcinfo->context;
01891     const RI_ConstraintInfo *riinfo;
01892     Relation    fk_rel;
01893     Relation    pk_rel;
01894     HeapTuple   new_row;
01895     HeapTuple   old_row;
01896     RI_QueryKey qkey;
01897     SPIPlanPtr  qplan;
01898 
01899     /*
01900      * Check that this is a valid trigger call on the right time and event.
01901      */
01902     ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
01903 
01904     /*
01905      * Get arguments.
01906      */
01907     riinfo = ri_FetchConstraintInfo(trigdata->tg_trigger,
01908                                     trigdata->tg_relation, true);
01909 
01910     /*
01911      * Get the relation descriptors of the FK and PK tables and the old tuple.
01912      *
01913      * fk_rel is opened in RowExclusiveLock mode since that's what our
01914      * eventual UPDATE will get on it.
01915      */
01916     fk_rel = heap_open(riinfo->fk_relid, RowExclusiveLock);
01917     pk_rel = trigdata->tg_relation;
01918     new_row = trigdata->tg_newtuple;
01919     old_row = trigdata->tg_trigtuple;
01920 
01921     switch (riinfo->confmatchtype)
01922     {
01923             /* ----------
01924              * SQL:2008 15.17 <Execution of referential actions>
01925              *  General rules 10) a) iii):
01926              *      MATCH SIMPLE/FULL
01927              *          ... ON UPDATE SET DEFAULT
01928              * ----------
01929              */
01930         case FKCONSTR_MATCH_SIMPLE:
01931         case FKCONSTR_MATCH_FULL:
01932             switch (ri_NullCheck(old_row, riinfo, true))
01933             {
01934                 case RI_KEYS_ALL_NULL:
01935                 case RI_KEYS_SOME_NULL:
01936 
01937                     /*
01938                      * No check needed - there cannot be any reference to old
01939                      * key if it contains a NULL
01940                      */
01941                     heap_close(fk_rel, RowExclusiveLock);
01942                     return PointerGetDatum(NULL);
01943 
01944                 case RI_KEYS_NONE_NULL:
01945 
01946                     /*
01947                      * Have a full qualified key - continue below
01948                      */
01949                     break;
01950             }
01951 
01952             /*
01953              * No need to do anything if old and new keys are equal
01954              */
01955             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
01956             {
01957                 heap_close(fk_rel, RowExclusiveLock);
01958                 return PointerGetDatum(NULL);
01959             }
01960 
01961             if (SPI_connect() != SPI_OK_CONNECT)
01962                 elog(ERROR, "SPI_connect failed");
01963 
01964             /*
01965              * Fetch or prepare a saved plan for the set default update
01966              * operation
01967              */
01968             ri_BuildQueryKey(&qkey, riinfo, RI_PLAN_SETDEFAULT_UPD_DOUPDATE);
01969 
01970             if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
01971             {
01972                 StringInfoData querybuf;
01973                 StringInfoData qualbuf;
01974                 char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
01975                 char        attname[MAX_QUOTED_NAME_LEN];
01976                 char        paramname[16];
01977                 const char *querysep;
01978                 const char *qualsep;
01979                 Oid         queryoids[RI_MAX_NUMKEYS];
01980                 int         i;
01981 
01982                 /* ----------
01983                  * The query string built is
01984                  *  UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
01985                  *          WHERE $1 = fkatt1 [AND ...]
01986                  * The type id's for the $ parameters are those of the
01987                  * corresponding PK attributes.
01988                  * ----------
01989                  */
01990                 initStringInfo(&querybuf);
01991                 initStringInfo(&qualbuf);
01992                 quoteRelationName(fkrelname, fk_rel);
01993                 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
01994                 querysep = "";
01995                 qualsep = "WHERE";
01996                 for (i = 0; i < riinfo->nkeys; i++)
01997                 {
01998                     Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
01999                     Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
02000 
02001                     quoteOneName(attname,
02002                                  RIAttName(fk_rel, riinfo->fk_attnums[i]));
02003                     appendStringInfo(&querybuf,
02004                                      "%s %s = DEFAULT",
02005                                      querysep, attname);
02006                     sprintf(paramname, "$%d", i + 1);
02007                     ri_GenerateQual(&qualbuf, qualsep,
02008                                     paramname, pk_type,
02009                                     riinfo->pf_eq_oprs[i],
02010                                     attname, fk_type);
02011                     querysep = ",";
02012                     qualsep = "AND";
02013                     queryoids[i] = pk_type;
02014                 }
02015                 appendStringInfoString(&querybuf, qualbuf.data);
02016 
02017                 /* Prepare and save the plan */
02018                 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
02019                                      &qkey, fk_rel, pk_rel, true);
02020             }
02021 
02022             /*
02023              * We have a plan now. Run it to update the existing references.
02024              */
02025             ri_PerformCheck(riinfo, &qkey, qplan,
02026                             fk_rel, pk_rel,
02027                             old_row, NULL,
02028                             true,       /* must detect new rows */
02029                             SPI_OK_UPDATE);
02030 
02031             if (SPI_finish() != SPI_OK_FINISH)
02032                 elog(ERROR, "SPI_finish failed");
02033 
02034             heap_close(fk_rel, RowExclusiveLock);
02035 
02036             /*
02037              * If we just updated the PK row whose key was equal to the FK
02038              * columns' default values, and a referencing row exists in the FK
02039              * table, we would have updated that row to the same values it
02040              * already had --- and RI_FKey_fk_upd_check_required would hence
02041              * believe no check is necessary.  So we need to do another lookup
02042              * now and in case a reference still exists, abort the operation.
02043              * That is already implemented in the NO ACTION trigger, so just
02044              * run it.  (This recheck is only needed in the SET DEFAULT case,
02045              * since CASCADE must change the FK key values, while SET NULL is
02046              * certain to result in rows that satisfy the FK constraint.)
02047              */
02048             RI_FKey_noaction_upd(fcinfo);
02049 
02050             return PointerGetDatum(NULL);
02051 
02052             /*
02053              * Handle MATCH PARTIAL set default update.
02054              */
02055         case FKCONSTR_MATCH_PARTIAL:
02056             ereport(ERROR,
02057                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02058                      errmsg("MATCH PARTIAL not yet implemented")));
02059             return PointerGetDatum(NULL);
02060 
02061         default:
02062             elog(ERROR, "unrecognized confmatchtype: %d",
02063                  riinfo->confmatchtype);
02064             break;
02065     }
02066 
02067     /* Never reached */
02068     return PointerGetDatum(NULL);
02069 }
02070 
02071 
02072 /* ----------
02073  * RI_FKey_pk_upd_check_required -
02074  *
02075  *  Check if we really need to fire the RI trigger for an update to a PK
02076  *  relation.  This is called by the AFTER trigger queue manager to see if
02077  *  it can skip queuing an instance of an RI trigger.  Returns TRUE if the
02078  *  trigger must be fired, FALSE if we can prove the constraint will still
02079  *  be satisfied.
02080  * ----------
02081  */
02082 bool
02083 RI_FKey_pk_upd_check_required(Trigger *trigger, Relation pk_rel,
02084                               HeapTuple old_row, HeapTuple new_row)
02085 {
02086     const RI_ConstraintInfo *riinfo;
02087 
02088     /*
02089      * Get arguments.
02090      */
02091     riinfo = ri_FetchConstraintInfo(trigger, pk_rel, true);
02092 
02093     switch (riinfo->confmatchtype)
02094     {
02095         case FKCONSTR_MATCH_SIMPLE:
02096         case FKCONSTR_MATCH_FULL:
02097 
02098             /*
02099              * If any old key value is NULL, the row could not have been
02100              * referenced by an FK row, so no check is needed.
02101              */
02102             if (ri_NullCheck(old_row, riinfo, true) != RI_KEYS_NONE_NULL)
02103                 return false;
02104 
02105             /* If all old and new key values are equal, no check is needed */
02106             if (ri_KeysEqual(pk_rel, old_row, new_row, riinfo, true))
02107                 return false;
02108 
02109             /* Else we need to fire the trigger. */
02110             return true;
02111 
02112             /* Handle MATCH PARTIAL check. */
02113         case FKCONSTR_MATCH_PARTIAL:
02114             ereport(ERROR,
02115                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02116                      errmsg("MATCH PARTIAL not yet implemented")));
02117             break;
02118 
02119         default:
02120             elog(ERROR, "unrecognized confmatchtype: %d",
02121                  riinfo->confmatchtype);
02122             break;
02123     }
02124 
02125     /* Never reached */
02126     return false;
02127 }
02128 
02129 /* ----------
02130  * RI_FKey_fk_upd_check_required -
02131  *
02132  *  Check if we really need to fire the RI trigger for an update to an FK
02133  *  relation.  This is called by the AFTER trigger queue manager to see if
02134  *  it can skip queuing an instance of an RI trigger.  Returns TRUE if the
02135  *  trigger must be fired, FALSE if we can prove the constraint will still
02136  *  be satisfied.
02137  * ----------
02138  */
02139 bool
02140 RI_FKey_fk_upd_check_required(Trigger *trigger, Relation fk_rel,
02141                               HeapTuple old_row, HeapTuple new_row)
02142 {
02143     const RI_ConstraintInfo *riinfo;
02144 
02145     /*
02146      * Get arguments.
02147      */
02148     riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
02149 
02150     switch (riinfo->confmatchtype)
02151     {
02152         case FKCONSTR_MATCH_SIMPLE:
02153             /*
02154              * If any new key value is NULL, the row must satisfy the
02155              * constraint, so no check is needed.
02156              */
02157             if (ri_NullCheck(new_row, riinfo, false) != RI_KEYS_NONE_NULL)
02158                 return false;
02159 
02160             /*
02161              * If the original row was inserted by our own transaction, we
02162              * must fire the trigger whether or not the keys are equal.  This
02163              * is because our UPDATE will invalidate the INSERT so that the
02164              * INSERT RI trigger will not do anything; so we had better do the
02165              * UPDATE check.  (We could skip this if we knew the INSERT
02166              * trigger already fired, but there is no easy way to know that.)
02167              */
02168             if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
02169                 return true;
02170 
02171             /* If all old and new key values are equal, no check is needed */
02172             if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
02173                 return false;
02174 
02175             /* Else we need to fire the trigger. */
02176             return true;
02177 
02178         case FKCONSTR_MATCH_FULL:
02179             /*
02180              * If all new key values are NULL, the row must satisfy the
02181              * constraint, so no check is needed.  On the other hand, if only
02182              * some of them are NULL, the row must fail the constraint.  We
02183              * must not throw error here, because the row might get
02184              * invalidated before the constraint is to be checked, but we
02185              * should queue the event to apply the check later.
02186              */
02187             switch (ri_NullCheck(new_row, riinfo, false))
02188             {
02189                 case RI_KEYS_ALL_NULL:
02190                     return false;
02191                 case RI_KEYS_SOME_NULL:
02192                     return true;
02193                 case RI_KEYS_NONE_NULL:
02194                     break;      /* continue with the check */
02195             }
02196 
02197             /*
02198              * If the original row was inserted by our own transaction, we
02199              * must fire the trigger whether or not the keys are equal.  This
02200              * is because our UPDATE will invalidate the INSERT so that the
02201              * INSERT RI trigger will not do anything; so we had better do the
02202              * UPDATE check.  (We could skip this if we knew the INSERT
02203              * trigger already fired, but there is no easy way to know that.)
02204              */
02205             if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(old_row->t_data)))
02206                 return true;
02207 
02208             /* If all old and new key values are equal, no check is needed */
02209             if (ri_KeysEqual(fk_rel, old_row, new_row, riinfo, false))
02210                 return false;
02211 
02212             /* Else we need to fire the trigger. */
02213             return true;
02214 
02215             /* Handle MATCH PARTIAL check. */
02216         case FKCONSTR_MATCH_PARTIAL:
02217             ereport(ERROR,
02218                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02219                      errmsg("MATCH PARTIAL not yet implemented")));
02220             break;
02221 
02222         default:
02223             elog(ERROR, "unrecognized confmatchtype: %d",
02224                  riinfo->confmatchtype);
02225             break;
02226     }
02227 
02228     /* Never reached */
02229     return false;
02230 }
02231 
02232 /* ----------
02233  * RI_Initial_Check -
02234  *
02235  *  Check an entire table for non-matching values using a single query.
02236  *  This is not a trigger procedure, but is called during ALTER TABLE
02237  *  ADD FOREIGN KEY to validate the initial table contents.
02238  *
02239  *  We expect that the caller has made provision to prevent any problems
02240  *  caused by concurrent actions. This could be either by locking rel and
02241  *  pkrel at ShareRowExclusiveLock or higher, or by otherwise ensuring
02242  *  that triggers implementing the checks are already active.
02243  *  Hence, we do not need to lock individual rows for the check.
02244  *
02245  *  If the check fails because the current user doesn't have permissions
02246  *  to read both tables, return false to let our caller know that they will
02247  *  need to do something else to check the constraint.
02248  * ----------
02249  */
02250 bool
02251 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
02252 {
02253     const RI_ConstraintInfo *riinfo;
02254     StringInfoData querybuf;
02255     char        pkrelname[MAX_QUOTED_REL_NAME_LEN];
02256     char        fkrelname[MAX_QUOTED_REL_NAME_LEN];
02257     char        pkattname[MAX_QUOTED_NAME_LEN + 3];
02258     char        fkattname[MAX_QUOTED_NAME_LEN + 3];
02259     RangeTblEntry *pkrte;
02260     RangeTblEntry *fkrte;
02261     const char *sep;
02262     int         i;
02263     int         save_nestlevel;
02264     char        workmembuf[32];
02265     int         spi_result;
02266     SPIPlanPtr  qplan;
02267 
02268     /* Fetch constraint info. */
02269     riinfo = ri_FetchConstraintInfo(trigger, fk_rel, false);
02270 
02271     /*
02272      * Check to make sure current user has enough permissions to do the test
02273      * query.  (If not, caller can fall back to the trigger method, which
02274      * works because it changes user IDs on the fly.)
02275      *
02276      * XXX are there any other show-stopper conditions to check?
02277      */
02278     pkrte = makeNode(RangeTblEntry);
02279     pkrte->rtekind = RTE_RELATION;
02280     pkrte->relid = RelationGetRelid(pk_rel);
02281     pkrte->relkind = pk_rel->rd_rel->relkind;
02282     pkrte->requiredPerms = ACL_SELECT;
02283 
02284     fkrte = makeNode(RangeTblEntry);
02285     fkrte->rtekind = RTE_RELATION;
02286     fkrte->relid = RelationGetRelid(fk_rel);
02287     fkrte->relkind = fk_rel->rd_rel->relkind;
02288     fkrte->requiredPerms = ACL_SELECT;
02289 
02290     for (i = 0; i < riinfo->nkeys; i++)
02291     {
02292         int         attno;
02293 
02294         attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
02295         pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
02296 
02297         attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
02298         fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
02299     }
02300 
02301     if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
02302         return false;
02303 
02304     /*----------
02305      * The query string built is:
02306      *  SELECT fk.keycols FROM ONLY relname fk
02307      *   LEFT OUTER JOIN ONLY pkrelname pk
02308      *   ON (pk.pkkeycol1=fk.keycol1 [AND ...])
02309      *   WHERE pk.pkkeycol1 IS NULL AND
02310      * For MATCH SIMPLE:
02311      *   (fk.keycol1 IS NOT NULL [AND ...])
02312      * For MATCH FULL:
02313      *   (fk.keycol1 IS NOT NULL [OR ...])
02314      *
02315      * We attach COLLATE clauses to the operators when comparing columns
02316      * that have different collations.
02317      *----------
02318      */
02319     initStringInfo(&querybuf);
02320     appendStringInfo(&querybuf, "SELECT ");
02321     sep = "";
02322     for (i = 0; i < riinfo->nkeys; i++)
02323     {
02324         quoteOneName(fkattname,
02325                      RIAttName(fk_rel, riinfo->fk_attnums[i]));
02326         appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
02327         sep = ", ";
02328     }
02329 
02330     quoteRelationName(pkrelname, pk_rel);
02331     quoteRelationName(fkrelname, fk_rel);
02332     appendStringInfo(&querybuf,
02333                      " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
02334                      fkrelname, pkrelname);
02335 
02336     strcpy(pkattname, "pk.");
02337     strcpy(fkattname, "fk.");
02338     sep = "(";
02339     for (i = 0; i < riinfo->nkeys; i++)
02340     {
02341         Oid         pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
02342         Oid         fk_type = RIAttType(fk_rel, riinfo->fk_attnums[i]);
02343         Oid         pk_coll = RIAttCollation(pk_rel, riinfo->pk_attnums[i]);
02344         Oid         fk_coll = RIAttCollation(fk_rel, riinfo->fk_attnums[i]);
02345 
02346         quoteOneName(pkattname + 3,
02347                      RIAttName(pk_rel, riinfo->pk_attnums[i]));
02348         quoteOneName(fkattname + 3,
02349                      RIAttName(fk_rel, riinfo->fk_attnums[i]));
02350         ri_GenerateQual(&querybuf, sep,
02351                         pkattname, pk_type,
02352                         riinfo->pf_eq_oprs[i],
02353                         fkattname, fk_type);
02354         if (pk_coll != fk_coll)
02355             ri_GenerateQualCollation(&querybuf, pk_coll);
02356         sep = "AND";
02357     }
02358 
02359     /*
02360      * It's sufficient to test any one pk attribute for null to detect a join
02361      * failure.
02362      */
02363     quoteOneName(pkattname, RIAttName(pk_rel, riinfo->pk_attnums[0]));
02364     appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
02365 
02366     sep = "";
02367     for (i = 0; i < riinfo->nkeys; i++)
02368     {
02369         quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
02370         appendStringInfo(&querybuf,
02371                          "%sfk.%s IS NOT NULL",
02372                          sep, fkattname);
02373         switch (riinfo->confmatchtype)
02374         {
02375             case FKCONSTR_MATCH_SIMPLE:
02376                 sep = " AND ";
02377                 break;
02378             case FKCONSTR_MATCH_FULL:
02379                 sep = " OR ";
02380                 break;
02381             case FKCONSTR_MATCH_PARTIAL:
02382                 ereport(ERROR,
02383                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
02384                          errmsg("MATCH PARTIAL not yet implemented")));
02385                 break;
02386             default:
02387                 elog(ERROR, "unrecognized confmatchtype: %d",
02388                      riinfo->confmatchtype);
02389                 break;
02390         }
02391     }
02392     appendStringInfo(&querybuf, ")");
02393 
02394     /*
02395      * Temporarily increase work_mem so that the check query can be executed
02396      * more efficiently.  It seems okay to do this because the query is simple
02397      * enough to not use a multiple of work_mem, and one typically would not
02398      * have many large foreign-key validations happening concurrently.  So
02399      * this seems to meet the criteria for being considered a "maintenance"
02400      * operation, and accordingly we use maintenance_work_mem.
02401      *
02402      * We use the equivalent of a function SET option to allow the setting to
02403      * persist for exactly the duration of the check query.  guc.c also takes
02404      * care of undoing the setting on error.
02405      */
02406     save_nestlevel = NewGUCNestLevel();
02407 
02408     snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
02409     (void) set_config_option("work_mem", workmembuf,
02410                              PGC_USERSET, PGC_S_SESSION,
02411                              GUC_ACTION_SAVE, true, 0);
02412 
02413     if (SPI_connect() != SPI_OK_CONNECT)
02414         elog(ERROR, "SPI_connect failed");
02415 
02416     /*
02417      * Generate the plan.  We don't need to cache it, and there are no
02418      * arguments to the plan.
02419      */
02420     qplan = SPI_prepare(querybuf.data, 0, NULL);
02421 
02422     if (qplan == NULL)
02423         elog(ERROR, "SPI_prepare returned %d for %s",
02424              SPI_result, querybuf.data);
02425 
02426     /*
02427      * Run the plan.  For safety we force a current snapshot to be used. (In
02428      * transaction-snapshot mode, this arguably violates transaction isolation
02429      * rules, but we really haven't got much choice.) We don't need to
02430      * register the snapshot, because SPI_execute_snapshot will see to it. We
02431      * need at most one tuple returned, so pass limit = 1.
02432      */
02433     spi_result = SPI_execute_snapshot(qplan,
02434                                       NULL, NULL,
02435                                       GetLatestSnapshot(),
02436                                       InvalidSnapshot,
02437                                       true, false, 1);
02438 
02439     /* Check result */
02440     if (spi_result != SPI_OK_SELECT)
02441         elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
02442 
02443     /* Did we find a tuple violating the constraint? */
02444     if (SPI_processed > 0)
02445     {
02446         HeapTuple   tuple = SPI_tuptable->vals[0];
02447         TupleDesc   tupdesc = SPI_tuptable->tupdesc;
02448         RI_ConstraintInfo fake_riinfo;
02449 
02450         /*
02451          * The columns to look at in the result tuple are 1..N, not whatever
02452          * they are in the fk_rel.  Hack up riinfo so that the subroutines
02453          * called here will behave properly.
02454          *
02455          * In addition to this, we have to pass the correct tupdesc to
02456          * ri_ReportViolation, overriding its normal habit of using the pk_rel
02457          * or fk_rel's tupdesc.
02458          */
02459         memcpy(&fake_riinfo, riinfo, sizeof(RI_ConstraintInfo));
02460         for (i = 0; i < fake_riinfo.nkeys; i++)
02461             fake_riinfo.fk_attnums[i] = i + 1;
02462 
02463         /*
02464          * If it's MATCH FULL, and there are any nulls in the FK keys,
02465          * complain about that rather than the lack of a match.  MATCH FULL
02466          * disallows partially-null FK rows.
02467          */
02468         if (fake_riinfo.confmatchtype == FKCONSTR_MATCH_FULL &&
02469             ri_NullCheck(tuple, &fake_riinfo, false) != RI_KEYS_NONE_NULL)
02470             ereport(ERROR,
02471                     (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
02472                      errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
02473                             RelationGetRelationName(fk_rel),
02474                             NameStr(fake_riinfo.conname)),
02475                      errdetail("MATCH FULL does not allow mixing of null and nonnull key values."),
02476                      errtableconstraint(fk_rel,
02477                                         NameStr(fake_riinfo.conname))));
02478 
02479         /*
02480          * We tell ri_ReportViolation we were doing the RI_PLAN_CHECK_LOOKUPPK
02481          * query, which isn't true, but will cause it to use
02482          * fake_riinfo.fk_attnums as we need.
02483          */
02484         ri_ReportViolation(&fake_riinfo,
02485                            pk_rel, fk_rel,
02486                            tuple, tupdesc,
02487                            RI_PLAN_CHECK_LOOKUPPK, false);
02488     }
02489 
02490     if (SPI_finish() != SPI_OK_FINISH)
02491         elog(ERROR, "SPI_finish failed");
02492 
02493     /*
02494      * Restore work_mem.
02495      */
02496     AtEOXact_GUC(true, save_nestlevel);
02497 
02498     return true;
02499 }
02500 
02501 
02502 /* ----------
02503  * Local functions below
02504  * ----------
02505  */
02506 
02507 
02508 /*
02509  * quoteOneName --- safely quote a single SQL name
02510  *
02511  * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
02512  */
02513 static void
02514 quoteOneName(char *buffer, const char *name)
02515 {
02516     /* Rather than trying to be smart, just always quote it. */
02517     *buffer++ = '"';
02518     while (*name)
02519     {
02520         if (*name == '"')
02521             *buffer++ = '"';
02522         *buffer++ = *name++;
02523     }
02524     *buffer++ = '"';
02525     *buffer = '\0';
02526 }
02527 
02528 /*
02529  * quoteRelationName --- safely quote a fully qualified relation name
02530  *
02531  * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
02532  */
02533 static void
02534 quoteRelationName(char *buffer, Relation rel)
02535 {
02536     quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
02537     buffer += strlen(buffer);
02538     *buffer++ = '.';
02539     quoteOneName(buffer, RelationGetRelationName(rel));
02540 }
02541 
02542 /*
02543  * ri_GenerateQual --- generate a WHERE clause equating two variables
02544  *
02545  * The idea is to append " sep leftop op rightop" to buf.  The complexity
02546  * comes from needing to be sure that the parser will select the desired
02547  * operator.  We always name the operator using OPERATOR(schema.op) syntax
02548  * (readability isn't a big priority here), so as to avoid search-path
02549  * uncertainties.  We have to emit casts too, if either input isn't already
02550  * the input type of the operator; else we are at the mercy of the parser's
02551  * heuristics for ambiguous-operator resolution.
02552  */
02553 static void
02554 ri_GenerateQual(StringInfo buf,
02555                 const char *sep,
02556                 const char *leftop, Oid leftoptype,
02557                 Oid opoid,
02558                 const char *rightop, Oid rightoptype)
02559 {
02560     HeapTuple   opertup;
02561     Form_pg_operator operform;
02562     char       *oprname;
02563     char       *nspname;
02564 
02565     opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
02566     if (!HeapTupleIsValid(opertup))
02567         elog(ERROR, "cache lookup failed for operator %u", opoid);
02568     operform = (Form_pg_operator) GETSTRUCT(opertup);
02569     Assert(operform->oprkind == 'b');
02570     oprname = NameStr(operform->oprname);
02571 
02572     nspname = get_namespace_name(operform->oprnamespace);
02573 
02574     appendStringInfo(buf, " %s %s", sep, leftop);
02575     if (leftoptype != operform->oprleft)
02576         ri_add_cast_to(buf, operform->oprleft);
02577     appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
02578     appendStringInfoString(buf, oprname);
02579     appendStringInfo(buf, ") %s", rightop);
02580     if (rightoptype != operform->oprright)
02581         ri_add_cast_to(buf, operform->oprright);
02582 
02583     ReleaseSysCache(opertup);
02584 }
02585 
02586 /*
02587  * Add a cast specification to buf.  We spell out the type name the hard way,
02588  * intentionally not using format_type_be().  This is to avoid corner cases
02589  * for CHARACTER, BIT, and perhaps other types, where specifying the type
02590  * using SQL-standard syntax results in undesirable data truncation.  By
02591  * doing it this way we can be certain that the cast will have default (-1)
02592  * target typmod.
02593  */
02594 static void
02595 ri_add_cast_to(StringInfo buf, Oid typid)
02596 {
02597     HeapTuple   typetup;
02598     Form_pg_type typform;
02599     char       *typname;
02600     char       *nspname;
02601 
02602     typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
02603     if (!HeapTupleIsValid(typetup))
02604         elog(ERROR, "cache lookup failed for type %u", typid);
02605     typform = (Form_pg_type) GETSTRUCT(typetup);
02606 
02607     typname = NameStr(typform->typname);
02608     nspname = get_namespace_name(typform->typnamespace);
02609 
02610     appendStringInfo(buf, "::%s.%s",
02611                      quote_identifier(nspname), quote_identifier(typname));
02612 
02613     ReleaseSysCache(typetup);
02614 }
02615 
02616 /*
02617  * ri_GenerateQualCollation --- add a COLLATE spec to a WHERE clause
02618  *
02619  * At present, we intentionally do not use this function for RI queries that
02620  * compare a variable to a $n parameter.  Since parameter symbols always have
02621  * default collation, the effect will be to use the variable's collation.
02622  * Now that is only strictly correct when testing the referenced column, since
02623  * the SQL standard specifies that RI comparisons should use the referenced
02624  * column's collation.  However, so long as all collations have the same
02625  * notion of equality (which they do, because texteq reduces to bitwise
02626  * equality), there's no visible semantic impact from using the referencing
02627  * column's collation when testing it, and this is a good thing to do because
02628  * it lets us use a normal index on the referencing column.  However, we do
02629  * have to use this function when directly comparing the referencing and
02630  * referenced columns, if they are of different collations; else the parser
02631  * will fail to resolve the collation to use.
02632  */
02633 static void
02634 ri_GenerateQualCollation(StringInfo buf, Oid collation)
02635 {
02636     HeapTuple   tp;
02637     Form_pg_collation colltup;
02638     char       *collname;
02639     char        onename[MAX_QUOTED_NAME_LEN];
02640 
02641     /* Nothing to do if it's a noncollatable data type */
02642     if (!OidIsValid(collation))
02643         return;
02644 
02645     tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
02646     if (!HeapTupleIsValid(tp))
02647         elog(ERROR, "cache lookup failed for collation %u", collation);
02648     colltup = (Form_pg_collation) GETSTRUCT(tp);
02649     collname = NameStr(colltup->collname);
02650 
02651     /*
02652      * We qualify the name always, for simplicity and to ensure the query is
02653      * not search-path-dependent.
02654      */
02655     quoteOneName(onename, get_namespace_name(colltup->collnamespace));
02656     appendStringInfo(buf, " COLLATE %s", onename);
02657     quoteOneName(onename, collname);
02658     appendStringInfo(buf, ".%s", onename);
02659 
02660     ReleaseSysCache(tp);
02661 }
02662 
02663 /* ----------
02664  * ri_BuildQueryKey -
02665  *
02666  *  Construct a hashtable key for a prepared SPI plan of an FK constraint.
02667  *
02668  *      key: output argument, *key is filled in based on the other arguments
02669  *      riinfo: info from pg_constraint entry
02670  *      constr_queryno: an internal number identifying the query type
02671  *          (see RI_PLAN_XXX constants at head of file)
02672  * ----------
02673  */
02674 static void
02675 ri_BuildQueryKey(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
02676                  int32 constr_queryno)
02677 {
02678     /*
02679      * We assume struct RI_QueryKey contains no padding bytes, else we'd
02680      * need to use memset to clear them.
02681      */
02682     key->constr_id = riinfo->constraint_id;
02683     key->constr_queryno = constr_queryno;
02684 }
02685 
02686 /*
02687  * Check that RI trigger function was called in expected context
02688  */
02689 static void
02690 ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
02691 {
02692     TriggerData *trigdata = (TriggerData *) fcinfo->context;
02693 
02694     if (!CALLED_AS_TRIGGER(fcinfo))
02695         ereport(ERROR,
02696                 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02697                  errmsg("function \"%s\" was not called by trigger manager", funcname)));
02698 
02699     /*
02700      * Check proper event
02701      */
02702     if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
02703         !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
02704         ereport(ERROR,
02705                 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02706                errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
02707 
02708     switch (tgkind)
02709     {
02710         case RI_TRIGTYPE_INSERT:
02711             if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
02712                 ereport(ERROR,
02713                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02714                          errmsg("function \"%s\" must be fired for INSERT", funcname)));
02715             break;
02716         case RI_TRIGTYPE_UPDATE:
02717             if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
02718                 ereport(ERROR,
02719                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02720                          errmsg("function \"%s\" must be fired for UPDATE", funcname)));
02721             break;
02722         case RI_TRIGTYPE_DELETE:
02723             if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
02724                 ereport(ERROR,
02725                         (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
02726                          errmsg("function \"%s\" must be fired for DELETE", funcname)));
02727             break;
02728     }
02729 }
02730 
02731 
02732 /*
02733  * Fetch the RI_ConstraintInfo struct for the trigger's FK constraint.
02734  */
02735 static const RI_ConstraintInfo *
02736 ri_FetchConstraintInfo(Trigger *trigger, Relation trig_rel, bool rel_is_pk)
02737 {
02738     Oid         constraintOid = trigger->tgconstraint;
02739     const RI_ConstraintInfo *riinfo;
02740 
02741     /*
02742      * Check that the FK constraint's OID is available; it might not be if
02743      * we've been invoked via an ordinary trigger or an old-style "constraint
02744      * trigger".
02745      */
02746     if (!OidIsValid(constraintOid))
02747         ereport(ERROR,
02748                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
02749           errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
02750                  trigger->tgname, RelationGetRelationName(trig_rel)),
02751                  errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
02752 
02753     /* Find or create a hashtable entry for the constraint */
02754     riinfo = ri_LoadConstraintInfo(constraintOid);
02755 
02756     /* Do some easy cross-checks against the trigger call data */
02757     if (rel_is_pk)
02758     {
02759         if (riinfo->fk_relid != trigger->tgconstrrelid ||
02760             riinfo->pk_relid != RelationGetRelid(trig_rel))
02761             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
02762                  trigger->tgname, RelationGetRelationName(trig_rel));
02763     }
02764     else
02765     {
02766         if (riinfo->fk_relid != RelationGetRelid(trig_rel) ||
02767             riinfo->pk_relid != trigger->tgconstrrelid)
02768             elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
02769                  trigger->tgname, RelationGetRelationName(trig_rel));
02770     }
02771 
02772     return riinfo;
02773 }
02774 
02775 /*
02776  * Fetch or create the RI_ConstraintInfo struct for an FK constraint.
02777  */
02778 static const RI_ConstraintInfo *
02779 ri_LoadConstraintInfo(Oid constraintOid)
02780 {
02781     RI_ConstraintInfo *riinfo;
02782     bool        found;
02783     HeapTuple   tup;
02784     Form_pg_constraint conForm;
02785     Datum       adatum;
02786     bool        isNull;
02787     ArrayType  *arr;
02788     int         numkeys;
02789 
02790     /*
02791      * On the first call initialize the hashtable
02792      */
02793     if (!ri_constraint_cache)
02794         ri_InitHashTables();
02795 
02796     /*
02797      * Find or create a hash entry.  If we find a valid one, just return it.
02798      */
02799     riinfo = (RI_ConstraintInfo *) hash_search(ri_constraint_cache,
02800                                                (void *) &constraintOid,
02801                                                HASH_ENTER, &found);
02802     if (!found)
02803         riinfo->valid = false;
02804     else if (riinfo->valid)
02805         return riinfo;
02806 
02807     /*
02808      * Fetch the pg_constraint row so we can fill in the entry.
02809      */
02810     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
02811     if (!HeapTupleIsValid(tup)) /* should not happen */
02812         elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
02813     conForm = (Form_pg_constraint) GETSTRUCT(tup);
02814 
02815     if (conForm->contype != CONSTRAINT_FOREIGN) /* should not happen */
02816         elog(ERROR, "constraint %u is not a foreign key constraint",
02817              constraintOid);
02818 
02819     /* And extract data */
02820     Assert(riinfo->constraint_id == constraintOid);
02821     riinfo->oidHashValue = GetSysCacheHashValue1(CONSTROID,
02822                                              ObjectIdGetDatum(constraintOid));
02823     memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
02824     riinfo->pk_relid = conForm->confrelid;
02825     riinfo->fk_relid = conForm->conrelid;
02826     riinfo->confupdtype = conForm->confupdtype;
02827     riinfo->confdeltype = conForm->confdeltype;
02828     riinfo->confmatchtype = conForm->confmatchtype;
02829 
02830     /*
02831      * We expect the arrays to be 1-D arrays of the right types; verify that.
02832      * We don't need to use deconstruct_array() since the array data is just
02833      * going to look like a C array of values.
02834      */
02835     adatum = SysCacheGetAttr(CONSTROID, tup,
02836                              Anum_pg_constraint_conkey, &isNull);
02837     if (isNull)
02838         elog(ERROR, "null conkey for constraint %u", constraintOid);
02839     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
02840     if (ARR_NDIM(arr) != 1 ||
02841         ARR_HASNULL(arr) ||
02842         ARR_ELEMTYPE(arr) != INT2OID)
02843         elog(ERROR, "conkey is not a 1-D smallint array");
02844     numkeys = ARR_DIMS(arr)[0];
02845     if (numkeys <= 0 || numkeys > RI_MAX_NUMKEYS)
02846         elog(ERROR, "foreign key constraint cannot have %d columns", numkeys);
02847     riinfo->nkeys = numkeys;
02848     memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
02849     if ((Pointer) arr != DatumGetPointer(adatum))
02850         pfree(arr);             /* free de-toasted copy, if any */
02851 
02852     adatum = SysCacheGetAttr(CONSTROID, tup,
02853                              Anum_pg_constraint_confkey, &isNull);
02854     if (isNull)
02855         elog(ERROR, "null confkey for constraint %u", constraintOid);
02856     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
02857     if (ARR_NDIM(arr) != 1 ||
02858         ARR_DIMS(arr)[0] != numkeys ||
02859         ARR_HASNULL(arr) ||
02860         ARR_ELEMTYPE(arr) != INT2OID)
02861         elog(ERROR, "confkey is not a 1-D smallint array");
02862     memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
02863     if ((Pointer) arr != DatumGetPointer(adatum))
02864         pfree(arr);             /* free de-toasted copy, if any */
02865 
02866     adatum = SysCacheGetAttr(CONSTROID, tup,
02867                              Anum_pg_constraint_conpfeqop, &isNull);
02868     if (isNull)
02869         elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
02870     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
02871     /* see TryReuseForeignKey if you change the test below */
02872     if (ARR_NDIM(arr) != 1 ||
02873         ARR_DIMS(arr)[0] != numkeys ||
02874         ARR_HASNULL(arr) ||
02875         ARR_ELEMTYPE(arr) != OIDOID)
02876         elog(ERROR, "conpfeqop is not a 1-D Oid array");
02877     memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02878     if ((Pointer) arr != DatumGetPointer(adatum))
02879         pfree(arr);             /* free de-toasted copy, if any */
02880 
02881     adatum = SysCacheGetAttr(CONSTROID, tup,
02882                              Anum_pg_constraint_conppeqop, &isNull);
02883     if (isNull)
02884         elog(ERROR, "null conppeqop for constraint %u", constraintOid);
02885     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
02886     if (ARR_NDIM(arr) != 1 ||
02887         ARR_DIMS(arr)[0] != numkeys ||
02888         ARR_HASNULL(arr) ||
02889         ARR_ELEMTYPE(arr) != OIDOID)
02890         elog(ERROR, "conppeqop is not a 1-D Oid array");
02891     memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02892     if ((Pointer) arr != DatumGetPointer(adatum))
02893         pfree(arr);             /* free de-toasted copy, if any */
02894 
02895     adatum = SysCacheGetAttr(CONSTROID, tup,
02896                              Anum_pg_constraint_conffeqop, &isNull);
02897     if (isNull)
02898         elog(ERROR, "null conffeqop for constraint %u", constraintOid);
02899     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
02900     if (ARR_NDIM(arr) != 1 ||
02901         ARR_DIMS(arr)[0] != numkeys ||
02902         ARR_HASNULL(arr) ||
02903         ARR_ELEMTYPE(arr) != OIDOID)
02904         elog(ERROR, "conffeqop is not a 1-D Oid array");
02905     memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
02906     if ((Pointer) arr != DatumGetPointer(adatum))
02907         pfree(arr);             /* free de-toasted copy, if any */
02908 
02909     ReleaseSysCache(tup);
02910 
02911     riinfo->valid = true;
02912 
02913     return riinfo;
02914 }
02915 
02916 /*
02917  * Callback for pg_constraint inval events
02918  *
02919  * While most syscache callbacks just flush all their entries, pg_constraint
02920  * gets enough update traffic that it's probably worth being smarter.
02921  * Invalidate any ri_constraint_cache entry associated with the syscache
02922  * entry with the specified hash value, or all entries if hashvalue == 0.
02923  */
02924 static void
02925 InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
02926 {
02927     HASH_SEQ_STATUS status;
02928     RI_ConstraintInfo *hentry;
02929 
02930     Assert(ri_constraint_cache != NULL);
02931 
02932     hash_seq_init(&status, ri_constraint_cache);
02933     while ((hentry = (RI_ConstraintInfo *) hash_seq_search(&status)) != NULL)
02934     {
02935         if (hashvalue == 0 || hentry->oidHashValue == hashvalue)
02936             hentry->valid = false;
02937     }
02938 }
02939 
02940 
02941 /*
02942  * Prepare execution plan for a query to enforce an RI restriction
02943  *
02944  * If cache_plan is true, the plan is saved into our plan hashtable
02945  * so that we don't need to plan it again.
02946  */
02947 static SPIPlanPtr
02948 ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
02949              RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
02950              bool cache_plan)
02951 {
02952     SPIPlanPtr  qplan;
02953     Relation    query_rel;
02954     Oid         save_userid;
02955     int         save_sec_context;
02956 
02957     /*
02958      * Use the query type code to determine whether the query is run against
02959      * the PK or FK table; we'll do the check as that table's owner
02960      */
02961     if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
02962         query_rel = pk_rel;
02963     else
02964         query_rel = fk_rel;
02965 
02966     /* Switch to proper UID to perform check as */
02967     GetUserIdAndSecContext(&save_userid, &save_sec_context);
02968     SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
02969                            save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
02970 
02971     /* Create the plan */
02972     qplan = SPI_prepare(querystr, nargs, argtypes);
02973 
02974     if (qplan == NULL)
02975         elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
02976 
02977     /* Restore UID and security context */
02978     SetUserIdAndSecContext(save_userid, save_sec_context);
02979 
02980     /* Save the plan if requested */
02981     if (cache_plan)
02982     {
02983         SPI_keepplan(qplan);
02984         ri_HashPreparedPlan(qkey, qplan);
02985     }
02986 
02987     return qplan;
02988 }
02989 
02990 /*
02991  * Perform a query to enforce an RI restriction
02992  */
02993 static bool
02994 ri_PerformCheck(const RI_ConstraintInfo *riinfo,
02995                 RI_QueryKey *qkey, SPIPlanPtr qplan,
02996                 Relation fk_rel, Relation pk_rel,
02997                 HeapTuple old_tuple, HeapTuple new_tuple,
02998                 bool detectNewRows, int expect_OK)
02999 {
03000     Relation    query_rel,
03001                 source_rel;
03002     bool        source_is_pk;
03003     Snapshot    test_snapshot;
03004     Snapshot    crosscheck_snapshot;
03005     int         limit;
03006     int         spi_result;
03007     Oid         save_userid;
03008     int         save_sec_context;
03009     Datum       vals[RI_MAX_NUMKEYS * 2];
03010     char        nulls[RI_MAX_NUMKEYS * 2];
03011 
03012     /*
03013      * Use the query type code to determine whether the query is run against
03014      * the PK or FK table; we'll do the check as that table's owner
03015      */
03016     if (qkey->constr_queryno <= RI_PLAN_LAST_ON_PK)
03017         query_rel = pk_rel;
03018     else
03019         query_rel = fk_rel;
03020 
03021     /*
03022      * The values for the query are taken from the table on which the trigger
03023      * is called - it is normally the other one with respect to query_rel.
03024      * An exception is ri_Check_Pk_Match(), which uses the PK table for both
03025      * (and sets queryno to RI_PLAN_CHECK_LOOKUPPK_FROM_PK).  We might
03026      * eventually need some less klugy way to determine this.
03027      */
03028     if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK)
03029     {
03030         source_rel = fk_rel;
03031         source_is_pk = false;
03032     }
03033     else
03034     {
03035         source_rel = pk_rel;
03036         source_is_pk = true;
03037     }
03038 
03039     /* Extract the parameters to be passed into the query */
03040     if (new_tuple)
03041     {
03042         ri_ExtractValues(source_rel, new_tuple, riinfo, source_is_pk,
03043                          vals, nulls);
03044         if (old_tuple)
03045             ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
03046                              vals + riinfo->nkeys, nulls + riinfo->nkeys);
03047     }
03048     else
03049     {
03050         ri_ExtractValues(source_rel, old_tuple, riinfo, source_is_pk,
03051                          vals, nulls);
03052     }
03053 
03054     /*
03055      * In READ COMMITTED mode, we just need to use an up-to-date regular
03056      * snapshot, and we will see all rows that could be interesting. But in
03057      * transaction-snapshot mode, we can't change the transaction snapshot. If
03058      * the caller passes detectNewRows == false then it's okay to do the query
03059      * with the transaction snapshot; otherwise we use a current snapshot, and
03060      * tell the executor to error out if it finds any rows under the current
03061      * snapshot that wouldn't be visible per the transaction snapshot.  Note
03062      * that SPI_execute_snapshot will register the snapshots, so we don't need
03063      * to bother here.
03064      */
03065     if (IsolationUsesXactSnapshot() && detectNewRows)
03066     {
03067         CommandCounterIncrement();      /* be sure all my own work is visible */
03068         test_snapshot = GetLatestSnapshot();
03069         crosscheck_snapshot = GetTransactionSnapshot();
03070     }
03071     else
03072     {
03073         /* the default SPI behavior is okay */
03074         test_snapshot = InvalidSnapshot;
03075         crosscheck_snapshot = InvalidSnapshot;
03076     }
03077 
03078     /*
03079      * If this is a select query (e.g., for a 'no action' or 'restrict'
03080      * trigger), we only need to see if there is a single row in the table,
03081      * matching the key.  Otherwise, limit = 0 - because we want the query to
03082      * affect ALL the matching rows.
03083      */
03084     limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
03085 
03086     /* Switch to proper UID to perform check as */
03087     GetUserIdAndSecContext(&save_userid, &save_sec_context);
03088     SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
03089                            save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
03090 
03091     /* Finally we can run the query. */
03092     spi_result = SPI_execute_snapshot(qplan,
03093                                       vals, nulls,
03094                                       test_snapshot, crosscheck_snapshot,
03095                                       false, false, limit);
03096 
03097     /* Restore UID and security context */
03098     SetUserIdAndSecContext(save_userid, save_sec_context);
03099 
03100     /* Check result */
03101     if (spi_result < 0)
03102         elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
03103 
03104     if (expect_OK >= 0 && spi_result != expect_OK)
03105         ri_ReportViolation(riinfo,
03106                            pk_rel, fk_rel,
03107                            new_tuple ? new_tuple : old_tuple,
03108                            NULL,
03109                            qkey->constr_queryno, true);
03110 
03111     /* XXX wouldn't it be clearer to do this part at the caller? */
03112     if (qkey->constr_queryno != RI_PLAN_CHECK_LOOKUPPK_FROM_PK &&
03113         expect_OK == SPI_OK_SELECT &&
03114     (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
03115         ri_ReportViolation(riinfo,
03116                            pk_rel, fk_rel,
03117                            new_tuple ? new_tuple : old_tuple,
03118                            NULL,
03119                            qkey->constr_queryno, false);
03120 
03121     return SPI_processed != 0;
03122 }
03123 
03124 /*
03125  * Extract fields from a tuple into Datum/nulls arrays
03126  */
03127 static void
03128 ri_ExtractValues(Relation rel, HeapTuple tup,
03129                  const RI_ConstraintInfo *riinfo, bool rel_is_pk,
03130                  Datum *vals, char *nulls)
03131 {
03132     TupleDesc   tupdesc = rel->rd_att;
03133     const int16 *attnums;
03134     int         i;
03135     bool        isnull;
03136 
03137     if (rel_is_pk)
03138         attnums = riinfo->pk_attnums;
03139     else
03140         attnums = riinfo->fk_attnums;
03141 
03142     for (i = 0; i < riinfo->nkeys; i++)
03143     {
03144         vals[i] = heap_getattr(tup, attnums[i], tupdesc,
03145                                &isnull);
03146         nulls[i] = isnull ? 'n' : ' ';
03147     }
03148 }
03149 
03150 /*
03151  * Produce an error report
03152  *
03153  * If the failed constraint was on insert/update to the FK table,
03154  * we want the key names and values extracted from there, and the error
03155  * message to look like 'key blah is not present in PK'.
03156  * Otherwise, the attr names and values come from the PK table and the
03157  * message looks like 'key blah is still referenced from FK'.
03158  */
03159 static void
03160 ri_ReportViolation(const RI_ConstraintInfo *riinfo,
03161                    Relation pk_rel, Relation fk_rel,
03162                    HeapTuple violator, TupleDesc tupdesc,
03163                    int queryno, bool spi_err)
03164 {
03165     StringInfoData key_names;
03166     StringInfoData key_values;
03167     bool        onfk;
03168     const int16 *attnums;
03169     int         idx;
03170 
03171     if (spi_err)
03172         ereport(ERROR,
03173                 (errcode(ERRCODE_INTERNAL_ERROR),
03174                  errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
03175                         RelationGetRelationName(pk_rel),
03176                         NameStr(riinfo->conname),
03177                         RelationGetRelationName(fk_rel)),
03178                  errhint("This is most likely due to a rule having rewritten the query.")));
03179 
03180     /*
03181      * Determine which relation to complain about.  If tupdesc wasn't passed
03182      * by caller, assume the violator tuple came from there.
03183      */
03184     onfk = (queryno == RI_PLAN_CHECK_LOOKUPPK);
03185     if (onfk)
03186     {
03187         attnums = riinfo->fk_attnums;
03188         if (tupdesc == NULL)
03189             tupdesc = fk_rel->rd_att;
03190     }
03191     else
03192     {
03193         attnums = riinfo->pk_attnums;
03194         if (tupdesc == NULL)
03195             tupdesc = pk_rel->rd_att;
03196     }
03197 
03198     /* Get printable versions of the keys involved */
03199     initStringInfo(&key_names);
03200     initStringInfo(&key_values);
03201     for (idx = 0; idx < riinfo->nkeys; idx++)
03202     {
03203         int         fnum = attnums[idx];
03204         char       *name,
03205                    *val;
03206 
03207         name = SPI_fname(tupdesc, fnum);
03208         val = SPI_getvalue(violator, tupdesc, fnum);
03209         if (!val)
03210             val = "null";
03211 
03212         if (idx > 0)
03213         {
03214             appendStringInfoString(&key_names, ", ");
03215             appendStringInfoString(&key_values, ", ");
03216         }
03217         appendStringInfoString(&key_names, name);
03218         appendStringInfoString(&key_values, val);
03219     }
03220 
03221     if (onfk)
03222         ereport(ERROR,
03223                 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
03224                  errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
03225                         RelationGetRelationName(fk_rel),
03226                         NameStr(riinfo->conname)),
03227                  errdetail("Key (%s)=(%s) is not present in table \"%s\".",
03228                            key_names.data, key_values.data,
03229                            RelationGetRelationName(pk_rel)),
03230                  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
03231     else
03232         ereport(ERROR,
03233                 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
03234                  errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
03235                         RelationGetRelationName(pk_rel),
03236                         NameStr(riinfo->conname),
03237                         RelationGetRelationName(fk_rel)),
03238             errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
03239                       key_names.data, key_values.data,
03240                       RelationGetRelationName(fk_rel)),
03241                  errtableconstraint(fk_rel, NameStr(riinfo->conname))));
03242 }
03243 
03244 
03245 /* ----------
03246  * ri_NullCheck -
03247  *
03248  *  Determine the NULL state of all key values in a tuple
03249  *
03250  *  Returns one of RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL or RI_KEYS_SOME_NULL.
03251  * ----------
03252  */
03253 static int
03254 ri_NullCheck(HeapTuple tup,
03255              const RI_ConstraintInfo *riinfo, bool rel_is_pk)
03256 {
03257     const int16 *attnums;
03258     int         i;
03259     bool        allnull = true;
03260     bool        nonenull = true;
03261 
03262     if (rel_is_pk)
03263         attnums = riinfo->pk_attnums;
03264     else
03265         attnums = riinfo->fk_attnums;
03266 
03267     for (i = 0; i < riinfo->nkeys; i++)
03268     {
03269         if (heap_attisnull(tup, attnums[i]))
03270             nonenull = false;
03271         else
03272             allnull = false;
03273     }
03274 
03275     if (allnull)
03276         return RI_KEYS_ALL_NULL;
03277 
03278     if (nonenull)
03279         return RI_KEYS_NONE_NULL;
03280 
03281     return RI_KEYS_SOME_NULL;
03282 }
03283 
03284 
03285 /* ----------
03286  * ri_InitHashTables -
03287  *
03288  *  Initialize our internal hash tables.
03289  * ----------
03290  */
03291 static void
03292 ri_InitHashTables(void)
03293 {
03294     HASHCTL     ctl;
03295 
03296     memset(&ctl, 0, sizeof(ctl));
03297     ctl.keysize = sizeof(Oid);
03298     ctl.entrysize = sizeof(RI_ConstraintInfo);
03299     ctl.hash = oid_hash;
03300     ri_constraint_cache = hash_create("RI constraint cache",
03301                                       RI_INIT_CONSTRAINTHASHSIZE,
03302                                       &ctl, HASH_ELEM | HASH_FUNCTION);
03303 
03304     /* Arrange to flush cache on pg_constraint changes */
03305     CacheRegisterSyscacheCallback(CONSTROID,
03306                                   InvalidateConstraintCacheCallBack,
03307                                   (Datum) 0);
03308 
03309     memset(&ctl, 0, sizeof(ctl));
03310     ctl.keysize = sizeof(RI_QueryKey);
03311     ctl.entrysize = sizeof(RI_QueryHashEntry);
03312     ctl.hash = tag_hash;
03313     ri_query_cache = hash_create("RI query cache",
03314                                  RI_INIT_QUERYHASHSIZE,
03315                                  &ctl, HASH_ELEM | HASH_FUNCTION);
03316 
03317     memset(&ctl, 0, sizeof(ctl));
03318     ctl.keysize = sizeof(RI_CompareKey);
03319     ctl.entrysize = sizeof(RI_CompareHashEntry);
03320     ctl.hash = tag_hash;
03321     ri_compare_cache = hash_create("RI compare cache",
03322                                    RI_INIT_QUERYHASHSIZE,
03323                                    &ctl, HASH_ELEM | HASH_FUNCTION);
03324 }
03325 
03326 
03327 /* ----------
03328  * ri_FetchPreparedPlan -
03329  *
03330  *  Lookup for a query key in our private hash table of prepared
03331  *  and saved SPI execution plans. Return the plan if found or NULL.
03332  * ----------
03333  */
03334 static SPIPlanPtr
03335 ri_FetchPreparedPlan(RI_QueryKey *key)
03336 {
03337     RI_QueryHashEntry *entry;
03338     SPIPlanPtr  plan;
03339 
03340     /*
03341      * On the first call initialize the hashtable
03342      */
03343     if (!ri_query_cache)
03344         ri_InitHashTables();
03345 
03346     /*
03347      * Lookup for the key
03348      */
03349     entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
03350                                               (void *) key,
03351                                               HASH_FIND, NULL);
03352     if (entry == NULL)
03353         return NULL;
03354 
03355     /*
03356      * Check whether the plan is still valid.  If it isn't, we don't want to
03357      * simply rely on plancache.c to regenerate it; rather we should start
03358      * from scratch and rebuild the query text too.  This is to cover cases
03359      * such as table/column renames.  We depend on the plancache machinery to
03360      * detect possible invalidations, though.
03361      *
03362      * CAUTION: this check is only trustworthy if the caller has already
03363      * locked both FK and PK rels.
03364      */
03365     plan = entry->plan;
03366     if (plan && SPI_plan_is_valid(plan))
03367         return plan;
03368 
03369     /*
03370      * Otherwise we might as well flush the cached plan now, to free a little
03371      * memory space before we make a new one.
03372      */
03373     entry->plan = NULL;
03374     if (plan)
03375         SPI_freeplan(plan);
03376 
03377     return NULL;
03378 }
03379 
03380 
03381 /* ----------
03382  * ri_HashPreparedPlan -
03383  *
03384  *  Add another plan to our private SPI query plan hashtable.
03385  * ----------
03386  */
03387 static void
03388 ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
03389 {
03390     RI_QueryHashEntry *entry;
03391     bool        found;
03392 
03393     /*
03394      * On the first call initialize the hashtable
03395      */
03396     if (!ri_query_cache)
03397         ri_InitHashTables();
03398 
03399     /*
03400      * Add the new plan.  We might be overwriting an entry previously found
03401      * invalid by ri_FetchPreparedPlan.
03402      */
03403     entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
03404                                               (void *) key,
03405                                               HASH_ENTER, &found);
03406     Assert(!found || entry->plan == NULL);
03407     entry->plan = plan;
03408 }
03409 
03410 
03411 /* ----------
03412  * ri_KeysEqual -
03413  *
03414  *  Check if all key values in OLD and NEW are equal.
03415  *
03416  *  Note: at some point we might wish to redefine this as checking for
03417  *  "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be
03418  *  considered equal.  Currently there is no need since all callers have
03419  *  previously found at least one of the rows to contain no nulls.
03420  * ----------
03421  */
03422 static bool
03423 ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
03424              const RI_ConstraintInfo *riinfo, bool rel_is_pk)
03425 {
03426     TupleDesc   tupdesc = RelationGetDescr(rel);
03427     const int16 *attnums;
03428     const Oid  *eq_oprs;
03429     int         i;
03430 
03431     if (rel_is_pk)
03432     {
03433         attnums = riinfo->pk_attnums;
03434         eq_oprs = riinfo->pp_eq_oprs;
03435     }
03436     else
03437     {
03438         attnums = riinfo->fk_attnums;
03439         eq_oprs = riinfo->ff_eq_oprs;
03440     }
03441 
03442     for (i = 0; i < riinfo->nkeys; i++)
03443     {
03444         Datum       oldvalue;
03445         Datum       newvalue;
03446         bool        isnull;
03447 
03448         /*
03449          * Get one attribute's oldvalue. If it is NULL - they're not equal.
03450          */
03451         oldvalue = heap_getattr(oldtup, attnums[i], tupdesc, &isnull);
03452         if (isnull)
03453             return false;
03454 
03455         /*
03456          * Get one attribute's newvalue. If it is NULL - they're not equal.
03457          */
03458         newvalue = heap_getattr(newtup, attnums[i], tupdesc, &isnull);
03459         if (isnull)
03460             return false;
03461 
03462         /*
03463          * Compare them with the appropriate equality operator.
03464          */
03465         if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
03466                                 oldvalue, newvalue))
03467             return false;
03468     }
03469 
03470     return true;
03471 }
03472 
03473 
03474 /* ----------
03475  * ri_AttributesEqual -
03476  *
03477  *  Call the appropriate equality comparison operator for two values.
03478  *
03479  *  NB: we have already checked that neither value is null.
03480  * ----------
03481  */
03482 static bool
03483 ri_AttributesEqual(Oid eq_opr, Oid typeid,
03484                    Datum oldvalue, Datum newvalue)
03485 {
03486     RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
03487 
03488     /* Do we need to cast the values? */
03489     if (OidIsValid(entry->cast_func_finfo.fn_oid))
03490     {
03491         oldvalue = FunctionCall3(&entry->cast_func_finfo,
03492                                  oldvalue,
03493                                  Int32GetDatum(-1),     /* typmod */
03494                                  BoolGetDatum(false));  /* implicit coercion */
03495         newvalue = FunctionCall3(&entry->cast_func_finfo,
03496                                  newvalue,
03497                                  Int32GetDatum(-1),     /* typmod */
03498                                  BoolGetDatum(false));  /* implicit coercion */
03499     }
03500 
03501     /*
03502      * Apply the comparison operator.  We assume it doesn't care about
03503      * collations.
03504      */
03505     return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
03506                                       oldvalue, newvalue));
03507 }
03508 
03509 /* ----------
03510  * ri_HashCompareOp -
03511  *
03512  *  See if we know how to compare two values, and create a new hash entry
03513  *  if not.
03514  * ----------
03515  */
03516 static RI_CompareHashEntry *
03517 ri_HashCompareOp(Oid eq_opr, Oid typeid)
03518 {
03519     RI_CompareKey key;
03520     RI_CompareHashEntry *entry;
03521     bool        found;
03522 
03523     /*
03524      * On the first call initialize the hashtable
03525      */
03526     if (!ri_compare_cache)
03527         ri_InitHashTables();
03528 
03529     /*
03530      * Find or create a hash entry.  Note we're assuming RI_CompareKey
03531      * contains no struct padding.
03532      */
03533     key.eq_opr = eq_opr;
03534     key.typeid = typeid;
03535     entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
03536                                                 (void *) &key,
03537                                                 HASH_ENTER, &found);
03538     if (!found)
03539         entry->valid = false;
03540 
03541     /*
03542      * If not already initialized, do so.  Since we'll keep this hash entry
03543      * for the life of the backend, put any subsidiary info for the function
03544      * cache structs into TopMemoryContext.
03545      */
03546     if (!entry->valid)
03547     {
03548         Oid         lefttype,
03549                     righttype,
03550                     castfunc;
03551         CoercionPathType pathtype;
03552 
03553         /* We always need to know how to call the equality operator */
03554         fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
03555                       TopMemoryContext);
03556 
03557         /*
03558          * If we chose to use a cast from FK to PK type, we may have to apply
03559          * the cast function to get to the operator's input type.
03560          *
03561          * XXX eventually it would be good to support array-coercion cases
03562          * here and in ri_AttributesEqual().  At the moment there is no point
03563          * because cases involving nonidentical array types will be rejected
03564          * at constraint creation time.
03565          *
03566          * XXX perhaps also consider supporting CoerceViaIO?  No need at the
03567          * moment since that will never be generated for implicit coercions.
03568          */
03569         op_input_types(eq_opr, &lefttype, &righttype);
03570         Assert(lefttype == righttype);
03571         if (typeid == lefttype)
03572             castfunc = InvalidOid;      /* simplest case */
03573         else
03574         {
03575             pathtype = find_coercion_pathway(lefttype, typeid,
03576                                              COERCION_IMPLICIT,
03577                                              &castfunc);
03578             if (pathtype != COERCION_PATH_FUNC &&
03579                 pathtype != COERCION_PATH_RELABELTYPE)
03580             {
03581                 /*
03582                  * The declared input type of the eq_opr might be a
03583                  * polymorphic type such as ANYARRAY or ANYENUM, or other
03584                  * special cases such as RECORD; find_coercion_pathway
03585                  * currently doesn't subsume these special cases.
03586                  */
03587                 if (!IsPolymorphicType(lefttype) &&
03588                     !IsBinaryCoercible(typeid, lefttype))
03589                     elog(ERROR, "no conversion function from %s to %s",
03590                          format_type_be(typeid),
03591                          format_type_be(lefttype));
03592             }
03593         }
03594         if (OidIsValid(castfunc))
03595             fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
03596                           TopMemoryContext);
03597         else
03598             entry->cast_func_finfo.fn_oid = InvalidOid;
03599         entry->valid = true;
03600     }
03601 
03602     return entry;
03603 }
03604 
03605 
03606 /*
03607  * Given a trigger function OID, determine whether it is an RI trigger,
03608  * and if so whether it is attached to PK or FK relation.
03609  */
03610 int
03611 RI_FKey_trigger_type(Oid tgfoid)
03612 {
03613     switch (tgfoid)
03614     {
03615         case F_RI_FKEY_CASCADE_DEL:
03616         case F_RI_FKEY_CASCADE_UPD:
03617         case F_RI_FKEY_RESTRICT_DEL:
03618         case F_RI_FKEY_RESTRICT_UPD:
03619         case F_RI_FKEY_SETNULL_DEL:
03620         case F_RI_FKEY_SETNULL_UPD:
03621         case F_RI_FKEY_SETDEFAULT_DEL:
03622         case F_RI_FKEY_SETDEFAULT_UPD:
03623         case F_RI_FKEY_NOACTION_DEL:
03624         case F_RI_FKEY_NOACTION_UPD:
03625             return RI_TRIGGER_PK;
03626 
03627         case F_RI_FKEY_CHECK_INS:
03628         case F_RI_FKEY_CHECK_UPD:
03629             return RI_TRIGGER_FK;
03630     }
03631 
03632     return RI_TRIGGER_NONE;
03633 }