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