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