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