Fix oversight in previous error-reporting patch; mustn't pfree path string
[PostgreSQL.git] / src / backend / utils / adt / ri_triggers.c
blob9cab2675a42c5613477ae6529691fc83bf42b545
1 /* ----------
2 * ri_triggers.c
4 * Generic trigger procedures for referential integrity constraint
5 * checks.
7 * Note about memory management: the private hashtables kept here live
8 * across query and transaction boundaries, in fact they live as long as
9 * the backend does. This works because the hashtable structures
10 * themselves are allocated by dynahash.c in its permanent DynaHashCxt,
11 * and the SPI plans they point to are saved using SPI_saveplan().
12 * There is not currently any provision for throwing away a no-longer-needed
13 * plan --- consider improving this someday.
16 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
18 * $PostgreSQL$
20 * ----------
24 /* ----------
25 * Internal TODO:
27 * Add MATCH PARTIAL logic.
28 * ----------
31 #include "postgres.h"
33 #include "catalog/pg_constraint.h"
34 #include "catalog/pg_operator.h"
35 #include "commands/trigger.h"
36 #include "executor/spi.h"
37 #include "parser/parse_coerce.h"
38 #include "parser/parse_relation.h"
39 #include "miscadmin.h"
40 #include "utils/acl.h"
41 #include "utils/fmgroids.h"
42 #include "utils/lsyscache.h"
43 #include "utils/memutils.h"
44 #include "utils/snapmgr.h"
45 #include "utils/tqual.h"
48 /* ----------
49 * Local definitions
50 * ----------
53 #define RI_MAX_NUMKEYS INDEX_MAX_KEYS
55 #define RI_INIT_QUERYHASHSIZE 128
57 #define RI_KEYS_ALL_NULL 0
58 #define RI_KEYS_SOME_NULL 1
59 #define RI_KEYS_NONE_NULL 2
61 /* queryno values must be distinct for the convenience of ri_PerformCheck */
62 #define RI_PLAN_CHECK_LOOKUPPK_NOCOLS 1
63 #define RI_PLAN_CHECK_LOOKUPPK 2
64 #define RI_PLAN_CASCADE_DEL_DODELETE 3
65 #define RI_PLAN_CASCADE_UPD_DOUPDATE 4
66 #define RI_PLAN_NOACTION_DEL_CHECKREF 5
67 #define RI_PLAN_NOACTION_UPD_CHECKREF 6
68 #define RI_PLAN_RESTRICT_DEL_CHECKREF 7
69 #define RI_PLAN_RESTRICT_UPD_CHECKREF 8
70 #define RI_PLAN_SETNULL_DEL_DOUPDATE 9
71 #define RI_PLAN_SETNULL_UPD_DOUPDATE 10
73 #define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3)
74 #define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2)
76 #define RIAttName(rel, attnum) NameStr(*attnumAttName(rel, attnum))
77 #define RIAttType(rel, attnum) attnumTypeId(rel, attnum)
79 #define RI_TRIGTYPE_INSERT 1
80 #define RI_TRIGTYPE_UPDATE 2
81 #define RI_TRIGTYPE_INUP 3
82 #define RI_TRIGTYPE_DELETE 4
84 #define RI_KEYPAIR_FK_IDX 0
85 #define RI_KEYPAIR_PK_IDX 1
88 /* ----------
89 * RI_ConstraintInfo
91 * Information extracted from an FK pg_constraint entry.
92 * ----------
94 typedef struct RI_ConstraintInfo
96 Oid constraint_id; /* OID of pg_constraint entry */
97 NameData conname; /* name of the FK constraint */
98 Oid pk_relid; /* referenced relation */
99 Oid fk_relid; /* referencing relation */
100 char confupdtype; /* foreign key's ON UPDATE action */
101 char confdeltype; /* foreign key's ON DELETE action */
102 char confmatchtype; /* foreign key's match type */
103 int nkeys; /* number of key columns */
104 int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
105 int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
106 Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK =
107 * FK) */
108 Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK =
109 * PK) */
110 Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK =
111 * FK) */
112 } RI_ConstraintInfo;
115 /* ----------
116 * RI_QueryKey
118 * The key identifying a prepared SPI plan in our query hashtable
119 * ----------
121 typedef struct RI_QueryKey
123 char constr_type;
124 Oid constr_id;
125 int32 constr_queryno;
126 Oid fk_relid;
127 Oid pk_relid;
128 int32 nkeypairs;
129 int16 keypair[RI_MAX_NUMKEYS][2];
130 } RI_QueryKey;
133 /* ----------
134 * RI_QueryHashEntry
135 * ----------
137 typedef struct RI_QueryHashEntry
139 RI_QueryKey key;
140 SPIPlanPtr plan;
141 } RI_QueryHashEntry;
144 /* ----------
145 * RI_CompareKey
147 * The key identifying an entry showing how to compare two values
148 * ----------
150 typedef struct RI_CompareKey
152 Oid eq_opr; /* the equality operator to apply */
153 Oid typeid; /* the data type to apply it to */
154 } RI_CompareKey;
157 /* ----------
158 * RI_CompareHashEntry
159 * ----------
161 typedef struct RI_CompareHashEntry
163 RI_CompareKey key;
164 bool valid; /* successfully initialized? */
165 FmgrInfo eq_opr_finfo; /* call info for equality fn */
166 FmgrInfo cast_func_finfo; /* in case we must coerce input */
167 } RI_CompareHashEntry;
170 /* ----------
171 * Local data
172 * ----------
174 static HTAB *ri_query_cache = NULL;
175 static HTAB *ri_compare_cache = NULL;
178 /* ----------
179 * Local function prototypes
180 * ----------
182 static void quoteOneName(char *buffer, const char *name);
183 static void quoteRelationName(char *buffer, Relation rel);
184 static void ri_GenerateQual(StringInfo buf,
185 const char *sep,
186 const char *leftop, Oid leftoptype,
187 Oid opoid,
188 const char *rightop, Oid rightoptype);
189 static void ri_add_cast_to(StringInfo buf, Oid typid);
190 static int ri_NullCheck(Relation rel, HeapTuple tup,
191 RI_QueryKey *key, int pairidx);
192 static void ri_BuildQueryKeyFull(RI_QueryKey *key,
193 const RI_ConstraintInfo *riinfo,
194 int32 constr_queryno);
195 static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key,
196 const RI_ConstraintInfo *riinfo,
197 int32 constr_queryno);
198 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
199 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
200 static bool ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
201 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
202 static bool ri_OneKeyEqual(Relation rel, int column,
203 HeapTuple oldtup, HeapTuple newtup,
204 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
205 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
206 Datum oldvalue, Datum newvalue);
207 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
208 HeapTuple old_row,
209 const RI_ConstraintInfo *riinfo);
211 static void ri_InitHashTables(void);
212 static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
213 static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
214 static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
216 static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
217 int tgkind);
218 static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
219 Trigger *trigger, Relation trig_rel, bool rel_is_pk);
220 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
221 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
222 bool cache_plan);
223 static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
224 Relation fk_rel, Relation pk_rel,
225 HeapTuple old_tuple, HeapTuple new_tuple,
226 bool detectNewRows,
227 int expect_OK, const char *constrname);
228 static void ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
229 Relation rel, HeapTuple tuple,
230 Datum *vals, char *nulls);
231 static void ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
232 Relation pk_rel, Relation fk_rel,
233 HeapTuple violator, TupleDesc tupdesc,
234 bool spi_err);
237 /* ----------
238 * RI_FKey_check -
240 * Check foreign key existence (combined for INSERT and UPDATE).
241 * ----------
243 static Datum
244 RI_FKey_check(PG_FUNCTION_ARGS)
246 TriggerData *trigdata = (TriggerData *) fcinfo->context;
247 RI_ConstraintInfo riinfo;
248 Relation fk_rel;
249 Relation pk_rel;
250 HeapTuple new_row;
251 HeapTuple old_row;
252 Buffer new_row_buf;
253 RI_QueryKey qkey;
254 SPIPlanPtr qplan;
255 int i;
258 * Check that this is a valid trigger call on the right time and event.
260 ri_CheckTrigger(fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP);
263 * Get arguments.
265 ri_FetchConstraintInfo(&riinfo,
266 trigdata->tg_trigger, trigdata->tg_relation, false);
268 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
270 old_row = trigdata->tg_trigtuple;
271 new_row = trigdata->tg_newtuple;
272 new_row_buf = trigdata->tg_newtuplebuf;
274 else
276 old_row = NULL;
277 new_row = trigdata->tg_trigtuple;
278 new_row_buf = trigdata->tg_trigtuplebuf;
282 * We should not even consider checking the row if it is no longer valid,
283 * since it was either deleted (so the deferred check should be skipped)
284 * or updated (in which case only the latest version of the row should be
285 * checked). Test its liveness according to SnapshotSelf.
287 * NOTE: The normal coding rule is that one must acquire the buffer
288 * content lock to call HeapTupleSatisfiesVisibility. We can skip that
289 * here because we know that AfterTriggerExecute just fetched the tuple
290 * successfully, so there cannot be a VACUUM compaction in progress on the
291 * page (either heap_fetch would have waited for the VACUUM, or the
292 * VACUUM's LockBufferForCleanup would be waiting for us to drop pin). And
293 * since this is a row inserted by our open transaction, no one else can
294 * be entitled to change its xmin/xmax.
296 Assert(new_row_buf != InvalidBuffer);
297 if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
298 return PointerGetDatum(NULL);
301 * Get the relation descriptors of the FK and PK tables.
303 * pk_rel is opened in RowShareLock mode since that's what our eventual
304 * SELECT FOR SHARE will get on it.
306 fk_rel = trigdata->tg_relation;
307 pk_rel = heap_open(riinfo.pk_relid, RowShareLock);
309 /* ----------
310 * SQL3 11.9 <referential constraint definition>
311 * General rules 2) a):
312 * If Rf and Rt are empty (no columns to compare given)
313 * constraint is true if 0 < (SELECT COUNT(*) FROM T)
315 * Note: The special case that no columns are given cannot
316 * occur up to now in Postgres, it's just there for
317 * future enhancements.
318 * ----------
320 if (riinfo.nkeys == 0)
322 ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK_NOCOLS);
324 if (SPI_connect() != SPI_OK_CONNECT)
325 elog(ERROR, "SPI_connect failed");
327 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
329 char querystr[MAX_QUOTED_REL_NAME_LEN + 100];
330 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
332 /* ---------
333 * The query string built is
334 * SELECT 1 FROM ONLY <pktable>
335 * ----------
337 quoteRelationName(pkrelname, pk_rel);
338 snprintf(querystr, sizeof(querystr),
339 "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
340 pkrelname);
342 /* Prepare and save the plan */
343 qplan = ri_PlanCheck(querystr, 0, NULL,
344 &qkey, fk_rel, pk_rel, true);
348 * Execute the plan
350 ri_PerformCheck(&qkey, qplan,
351 fk_rel, pk_rel,
352 NULL, NULL,
353 false,
354 SPI_OK_SELECT,
355 NameStr(riinfo.conname));
357 if (SPI_finish() != SPI_OK_FINISH)
358 elog(ERROR, "SPI_finish failed");
360 heap_close(pk_rel, RowShareLock);
362 return PointerGetDatum(NULL);
365 if (riinfo.confmatchtype == FKCONSTR_MATCH_PARTIAL)
366 ereport(ERROR,
367 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
368 errmsg("MATCH PARTIAL not yet implemented")));
370 ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
372 switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
374 case RI_KEYS_ALL_NULL:
377 * No check - if NULLs are allowed at all is already checked by
378 * NOT NULL constraint.
380 * This is true for MATCH FULL, MATCH PARTIAL, and MATCH
381 * <unspecified>
383 heap_close(pk_rel, RowShareLock);
384 return PointerGetDatum(NULL);
386 case RI_KEYS_SOME_NULL:
389 * This is the only case that differs between the three kinds of
390 * MATCH.
392 switch (riinfo.confmatchtype)
394 case FKCONSTR_MATCH_FULL:
397 * Not allowed - MATCH FULL says either all or none of the
398 * attributes can be NULLs
400 ereport(ERROR,
401 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
402 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
403 RelationGetRelationName(trigdata->tg_relation),
404 NameStr(riinfo.conname)),
405 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
406 heap_close(pk_rel, RowShareLock);
407 return PointerGetDatum(NULL);
409 case FKCONSTR_MATCH_UNSPECIFIED:
412 * MATCH <unspecified> - if ANY column is null, we have a
413 * match.
415 heap_close(pk_rel, RowShareLock);
416 return PointerGetDatum(NULL);
418 case FKCONSTR_MATCH_PARTIAL:
421 * MATCH PARTIAL - all non-null columns must match. (not
422 * implemented, can be done by modifying the query below
423 * to only include non-null columns, or by writing a
424 * special version here)
426 ereport(ERROR,
427 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
428 errmsg("MATCH PARTIAL not yet implemented")));
429 heap_close(pk_rel, RowShareLock);
430 return PointerGetDatum(NULL);
433 case RI_KEYS_NONE_NULL:
436 * Have a full qualified key - continue below for all three kinds
437 * of MATCH.
439 break;
442 if (SPI_connect() != SPI_OK_CONNECT)
443 elog(ERROR, "SPI_connect failed");
446 * Fetch or prepare a saved plan for the real check
448 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
450 StringInfoData querybuf;
451 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
452 char attname[MAX_QUOTED_NAME_LEN];
453 char paramname[16];
454 const char *querysep;
455 Oid queryoids[RI_MAX_NUMKEYS];
457 /* ----------
458 * The query string built is
459 * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
460 * The type id's for the $ parameters are those of the
461 * corresponding FK attributes.
462 * ----------
464 initStringInfo(&querybuf);
465 quoteRelationName(pkrelname, pk_rel);
466 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
467 querysep = "WHERE";
468 for (i = 0; i < riinfo.nkeys; i++)
470 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
471 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
473 quoteOneName(attname,
474 RIAttName(pk_rel, riinfo.pk_attnums[i]));
475 sprintf(paramname, "$%d", i + 1);
476 ri_GenerateQual(&querybuf, querysep,
477 attname, pk_type,
478 riinfo.pf_eq_oprs[i],
479 paramname, fk_type);
480 querysep = "AND";
481 queryoids[i] = fk_type;
483 appendStringInfo(&querybuf, " FOR SHARE OF x");
485 /* Prepare and save the plan */
486 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
487 &qkey, fk_rel, pk_rel, true);
491 * Now check that foreign key exists in PK table
493 ri_PerformCheck(&qkey, qplan,
494 fk_rel, pk_rel,
495 NULL, new_row,
496 false,
497 SPI_OK_SELECT,
498 NameStr(riinfo.conname));
500 if (SPI_finish() != SPI_OK_FINISH)
501 elog(ERROR, "SPI_finish failed");
503 heap_close(pk_rel, RowShareLock);
505 return PointerGetDatum(NULL);
509 /* ----------
510 * RI_FKey_check_ins -
512 * Check foreign key existence at insert event on FK table.
513 * ----------
515 Datum
516 RI_FKey_check_ins(PG_FUNCTION_ARGS)
518 return RI_FKey_check(fcinfo);
522 /* ----------
523 * RI_FKey_check_upd -
525 * Check foreign key existence at update event on FK table.
526 * ----------
528 Datum
529 RI_FKey_check_upd(PG_FUNCTION_ARGS)
531 return RI_FKey_check(fcinfo);
535 /* ----------
536 * ri_Check_Pk_Match
538 * Check for matching value of old pk row in current state for
539 * noaction triggers. Returns false if no row was found and a fk row
540 * could potentially be referencing this row, true otherwise.
541 * ----------
543 static bool
544 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
545 HeapTuple old_row,
546 const RI_ConstraintInfo *riinfo)
548 SPIPlanPtr qplan;
549 RI_QueryKey qkey;
550 int i;
551 bool result;
553 ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
555 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
557 case RI_KEYS_ALL_NULL:
560 * No check - nothing could have been referencing this row anyway.
562 return true;
564 case RI_KEYS_SOME_NULL:
567 * This is the only case that differs between the three kinds of
568 * MATCH.
570 switch (riinfo->confmatchtype)
572 case FKCONSTR_MATCH_FULL:
573 case FKCONSTR_MATCH_UNSPECIFIED:
576 * MATCH <unspecified>/FULL - if ANY column is null, we
577 * can't be matching to this row already.
579 return true;
581 case FKCONSTR_MATCH_PARTIAL:
584 * MATCH PARTIAL - all non-null columns must match. (not
585 * implemented, can be done by modifying the query below
586 * to only include non-null columns, or by writing a
587 * special version here)
589 ereport(ERROR,
590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
591 errmsg("MATCH PARTIAL not yet implemented")));
592 break;
595 case RI_KEYS_NONE_NULL:
598 * Have a full qualified key - continue below for all three kinds
599 * of MATCH.
601 break;
604 if (SPI_connect() != SPI_OK_CONNECT)
605 elog(ERROR, "SPI_connect failed");
608 * Fetch or prepare a saved plan for the real check
610 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
612 StringInfoData querybuf;
613 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
614 char attname[MAX_QUOTED_NAME_LEN];
615 char paramname[16];
616 const char *querysep;
617 Oid queryoids[RI_MAX_NUMKEYS];
619 /* ----------
620 * The query string built is
621 * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
622 * The type id's for the $ parameters are those of the
623 * PK attributes themselves.
624 * ----------
626 initStringInfo(&querybuf);
627 quoteRelationName(pkrelname, pk_rel);
628 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
629 querysep = "WHERE";
630 for (i = 0; i < riinfo->nkeys; i++)
632 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
634 quoteOneName(attname,
635 RIAttName(pk_rel, riinfo->pk_attnums[i]));
636 sprintf(paramname, "$%d", i + 1);
637 ri_GenerateQual(&querybuf, querysep,
638 attname, pk_type,
639 riinfo->pp_eq_oprs[i],
640 paramname, pk_type);
641 querysep = "AND";
642 queryoids[i] = pk_type;
644 appendStringInfo(&querybuf, " FOR SHARE OF x");
646 /* Prepare and save the plan */
647 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
648 &qkey, fk_rel, pk_rel, true);
652 * We have a plan now. Run it.
654 result = ri_PerformCheck(&qkey, qplan,
655 fk_rel, pk_rel,
656 old_row, NULL,
657 true, /* treat like update */
658 SPI_OK_SELECT, NULL);
660 if (SPI_finish() != SPI_OK_FINISH)
661 elog(ERROR, "SPI_finish failed");
663 return result;
667 /* ----------
668 * RI_FKey_noaction_del -
670 * Give an error and roll back the current transaction if the
671 * delete has resulted in a violation of the given referential
672 * integrity constraint.
673 * ----------
675 Datum
676 RI_FKey_noaction_del(PG_FUNCTION_ARGS)
678 TriggerData *trigdata = (TriggerData *) fcinfo->context;
679 RI_ConstraintInfo riinfo;
680 Relation fk_rel;
681 Relation pk_rel;
682 HeapTuple old_row;
683 RI_QueryKey qkey;
684 SPIPlanPtr qplan;
685 int i;
688 * Check that this is a valid trigger call on the right time and event.
690 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
693 * Get arguments.
695 ri_FetchConstraintInfo(&riinfo,
696 trigdata->tg_trigger, trigdata->tg_relation, true);
699 * Nothing to do if no column names to compare given
701 if (riinfo.nkeys == 0)
702 return PointerGetDatum(NULL);
705 * Get the relation descriptors of the FK and PK tables and the old tuple.
707 * fk_rel is opened in RowShareLock mode since that's what our eventual
708 * SELECT FOR SHARE will get on it.
710 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
711 pk_rel = trigdata->tg_relation;
712 old_row = trigdata->tg_trigtuple;
714 if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
717 * There's either another row, or no row could match this one. In
718 * either case, we don't need to do the check.
720 heap_close(fk_rel, RowShareLock);
721 return PointerGetDatum(NULL);
724 switch (riinfo.confmatchtype)
726 /* ----------
727 * SQL3 11.9 <referential constraint definition>
728 * Gereral rules 6) a) iv):
729 * MATCH <unspecified> or MATCH FULL
730 * ... ON DELETE CASCADE
731 * ----------
733 case FKCONSTR_MATCH_UNSPECIFIED:
734 case FKCONSTR_MATCH_FULL:
735 ri_BuildQueryKeyFull(&qkey, &riinfo,
736 RI_PLAN_NOACTION_DEL_CHECKREF);
738 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
740 case RI_KEYS_ALL_NULL:
741 case RI_KEYS_SOME_NULL:
744 * No check - MATCH FULL means there cannot be any
745 * reference to old key if it contains NULL
747 heap_close(fk_rel, RowShareLock);
748 return PointerGetDatum(NULL);
750 case RI_KEYS_NONE_NULL:
753 * Have a full qualified key - continue below
755 break;
758 if (SPI_connect() != SPI_OK_CONNECT)
759 elog(ERROR, "SPI_connect failed");
762 * Fetch or prepare a saved plan for the restrict delete lookup if
763 * foreign references exist
765 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
767 StringInfoData querybuf;
768 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
769 char attname[MAX_QUOTED_NAME_LEN];
770 char paramname[16];
771 const char *querysep;
772 Oid queryoids[RI_MAX_NUMKEYS];
774 /* ----------
775 * The query string built is
776 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
777 * The type id's for the $ parameters are those of the
778 * corresponding PK attributes.
779 * ----------
781 initStringInfo(&querybuf);
782 quoteRelationName(fkrelname, fk_rel);
783 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
784 fkrelname);
785 querysep = "WHERE";
786 for (i = 0; i < riinfo.nkeys; i++)
788 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
789 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
791 quoteOneName(attname,
792 RIAttName(fk_rel, riinfo.fk_attnums[i]));
793 sprintf(paramname, "$%d", i + 1);
794 ri_GenerateQual(&querybuf, querysep,
795 paramname, pk_type,
796 riinfo.pf_eq_oprs[i],
797 attname, fk_type);
798 querysep = "AND";
799 queryoids[i] = pk_type;
801 appendStringInfo(&querybuf, " FOR SHARE OF x");
803 /* Prepare and save the plan */
804 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
805 &qkey, fk_rel, pk_rel, true);
809 * We have a plan now. Run it to check for existing references.
811 ri_PerformCheck(&qkey, qplan,
812 fk_rel, pk_rel,
813 old_row, NULL,
814 true, /* must detect new rows */
815 SPI_OK_SELECT,
816 NameStr(riinfo.conname));
818 if (SPI_finish() != SPI_OK_FINISH)
819 elog(ERROR, "SPI_finish failed");
821 heap_close(fk_rel, RowShareLock);
823 return PointerGetDatum(NULL);
826 * Handle MATCH PARTIAL restrict delete.
828 case FKCONSTR_MATCH_PARTIAL:
829 ereport(ERROR,
830 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
831 errmsg("MATCH PARTIAL not yet implemented")));
832 return PointerGetDatum(NULL);
836 * Never reached
838 elog(ERROR, "invalid confmatchtype");
839 return PointerGetDatum(NULL);
843 /* ----------
844 * RI_FKey_noaction_upd -
846 * Give an error and roll back the current transaction if the
847 * update has resulted in a violation of the given referential
848 * integrity constraint.
849 * ----------
851 Datum
852 RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
854 TriggerData *trigdata = (TriggerData *) fcinfo->context;
855 RI_ConstraintInfo riinfo;
856 Relation fk_rel;
857 Relation pk_rel;
858 HeapTuple new_row;
859 HeapTuple old_row;
860 RI_QueryKey qkey;
861 SPIPlanPtr qplan;
862 int i;
865 * Check that this is a valid trigger call on the right time and event.
867 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
870 * Get arguments.
872 ri_FetchConstraintInfo(&riinfo,
873 trigdata->tg_trigger, trigdata->tg_relation, true);
876 * Nothing to do if no column names to compare given
878 if (riinfo.nkeys == 0)
879 return PointerGetDatum(NULL);
882 * Get the relation descriptors of the FK and PK tables and the new and
883 * old tuple.
885 * fk_rel is opened in RowShareLock mode since that's what our eventual
886 * SELECT FOR SHARE will get on it.
888 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
889 pk_rel = trigdata->tg_relation;
890 new_row = trigdata->tg_newtuple;
891 old_row = trigdata->tg_trigtuple;
893 switch (riinfo.confmatchtype)
895 /* ----------
896 * SQL3 11.9 <referential constraint definition>
897 * Gereral rules 6) a) iv):
898 * MATCH <unspecified> or MATCH FULL
899 * ... ON DELETE CASCADE
900 * ----------
902 case FKCONSTR_MATCH_UNSPECIFIED:
903 case FKCONSTR_MATCH_FULL:
904 ri_BuildQueryKeyFull(&qkey, &riinfo,
905 RI_PLAN_NOACTION_UPD_CHECKREF);
907 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
909 case RI_KEYS_ALL_NULL:
910 case RI_KEYS_SOME_NULL:
913 * No check - MATCH FULL means there cannot be any
914 * reference to old key if it contains NULL
916 heap_close(fk_rel, RowShareLock);
917 return PointerGetDatum(NULL);
919 case RI_KEYS_NONE_NULL:
922 * Have a full qualified key - continue below
924 break;
928 * No need to check anything if old and new keys are equal
930 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
932 heap_close(fk_rel, RowShareLock);
933 return PointerGetDatum(NULL);
936 if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
939 * There's either another row, or no row could match this one.
940 * In either case, we don't need to do the check.
942 heap_close(fk_rel, RowShareLock);
943 return PointerGetDatum(NULL);
946 if (SPI_connect() != SPI_OK_CONNECT)
947 elog(ERROR, "SPI_connect failed");
950 * Fetch or prepare a saved plan for the noaction update lookup if
951 * foreign references exist
953 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
955 StringInfoData querybuf;
956 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
957 char attname[MAX_QUOTED_NAME_LEN];
958 char paramname[16];
959 const char *querysep;
960 Oid queryoids[RI_MAX_NUMKEYS];
962 /* ----------
963 * The query string built is
964 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
965 * The type id's for the $ parameters are those of the
966 * corresponding PK attributes.
967 * ----------
969 initStringInfo(&querybuf);
970 quoteRelationName(fkrelname, fk_rel);
971 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
972 fkrelname);
973 querysep = "WHERE";
974 for (i = 0; i < riinfo.nkeys; i++)
976 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
977 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
979 quoteOneName(attname,
980 RIAttName(fk_rel, riinfo.fk_attnums[i]));
981 sprintf(paramname, "$%d", i + 1);
982 ri_GenerateQual(&querybuf, querysep,
983 paramname, pk_type,
984 riinfo.pf_eq_oprs[i],
985 attname, fk_type);
986 querysep = "AND";
987 queryoids[i] = pk_type;
989 appendStringInfo(&querybuf, " FOR SHARE OF x");
991 /* Prepare and save the plan */
992 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
993 &qkey, fk_rel, pk_rel, true);
997 * We have a plan now. Run it to check for existing references.
999 ri_PerformCheck(&qkey, qplan,
1000 fk_rel, pk_rel,
1001 old_row, NULL,
1002 true, /* must detect new rows */
1003 SPI_OK_SELECT,
1004 NameStr(riinfo.conname));
1006 if (SPI_finish() != SPI_OK_FINISH)
1007 elog(ERROR, "SPI_finish failed");
1009 heap_close(fk_rel, RowShareLock);
1011 return PointerGetDatum(NULL);
1014 * Handle MATCH PARTIAL noaction update.
1016 case FKCONSTR_MATCH_PARTIAL:
1017 ereport(ERROR,
1018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1019 errmsg("MATCH PARTIAL not yet implemented")));
1020 return PointerGetDatum(NULL);
1024 * Never reached
1026 elog(ERROR, "invalid confmatchtype");
1027 return PointerGetDatum(NULL);
1031 /* ----------
1032 * RI_FKey_cascade_del -
1034 * Cascaded delete foreign key references at delete event on PK table.
1035 * ----------
1037 Datum
1038 RI_FKey_cascade_del(PG_FUNCTION_ARGS)
1040 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1041 RI_ConstraintInfo riinfo;
1042 Relation fk_rel;
1043 Relation pk_rel;
1044 HeapTuple old_row;
1045 RI_QueryKey qkey;
1046 SPIPlanPtr qplan;
1047 int i;
1050 * Check that this is a valid trigger call on the right time and event.
1052 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
1055 * Get arguments.
1057 ri_FetchConstraintInfo(&riinfo,
1058 trigdata->tg_trigger, trigdata->tg_relation, true);
1061 * Nothing to do if no column names to compare given
1063 if (riinfo.nkeys == 0)
1064 return PointerGetDatum(NULL);
1067 * Get the relation descriptors of the FK and PK tables and the old tuple.
1069 * fk_rel is opened in RowExclusiveLock mode since that's what our
1070 * eventual DELETE will get on it.
1072 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1073 pk_rel = trigdata->tg_relation;
1074 old_row = trigdata->tg_trigtuple;
1076 switch (riinfo.confmatchtype)
1078 /* ----------
1079 * SQL3 11.9 <referential constraint definition>
1080 * Gereral rules 6) a) i):
1081 * MATCH <unspecified> or MATCH FULL
1082 * ... ON DELETE CASCADE
1083 * ----------
1085 case FKCONSTR_MATCH_UNSPECIFIED:
1086 case FKCONSTR_MATCH_FULL:
1087 ri_BuildQueryKeyFull(&qkey, &riinfo,
1088 RI_PLAN_CASCADE_DEL_DODELETE);
1090 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1092 case RI_KEYS_ALL_NULL:
1093 case RI_KEYS_SOME_NULL:
1096 * No check - MATCH FULL means there cannot be any
1097 * reference to old key if it contains NULL
1099 heap_close(fk_rel, RowExclusiveLock);
1100 return PointerGetDatum(NULL);
1102 case RI_KEYS_NONE_NULL:
1105 * Have a full qualified key - continue below
1107 break;
1110 if (SPI_connect() != SPI_OK_CONNECT)
1111 elog(ERROR, "SPI_connect failed");
1114 * Fetch or prepare a saved plan for the cascaded delete
1116 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1118 StringInfoData querybuf;
1119 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1120 char attname[MAX_QUOTED_NAME_LEN];
1121 char paramname[16];
1122 const char *querysep;
1123 Oid queryoids[RI_MAX_NUMKEYS];
1125 /* ----------
1126 * The query string built is
1127 * DELETE FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1128 * The type id's for the $ parameters are those of the
1129 * corresponding PK attributes.
1130 * ----------
1132 initStringInfo(&querybuf);
1133 quoteRelationName(fkrelname, fk_rel);
1134 appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
1135 querysep = "WHERE";
1136 for (i = 0; i < riinfo.nkeys; i++)
1138 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1139 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1141 quoteOneName(attname,
1142 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1143 sprintf(paramname, "$%d", i + 1);
1144 ri_GenerateQual(&querybuf, querysep,
1145 paramname, pk_type,
1146 riinfo.pf_eq_oprs[i],
1147 attname, fk_type);
1148 querysep = "AND";
1149 queryoids[i] = pk_type;
1152 /* Prepare and save the plan */
1153 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1154 &qkey, fk_rel, pk_rel, true);
1158 * We have a plan now. Build up the arguments from the key values
1159 * in the deleted PK tuple and delete the referencing rows
1161 ri_PerformCheck(&qkey, qplan,
1162 fk_rel, pk_rel,
1163 old_row, NULL,
1164 true, /* must detect new rows */
1165 SPI_OK_DELETE,
1166 NameStr(riinfo.conname));
1168 if (SPI_finish() != SPI_OK_FINISH)
1169 elog(ERROR, "SPI_finish failed");
1171 heap_close(fk_rel, RowExclusiveLock);
1173 return PointerGetDatum(NULL);
1176 * Handle MATCH PARTIAL cascaded delete.
1178 case FKCONSTR_MATCH_PARTIAL:
1179 ereport(ERROR,
1180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1181 errmsg("MATCH PARTIAL not yet implemented")));
1182 return PointerGetDatum(NULL);
1186 * Never reached
1188 elog(ERROR, "invalid confmatchtype");
1189 return PointerGetDatum(NULL);
1193 /* ----------
1194 * RI_FKey_cascade_upd -
1196 * Cascaded update/delete foreign key references at update event on PK table.
1197 * ----------
1199 Datum
1200 RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
1202 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1203 RI_ConstraintInfo riinfo;
1204 Relation fk_rel;
1205 Relation pk_rel;
1206 HeapTuple new_row;
1207 HeapTuple old_row;
1208 RI_QueryKey qkey;
1209 SPIPlanPtr qplan;
1210 int i;
1211 int j;
1214 * Check that this is a valid trigger call on the right time and event.
1216 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
1219 * Get arguments.
1221 ri_FetchConstraintInfo(&riinfo,
1222 trigdata->tg_trigger, trigdata->tg_relation, true);
1225 * Nothing to do if no column names to compare given
1227 if (riinfo.nkeys == 0)
1228 return PointerGetDatum(NULL);
1231 * Get the relation descriptors of the FK and PK tables and the new and
1232 * old tuple.
1234 * fk_rel is opened in RowExclusiveLock mode since that's what our
1235 * eventual UPDATE will get on it.
1237 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1238 pk_rel = trigdata->tg_relation;
1239 new_row = trigdata->tg_newtuple;
1240 old_row = trigdata->tg_trigtuple;
1242 switch (riinfo.confmatchtype)
1244 /* ----------
1245 * SQL3 11.9 <referential constraint definition>
1246 * Gereral rules 7) a) i):
1247 * MATCH <unspecified> or MATCH FULL
1248 * ... ON UPDATE CASCADE
1249 * ----------
1251 case FKCONSTR_MATCH_UNSPECIFIED:
1252 case FKCONSTR_MATCH_FULL:
1253 ri_BuildQueryKeyFull(&qkey, &riinfo,
1254 RI_PLAN_CASCADE_UPD_DOUPDATE);
1256 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1258 case RI_KEYS_ALL_NULL:
1259 case RI_KEYS_SOME_NULL:
1262 * No update - MATCH FULL means there cannot be any
1263 * reference to old key if it contains NULL
1265 heap_close(fk_rel, RowExclusiveLock);
1266 return PointerGetDatum(NULL);
1268 case RI_KEYS_NONE_NULL:
1271 * Have a full qualified key - continue below
1273 break;
1277 * No need to do anything if old and new keys are equal
1279 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
1281 heap_close(fk_rel, RowExclusiveLock);
1282 return PointerGetDatum(NULL);
1285 if (SPI_connect() != SPI_OK_CONNECT)
1286 elog(ERROR, "SPI_connect failed");
1289 * Fetch or prepare a saved plan for the cascaded update of
1290 * foreign references
1292 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1294 StringInfoData querybuf;
1295 StringInfoData qualbuf;
1296 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1297 char attname[MAX_QUOTED_NAME_LEN];
1298 char paramname[16];
1299 const char *querysep;
1300 const char *qualsep;
1301 Oid queryoids[RI_MAX_NUMKEYS * 2];
1303 /* ----------
1304 * The query string built is
1305 * UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]
1306 * WHERE $n = fkatt1 [AND ...]
1307 * The type id's for the $ parameters are those of the
1308 * corresponding PK attributes. Note that we are assuming
1309 * there is an assignment cast from the PK to the FK type;
1310 * else the parser will fail.
1311 * ----------
1313 initStringInfo(&querybuf);
1314 initStringInfo(&qualbuf);
1315 quoteRelationName(fkrelname, fk_rel);
1316 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
1317 querysep = "";
1318 qualsep = "WHERE";
1319 for (i = 0, j = riinfo.nkeys; i < riinfo.nkeys; i++, j++)
1321 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1322 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1324 quoteOneName(attname,
1325 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1326 appendStringInfo(&querybuf,
1327 "%s %s = $%d",
1328 querysep, attname, i + 1);
1329 sprintf(paramname, "$%d", j + 1);
1330 ri_GenerateQual(&qualbuf, qualsep,
1331 paramname, pk_type,
1332 riinfo.pf_eq_oprs[i],
1333 attname, fk_type);
1334 querysep = ",";
1335 qualsep = "AND";
1336 queryoids[i] = pk_type;
1337 queryoids[j] = pk_type;
1339 appendStringInfoString(&querybuf, qualbuf.data);
1341 /* Prepare and save the plan */
1342 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys * 2, queryoids,
1343 &qkey, fk_rel, pk_rel, true);
1347 * We have a plan now. Run it to update the existing references.
1349 ri_PerformCheck(&qkey, qplan,
1350 fk_rel, pk_rel,
1351 old_row, new_row,
1352 true, /* must detect new rows */
1353 SPI_OK_UPDATE,
1354 NameStr(riinfo.conname));
1356 if (SPI_finish() != SPI_OK_FINISH)
1357 elog(ERROR, "SPI_finish failed");
1359 heap_close(fk_rel, RowExclusiveLock);
1361 return PointerGetDatum(NULL);
1364 * Handle MATCH PARTIAL cascade update.
1366 case FKCONSTR_MATCH_PARTIAL:
1367 ereport(ERROR,
1368 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1369 errmsg("MATCH PARTIAL not yet implemented")));
1370 return PointerGetDatum(NULL);
1374 * Never reached
1376 elog(ERROR, "invalid confmatchtype");
1377 return PointerGetDatum(NULL);
1381 /* ----------
1382 * RI_FKey_restrict_del -
1384 * Restrict delete from PK table to rows unreferenced by foreign key.
1386 * SQL3 intends that this referential action occur BEFORE the
1387 * update is performed, rather than after. This appears to be
1388 * the only difference between "NO ACTION" and "RESTRICT".
1390 * For now, however, we treat "RESTRICT" and "NO ACTION" as
1391 * equivalent.
1392 * ----------
1394 Datum
1395 RI_FKey_restrict_del(PG_FUNCTION_ARGS)
1397 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1398 RI_ConstraintInfo riinfo;
1399 Relation fk_rel;
1400 Relation pk_rel;
1401 HeapTuple old_row;
1402 RI_QueryKey qkey;
1403 SPIPlanPtr qplan;
1404 int i;
1407 * Check that this is a valid trigger call on the right time and event.
1409 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
1412 * Get arguments.
1414 ri_FetchConstraintInfo(&riinfo,
1415 trigdata->tg_trigger, trigdata->tg_relation, true);
1418 * Nothing to do if no column names to compare given
1420 if (riinfo.nkeys == 0)
1421 return PointerGetDatum(NULL);
1424 * Get the relation descriptors of the FK and PK tables and the old tuple.
1426 * fk_rel is opened in RowShareLock mode since that's what our eventual
1427 * SELECT FOR SHARE will get on it.
1429 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
1430 pk_rel = trigdata->tg_relation;
1431 old_row = trigdata->tg_trigtuple;
1433 switch (riinfo.confmatchtype)
1435 /* ----------
1436 * SQL3 11.9 <referential constraint definition>
1437 * Gereral rules 6) a) iv):
1438 * MATCH <unspecified> or MATCH FULL
1439 * ... ON DELETE CASCADE
1440 * ----------
1442 case FKCONSTR_MATCH_UNSPECIFIED:
1443 case FKCONSTR_MATCH_FULL:
1444 ri_BuildQueryKeyFull(&qkey, &riinfo,
1445 RI_PLAN_RESTRICT_DEL_CHECKREF);
1447 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1449 case RI_KEYS_ALL_NULL:
1450 case RI_KEYS_SOME_NULL:
1453 * No check - MATCH FULL means there cannot be any
1454 * reference to old key if it contains NULL
1456 heap_close(fk_rel, RowShareLock);
1457 return PointerGetDatum(NULL);
1459 case RI_KEYS_NONE_NULL:
1462 * Have a full qualified key - continue below
1464 break;
1467 if (SPI_connect() != SPI_OK_CONNECT)
1468 elog(ERROR, "SPI_connect failed");
1471 * Fetch or prepare a saved plan for the restrict delete lookup if
1472 * foreign references exist
1474 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1476 StringInfoData querybuf;
1477 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1478 char attname[MAX_QUOTED_NAME_LEN];
1479 char paramname[16];
1480 const char *querysep;
1481 Oid queryoids[RI_MAX_NUMKEYS];
1483 /* ----------
1484 * The query string built is
1485 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1486 * The type id's for the $ parameters are those of the
1487 * corresponding PK attributes.
1488 * ----------
1490 initStringInfo(&querybuf);
1491 quoteRelationName(fkrelname, fk_rel);
1492 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
1493 fkrelname);
1494 querysep = "WHERE";
1495 for (i = 0; i < riinfo.nkeys; i++)
1497 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1498 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1500 quoteOneName(attname,
1501 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1502 sprintf(paramname, "$%d", i + 1);
1503 ri_GenerateQual(&querybuf, querysep,
1504 paramname, pk_type,
1505 riinfo.pf_eq_oprs[i],
1506 attname, fk_type);
1507 querysep = "AND";
1508 queryoids[i] = pk_type;
1510 appendStringInfo(&querybuf, " FOR SHARE OF x");
1512 /* Prepare and save the plan */
1513 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1514 &qkey, fk_rel, pk_rel, true);
1518 * We have a plan now. Run it to check for existing references.
1520 ri_PerformCheck(&qkey, qplan,
1521 fk_rel, pk_rel,
1522 old_row, NULL,
1523 true, /* must detect new rows */
1524 SPI_OK_SELECT,
1525 NameStr(riinfo.conname));
1527 if (SPI_finish() != SPI_OK_FINISH)
1528 elog(ERROR, "SPI_finish failed");
1530 heap_close(fk_rel, RowShareLock);
1532 return PointerGetDatum(NULL);
1535 * Handle MATCH PARTIAL restrict delete.
1537 case FKCONSTR_MATCH_PARTIAL:
1538 ereport(ERROR,
1539 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1540 errmsg("MATCH PARTIAL not yet implemented")));
1541 return PointerGetDatum(NULL);
1545 * Never reached
1547 elog(ERROR, "invalid confmatchtype");
1548 return PointerGetDatum(NULL);
1552 /* ----------
1553 * RI_FKey_restrict_upd -
1555 * Restrict update of PK to rows unreferenced by foreign key.
1557 * SQL3 intends that this referential action occur BEFORE the
1558 * update is performed, rather than after. This appears to be
1559 * the only difference between "NO ACTION" and "RESTRICT".
1561 * For now, however, we treat "RESTRICT" and "NO ACTION" as
1562 * equivalent.
1563 * ----------
1565 Datum
1566 RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
1568 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1569 RI_ConstraintInfo riinfo;
1570 Relation fk_rel;
1571 Relation pk_rel;
1572 HeapTuple new_row;
1573 HeapTuple old_row;
1574 RI_QueryKey qkey;
1575 SPIPlanPtr qplan;
1576 int i;
1579 * Check that this is a valid trigger call on the right time and event.
1581 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
1584 * Get arguments.
1586 ri_FetchConstraintInfo(&riinfo,
1587 trigdata->tg_trigger, trigdata->tg_relation, true);
1590 * Nothing to do if no column names to compare given
1592 if (riinfo.nkeys == 0)
1593 return PointerGetDatum(NULL);
1596 * Get the relation descriptors of the FK and PK tables and the new and
1597 * old tuple.
1599 * fk_rel is opened in RowShareLock mode since that's what our eventual
1600 * SELECT FOR SHARE will get on it.
1602 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
1603 pk_rel = trigdata->tg_relation;
1604 new_row = trigdata->tg_newtuple;
1605 old_row = trigdata->tg_trigtuple;
1607 switch (riinfo.confmatchtype)
1609 /* ----------
1610 * SQL3 11.9 <referential constraint definition>
1611 * Gereral rules 6) a) iv):
1612 * MATCH <unspecified> or MATCH FULL
1613 * ... ON DELETE CASCADE
1614 * ----------
1616 case FKCONSTR_MATCH_UNSPECIFIED:
1617 case FKCONSTR_MATCH_FULL:
1618 ri_BuildQueryKeyFull(&qkey, &riinfo,
1619 RI_PLAN_RESTRICT_UPD_CHECKREF);
1621 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1623 case RI_KEYS_ALL_NULL:
1624 case RI_KEYS_SOME_NULL:
1627 * No check - MATCH FULL means there cannot be any
1628 * reference to old key if it contains NULL
1630 heap_close(fk_rel, RowShareLock);
1631 return PointerGetDatum(NULL);
1633 case RI_KEYS_NONE_NULL:
1636 * Have a full qualified key - continue below
1638 break;
1642 * No need to check anything if old and new keys are equal
1644 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
1646 heap_close(fk_rel, RowShareLock);
1647 return PointerGetDatum(NULL);
1650 if (SPI_connect() != SPI_OK_CONNECT)
1651 elog(ERROR, "SPI_connect failed");
1654 * Fetch or prepare a saved plan for the restrict update lookup if
1655 * foreign references exist
1657 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1659 StringInfoData querybuf;
1660 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1661 char attname[MAX_QUOTED_NAME_LEN];
1662 char paramname[16];
1663 const char *querysep;
1664 Oid queryoids[RI_MAX_NUMKEYS];
1666 /* ----------
1667 * The query string built is
1668 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1669 * The type id's for the $ parameters are those of the
1670 * corresponding PK attributes.
1671 * ----------
1673 initStringInfo(&querybuf);
1674 quoteRelationName(fkrelname, fk_rel);
1675 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
1676 fkrelname);
1677 querysep = "WHERE";
1678 for (i = 0; i < riinfo.nkeys; i++)
1680 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1681 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1683 quoteOneName(attname,
1684 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1685 sprintf(paramname, "$%d", i + 1);
1686 ri_GenerateQual(&querybuf, querysep,
1687 paramname, pk_type,
1688 riinfo.pf_eq_oprs[i],
1689 attname, fk_type);
1690 querysep = "AND";
1691 queryoids[i] = pk_type;
1693 appendStringInfo(&querybuf, " FOR SHARE OF x");
1695 /* Prepare and save the plan */
1696 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1697 &qkey, fk_rel, pk_rel, true);
1701 * We have a plan now. Run it to check for existing references.
1703 ri_PerformCheck(&qkey, qplan,
1704 fk_rel, pk_rel,
1705 old_row, NULL,
1706 true, /* must detect new rows */
1707 SPI_OK_SELECT,
1708 NameStr(riinfo.conname));
1710 if (SPI_finish() != SPI_OK_FINISH)
1711 elog(ERROR, "SPI_finish failed");
1713 heap_close(fk_rel, RowShareLock);
1715 return PointerGetDatum(NULL);
1718 * Handle MATCH PARTIAL restrict update.
1720 case FKCONSTR_MATCH_PARTIAL:
1721 ereport(ERROR,
1722 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1723 errmsg("MATCH PARTIAL not yet implemented")));
1724 return PointerGetDatum(NULL);
1728 * Never reached
1730 elog(ERROR, "invalid confmatchtype");
1731 return PointerGetDatum(NULL);
1735 /* ----------
1736 * RI_FKey_setnull_del -
1738 * Set foreign key references to NULL values at delete event on PK table.
1739 * ----------
1741 Datum
1742 RI_FKey_setnull_del(PG_FUNCTION_ARGS)
1744 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1745 RI_ConstraintInfo riinfo;
1746 Relation fk_rel;
1747 Relation pk_rel;
1748 HeapTuple old_row;
1749 RI_QueryKey qkey;
1750 SPIPlanPtr qplan;
1751 int i;
1754 * Check that this is a valid trigger call on the right time and event.
1756 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
1759 * Get arguments.
1761 ri_FetchConstraintInfo(&riinfo,
1762 trigdata->tg_trigger, trigdata->tg_relation, true);
1765 * Nothing to do if no column names to compare given
1767 if (riinfo.nkeys == 0)
1768 return PointerGetDatum(NULL);
1771 * Get the relation descriptors of the FK and PK tables and the old tuple.
1773 * fk_rel is opened in RowExclusiveLock mode since that's what our
1774 * eventual UPDATE will get on it.
1776 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1777 pk_rel = trigdata->tg_relation;
1778 old_row = trigdata->tg_trigtuple;
1780 switch (riinfo.confmatchtype)
1782 /* ----------
1783 * SQL3 11.9 <referential constraint definition>
1784 * Gereral rules 6) a) ii):
1785 * MATCH <UNSPECIFIED> or MATCH FULL
1786 * ... ON DELETE SET NULL
1787 * ----------
1789 case FKCONSTR_MATCH_UNSPECIFIED:
1790 case FKCONSTR_MATCH_FULL:
1791 ri_BuildQueryKeyFull(&qkey, &riinfo,
1792 RI_PLAN_SETNULL_DEL_DOUPDATE);
1794 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1796 case RI_KEYS_ALL_NULL:
1797 case RI_KEYS_SOME_NULL:
1800 * No update - MATCH FULL means there cannot be any
1801 * reference to old key if it contains NULL
1803 heap_close(fk_rel, RowExclusiveLock);
1804 return PointerGetDatum(NULL);
1806 case RI_KEYS_NONE_NULL:
1809 * Have a full qualified key - continue below
1811 break;
1814 if (SPI_connect() != SPI_OK_CONNECT)
1815 elog(ERROR, "SPI_connect failed");
1818 * Fetch or prepare a saved plan for the set null delete operation
1820 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1822 StringInfoData querybuf;
1823 StringInfoData qualbuf;
1824 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1825 char attname[MAX_QUOTED_NAME_LEN];
1826 char paramname[16];
1827 const char *querysep;
1828 const char *qualsep;
1829 Oid queryoids[RI_MAX_NUMKEYS];
1831 /* ----------
1832 * The query string built is
1833 * UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
1834 * WHERE $1 = fkatt1 [AND ...]
1835 * The type id's for the $ parameters are those of the
1836 * corresponding PK attributes.
1837 * ----------
1839 initStringInfo(&querybuf);
1840 initStringInfo(&qualbuf);
1841 quoteRelationName(fkrelname, fk_rel);
1842 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
1843 querysep = "";
1844 qualsep = "WHERE";
1845 for (i = 0; i < riinfo.nkeys; i++)
1847 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1848 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1850 quoteOneName(attname,
1851 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1852 appendStringInfo(&querybuf,
1853 "%s %s = NULL",
1854 querysep, attname);
1855 sprintf(paramname, "$%d", i + 1);
1856 ri_GenerateQual(&qualbuf, qualsep,
1857 paramname, pk_type,
1858 riinfo.pf_eq_oprs[i],
1859 attname, fk_type);
1860 querysep = ",";
1861 qualsep = "AND";
1862 queryoids[i] = pk_type;
1864 appendStringInfoString(&querybuf, qualbuf.data);
1866 /* Prepare and save the plan */
1867 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1868 &qkey, fk_rel, pk_rel, true);
1872 * We have a plan now. Run it to check for existing references.
1874 ri_PerformCheck(&qkey, qplan,
1875 fk_rel, pk_rel,
1876 old_row, NULL,
1877 true, /* must detect new rows */
1878 SPI_OK_UPDATE,
1879 NameStr(riinfo.conname));
1881 if (SPI_finish() != SPI_OK_FINISH)
1882 elog(ERROR, "SPI_finish failed");
1884 heap_close(fk_rel, RowExclusiveLock);
1886 return PointerGetDatum(NULL);
1889 * Handle MATCH PARTIAL set null delete.
1891 case FKCONSTR_MATCH_PARTIAL:
1892 ereport(ERROR,
1893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1894 errmsg("MATCH PARTIAL not yet implemented")));
1895 return PointerGetDatum(NULL);
1899 * Never reached
1901 elog(ERROR, "invalid confmatchtype");
1902 return PointerGetDatum(NULL);
1906 /* ----------
1907 * RI_FKey_setnull_upd -
1909 * Set foreign key references to NULL at update event on PK table.
1910 * ----------
1912 Datum
1913 RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
1915 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1916 RI_ConstraintInfo riinfo;
1917 Relation fk_rel;
1918 Relation pk_rel;
1919 HeapTuple new_row;
1920 HeapTuple old_row;
1921 RI_QueryKey qkey;
1922 SPIPlanPtr qplan;
1923 int i;
1924 bool use_cached_query;
1927 * Check that this is a valid trigger call on the right time and event.
1929 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
1932 * Get arguments.
1934 ri_FetchConstraintInfo(&riinfo,
1935 trigdata->tg_trigger, trigdata->tg_relation, true);
1938 * Nothing to do if no column names to compare given
1940 if (riinfo.nkeys == 0)
1941 return PointerGetDatum(NULL);
1944 * Get the relation descriptors of the FK and PK tables and the old tuple.
1946 * fk_rel is opened in RowExclusiveLock mode since that's what our
1947 * eventual UPDATE will get on it.
1949 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1950 pk_rel = trigdata->tg_relation;
1951 new_row = trigdata->tg_newtuple;
1952 old_row = trigdata->tg_trigtuple;
1954 switch (riinfo.confmatchtype)
1956 /* ----------
1957 * SQL3 11.9 <referential constraint definition>
1958 * Gereral rules 7) a) ii) 2):
1959 * MATCH FULL
1960 * ... ON UPDATE SET NULL
1961 * ----------
1963 case FKCONSTR_MATCH_UNSPECIFIED:
1964 case FKCONSTR_MATCH_FULL:
1965 ri_BuildQueryKeyFull(&qkey, &riinfo,
1966 RI_PLAN_SETNULL_UPD_DOUPDATE);
1968 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1970 case RI_KEYS_ALL_NULL:
1971 case RI_KEYS_SOME_NULL:
1974 * No update - MATCH FULL means there cannot be any
1975 * reference to old key if it contains NULL
1977 heap_close(fk_rel, RowExclusiveLock);
1978 return PointerGetDatum(NULL);
1980 case RI_KEYS_NONE_NULL:
1983 * Have a full qualified key - continue below
1985 break;
1989 * No need to do anything if old and new keys are equal
1991 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
1993 heap_close(fk_rel, RowExclusiveLock);
1994 return PointerGetDatum(NULL);
1997 if (SPI_connect() != SPI_OK_CONNECT)
1998 elog(ERROR, "SPI_connect failed");
2001 * "MATCH <unspecified>" only changes columns corresponding to the
2002 * referenced columns that have changed in pk_rel. This means the
2003 * "SET attrn=NULL [, attrn=NULL]" string will be change as well.
2004 * In this case, we need to build a temporary plan rather than use
2005 * our cached plan, unless the update happens to change all
2006 * columns in the key. Fortunately, for the most common case of a
2007 * single-column foreign key, this will be true.
2009 * In case you're wondering, the inequality check works because we
2010 * know that the old key value has no NULLs (see above).
2013 use_cached_query = (riinfo.confmatchtype == FKCONSTR_MATCH_FULL) ||
2014 ri_AllKeysUnequal(pk_rel, old_row, new_row,
2015 &riinfo, true);
2018 * Fetch or prepare a saved plan for the set null update operation
2019 * if possible, or build a temporary plan if not.
2021 if (!use_cached_query ||
2022 (qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
2024 StringInfoData querybuf;
2025 StringInfoData qualbuf;
2026 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2027 char attname[MAX_QUOTED_NAME_LEN];
2028 char paramname[16];
2029 const char *querysep;
2030 const char *qualsep;
2031 Oid queryoids[RI_MAX_NUMKEYS];
2033 /* ----------
2034 * The query string built is
2035 * UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
2036 * WHERE $1 = fkatt1 [AND ...]
2037 * The type id's for the $ parameters are those of the
2038 * corresponding PK attributes.
2039 * ----------
2041 initStringInfo(&querybuf);
2042 initStringInfo(&qualbuf);
2043 quoteRelationName(fkrelname, fk_rel);
2044 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2045 querysep = "";
2046 qualsep = "WHERE";
2047 for (i = 0; i < riinfo.nkeys; i++)
2049 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2050 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2052 quoteOneName(attname,
2053 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2056 * MATCH <unspecified> - only change columns corresponding
2057 * to changed columns in pk_rel's key
2059 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
2060 !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
2061 &riinfo, true))
2063 appendStringInfo(&querybuf,
2064 "%s %s = NULL",
2065 querysep, attname);
2066 querysep = ",";
2068 sprintf(paramname, "$%d", i + 1);
2069 ri_GenerateQual(&qualbuf, qualsep,
2070 paramname, pk_type,
2071 riinfo.pf_eq_oprs[i],
2072 attname, fk_type);
2073 qualsep = "AND";
2074 queryoids[i] = pk_type;
2076 appendStringInfoString(&querybuf, qualbuf.data);
2079 * Prepare the plan. Save it only if we're building the
2080 * "standard" plan.
2082 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2083 &qkey, fk_rel, pk_rel,
2084 use_cached_query);
2088 * We have a plan now. Run it to update the existing references.
2090 ri_PerformCheck(&qkey, qplan,
2091 fk_rel, pk_rel,
2092 old_row, NULL,
2093 true, /* must detect new rows */
2094 SPI_OK_UPDATE,
2095 NameStr(riinfo.conname));
2097 if (SPI_finish() != SPI_OK_FINISH)
2098 elog(ERROR, "SPI_finish failed");
2100 heap_close(fk_rel, RowExclusiveLock);
2102 return PointerGetDatum(NULL);
2105 * Handle MATCH PARTIAL set null update.
2107 case FKCONSTR_MATCH_PARTIAL:
2108 ereport(ERROR,
2109 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2110 errmsg("MATCH PARTIAL not yet implemented")));
2111 return PointerGetDatum(NULL);
2115 * Never reached
2117 elog(ERROR, "invalid confmatchtype");
2118 return PointerGetDatum(NULL);
2122 /* ----------
2123 * RI_FKey_setdefault_del -
2125 * Set foreign key references to defaults at delete event on PK table.
2126 * ----------
2128 Datum
2129 RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
2131 TriggerData *trigdata = (TriggerData *) fcinfo->context;
2132 RI_ConstraintInfo riinfo;
2133 Relation fk_rel;
2134 Relation pk_rel;
2135 HeapTuple old_row;
2136 RI_QueryKey qkey;
2137 SPIPlanPtr qplan;
2140 * Check that this is a valid trigger call on the right time and event.
2142 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
2145 * Get arguments.
2147 ri_FetchConstraintInfo(&riinfo,
2148 trigdata->tg_trigger, trigdata->tg_relation, true);
2151 * Nothing to do if no column names to compare given
2153 if (riinfo.nkeys == 0)
2154 return PointerGetDatum(NULL);
2157 * Get the relation descriptors of the FK and PK tables and the old tuple.
2159 * fk_rel is opened in RowExclusiveLock mode since that's what our
2160 * eventual UPDATE will get on it.
2162 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
2163 pk_rel = trigdata->tg_relation;
2164 old_row = trigdata->tg_trigtuple;
2166 switch (riinfo.confmatchtype)
2168 /* ----------
2169 * SQL3 11.9 <referential constraint definition>
2170 * Gereral rules 6) a) iii):
2171 * MATCH <UNSPECIFIED> or MATCH FULL
2172 * ... ON DELETE SET DEFAULT
2173 * ----------
2175 case FKCONSTR_MATCH_UNSPECIFIED:
2176 case FKCONSTR_MATCH_FULL:
2177 ri_BuildQueryKeyFull(&qkey, &riinfo,
2178 RI_PLAN_SETNULL_DEL_DOUPDATE);
2180 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
2182 case RI_KEYS_ALL_NULL:
2183 case RI_KEYS_SOME_NULL:
2186 * No update - MATCH FULL means there cannot be any
2187 * reference to old key if it contains NULL
2189 heap_close(fk_rel, RowExclusiveLock);
2190 return PointerGetDatum(NULL);
2192 case RI_KEYS_NONE_NULL:
2195 * Have a full qualified key - continue below
2197 break;
2200 if (SPI_connect() != SPI_OK_CONNECT)
2201 elog(ERROR, "SPI_connect failed");
2204 * Prepare a plan for the set default delete operation.
2205 * Unfortunately we need to do it on every invocation because the
2206 * default value could potentially change between calls.
2209 StringInfoData querybuf;
2210 StringInfoData qualbuf;
2211 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2212 char attname[MAX_QUOTED_NAME_LEN];
2213 char paramname[16];
2214 const char *querysep;
2215 const char *qualsep;
2216 Oid queryoids[RI_MAX_NUMKEYS];
2217 int i;
2219 /* ----------
2220 * The query string built is
2221 * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
2222 * WHERE $1 = fkatt1 [AND ...]
2223 * The type id's for the $ parameters are those of the
2224 * corresponding PK attributes.
2225 * ----------
2227 initStringInfo(&querybuf);
2228 initStringInfo(&qualbuf);
2229 quoteRelationName(fkrelname, fk_rel);
2230 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2231 querysep = "";
2232 qualsep = "WHERE";
2233 for (i = 0; i < riinfo.nkeys; i++)
2235 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2236 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2238 quoteOneName(attname,
2239 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2240 appendStringInfo(&querybuf,
2241 "%s %s = DEFAULT",
2242 querysep, attname);
2243 sprintf(paramname, "$%d", i + 1);
2244 ri_GenerateQual(&qualbuf, qualsep,
2245 paramname, pk_type,
2246 riinfo.pf_eq_oprs[i],
2247 attname, fk_type);
2248 querysep = ",";
2249 qualsep = "AND";
2250 queryoids[i] = pk_type;
2252 appendStringInfoString(&querybuf, qualbuf.data);
2254 /* Prepare the plan, don't save it */
2255 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2256 &qkey, fk_rel, pk_rel, false);
2260 * We have a plan now. Run it to update the existing references.
2262 ri_PerformCheck(&qkey, qplan,
2263 fk_rel, pk_rel,
2264 old_row, NULL,
2265 true, /* must detect new rows */
2266 SPI_OK_UPDATE,
2267 NameStr(riinfo.conname));
2269 if (SPI_finish() != SPI_OK_FINISH)
2270 elog(ERROR, "SPI_finish failed");
2272 heap_close(fk_rel, RowExclusiveLock);
2275 * In the case we delete the row who's key is equal to the default
2276 * values AND a referencing row in the foreign key table exists,
2277 * we would just have updated it to the same values. We need to do
2278 * another lookup now and in case a reference exists, abort the
2279 * operation. That is already implemented in the NO ACTION
2280 * trigger.
2282 RI_FKey_noaction_del(fcinfo);
2284 return PointerGetDatum(NULL);
2287 * Handle MATCH PARTIAL set null delete.
2289 case FKCONSTR_MATCH_PARTIAL:
2290 ereport(ERROR,
2291 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2292 errmsg("MATCH PARTIAL not yet implemented")));
2293 return PointerGetDatum(NULL);
2297 * Never reached
2299 elog(ERROR, "invalid confmatchtype");
2300 return PointerGetDatum(NULL);
2304 /* ----------
2305 * RI_FKey_setdefault_upd -
2307 * Set foreign key references to defaults at update event on PK table.
2308 * ----------
2310 Datum
2311 RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
2313 TriggerData *trigdata = (TriggerData *) fcinfo->context;
2314 RI_ConstraintInfo riinfo;
2315 Relation fk_rel;
2316 Relation pk_rel;
2317 HeapTuple new_row;
2318 HeapTuple old_row;
2319 RI_QueryKey qkey;
2320 SPIPlanPtr qplan;
2323 * Check that this is a valid trigger call on the right time and event.
2325 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
2328 * Get arguments.
2330 ri_FetchConstraintInfo(&riinfo,
2331 trigdata->tg_trigger, trigdata->tg_relation, true);
2334 * Nothing to do if no column names to compare given
2336 if (riinfo.nkeys == 0)
2337 return PointerGetDatum(NULL);
2340 * Get the relation descriptors of the FK and PK tables and the old tuple.
2342 * fk_rel is opened in RowExclusiveLock mode since that's what our
2343 * eventual UPDATE will get on it.
2345 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
2346 pk_rel = trigdata->tg_relation;
2347 new_row = trigdata->tg_newtuple;
2348 old_row = trigdata->tg_trigtuple;
2350 switch (riinfo.confmatchtype)
2352 /* ----------
2353 * SQL3 11.9 <referential constraint definition>
2354 * Gereral rules 7) a) iii):
2355 * MATCH <UNSPECIFIED> or MATCH FULL
2356 * ... ON UPDATE SET DEFAULT
2357 * ----------
2359 case FKCONSTR_MATCH_UNSPECIFIED:
2360 case FKCONSTR_MATCH_FULL:
2361 ri_BuildQueryKeyFull(&qkey, &riinfo,
2362 RI_PLAN_SETNULL_DEL_DOUPDATE);
2364 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
2366 case RI_KEYS_ALL_NULL:
2367 case RI_KEYS_SOME_NULL:
2370 * No update - MATCH FULL means there cannot be any
2371 * reference to old key if it contains NULL
2373 heap_close(fk_rel, RowExclusiveLock);
2374 return PointerGetDatum(NULL);
2376 case RI_KEYS_NONE_NULL:
2379 * Have a full qualified key - continue below
2381 break;
2385 * No need to do anything if old and new keys are equal
2387 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
2389 heap_close(fk_rel, RowExclusiveLock);
2390 return PointerGetDatum(NULL);
2393 if (SPI_connect() != SPI_OK_CONNECT)
2394 elog(ERROR, "SPI_connect failed");
2397 * Prepare a plan for the set default delete operation.
2398 * Unfortunately we need to do it on every invocation because the
2399 * default value could potentially change between calls.
2402 StringInfoData querybuf;
2403 StringInfoData qualbuf;
2404 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2405 char attname[MAX_QUOTED_NAME_LEN];
2406 char paramname[16];
2407 const char *querysep;
2408 const char *qualsep;
2409 Oid queryoids[RI_MAX_NUMKEYS];
2410 int i;
2412 /* ----------
2413 * The query string built is
2414 * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
2415 * WHERE $1 = fkatt1 [AND ...]
2416 * The type id's for the $ parameters are those of the
2417 * corresponding PK attributes.
2418 * ----------
2420 initStringInfo(&querybuf);
2421 initStringInfo(&qualbuf);
2422 quoteRelationName(fkrelname, fk_rel);
2423 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2424 querysep = "";
2425 qualsep = "WHERE";
2426 for (i = 0; i < riinfo.nkeys; i++)
2428 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2429 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2431 quoteOneName(attname,
2432 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2435 * MATCH <unspecified> - only change columns corresponding
2436 * to changed columns in pk_rel's key
2438 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
2439 !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
2440 &riinfo, true))
2442 appendStringInfo(&querybuf,
2443 "%s %s = DEFAULT",
2444 querysep, attname);
2445 querysep = ",";
2447 sprintf(paramname, "$%d", i + 1);
2448 ri_GenerateQual(&qualbuf, qualsep,
2449 paramname, pk_type,
2450 riinfo.pf_eq_oprs[i],
2451 attname, fk_type);
2452 qualsep = "AND";
2453 queryoids[i] = pk_type;
2455 appendStringInfoString(&querybuf, qualbuf.data);
2457 /* Prepare the plan, don't save it */
2458 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2459 &qkey, fk_rel, pk_rel, false);
2463 * We have a plan now. Run it to update the existing references.
2465 ri_PerformCheck(&qkey, qplan,
2466 fk_rel, pk_rel,
2467 old_row, NULL,
2468 true, /* must detect new rows */
2469 SPI_OK_UPDATE,
2470 NameStr(riinfo.conname));
2472 if (SPI_finish() != SPI_OK_FINISH)
2473 elog(ERROR, "SPI_finish failed");
2475 heap_close(fk_rel, RowExclusiveLock);
2478 * In the case we updated the row who's key was equal to the
2479 * default values AND a referencing row in the foreign key table
2480 * exists, we would just have updated it to the same values. We
2481 * need to do another lookup now and in case a reference exists,
2482 * abort the operation. That is already implemented in the NO
2483 * ACTION trigger.
2485 RI_FKey_noaction_upd(fcinfo);
2487 return PointerGetDatum(NULL);
2490 * Handle MATCH PARTIAL set null delete.
2492 case FKCONSTR_MATCH_PARTIAL:
2493 ereport(ERROR,
2494 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2495 errmsg("MATCH PARTIAL not yet implemented")));
2496 return PointerGetDatum(NULL);
2500 * Never reached
2502 elog(ERROR, "invalid confmatchtype");
2503 return PointerGetDatum(NULL);
2507 /* ----------
2508 * RI_FKey_keyequal_upd_pk -
2510 * Check if we have a key change on an update to a PK relation. This is
2511 * used by the AFTER trigger queue manager to see if it can skip queuing
2512 * an instance of an RI trigger.
2513 * ----------
2515 bool
2516 RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
2517 HeapTuple old_row, HeapTuple new_row)
2519 RI_ConstraintInfo riinfo;
2522 * Get arguments.
2524 ri_FetchConstraintInfo(&riinfo, trigger, pk_rel, true);
2527 * Nothing to do if no column names to compare given
2529 if (riinfo.nkeys == 0)
2530 return true;
2532 switch (riinfo.confmatchtype)
2534 case FKCONSTR_MATCH_UNSPECIFIED:
2535 case FKCONSTR_MATCH_FULL:
2536 /* Return true if keys are equal */
2537 return ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true);
2539 /* Handle MATCH PARTIAL set null delete. */
2540 case FKCONSTR_MATCH_PARTIAL:
2541 ereport(ERROR,
2542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2543 errmsg("MATCH PARTIAL not yet implemented")));
2544 break;
2547 /* Never reached */
2548 elog(ERROR, "invalid confmatchtype");
2549 return false;
2552 /* ----------
2553 * RI_FKey_keyequal_upd_fk -
2555 * Check if we have a key change on an update to an FK relation. This is
2556 * used by the AFTER trigger queue manager to see if it can skip queuing
2557 * an instance of an RI trigger.
2558 * ----------
2560 bool
2561 RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
2562 HeapTuple old_row, HeapTuple new_row)
2564 RI_ConstraintInfo riinfo;
2567 * Get arguments.
2569 ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
2572 * Nothing to do if no column names to compare given
2574 if (riinfo.nkeys == 0)
2575 return true;
2577 switch (riinfo.confmatchtype)
2579 case FKCONSTR_MATCH_UNSPECIFIED:
2580 case FKCONSTR_MATCH_FULL:
2581 /* Return true if keys are equal */
2582 return ri_KeysEqual(fk_rel, old_row, new_row, &riinfo, false);
2584 /* Handle MATCH PARTIAL set null delete. */
2585 case FKCONSTR_MATCH_PARTIAL:
2586 ereport(ERROR,
2587 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2588 errmsg("MATCH PARTIAL not yet implemented")));
2589 break;
2592 /* Never reached */
2593 elog(ERROR, "invalid confmatchtype");
2594 return false;
2597 /* ----------
2598 * RI_Initial_Check -
2600 * Check an entire table for non-matching values using a single query.
2601 * This is not a trigger procedure, but is called during ALTER TABLE
2602 * ADD FOREIGN KEY to validate the initial table contents.
2604 * We expect that an exclusive lock has been taken on rel and pkrel;
2605 * hence, we do not need to lock individual rows for the check.
2607 * If the check fails because the current user doesn't have permissions
2608 * to read both tables, return false to let our caller know that they will
2609 * need to do something else to check the constraint.
2610 * ----------
2612 bool
2613 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
2615 RI_ConstraintInfo riinfo;
2616 const char *constrname = trigger->tgname;
2617 StringInfoData querybuf;
2618 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
2619 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2620 char pkattname[MAX_QUOTED_NAME_LEN + 3];
2621 char fkattname[MAX_QUOTED_NAME_LEN + 3];
2622 const char *sep;
2623 int i;
2624 int old_work_mem;
2625 char workmembuf[32];
2626 int spi_result;
2627 SPIPlanPtr qplan;
2630 * Check to make sure current user has enough permissions to do the test
2631 * query. (If not, caller can fall back to the trigger method, which
2632 * works because it changes user IDs on the fly.)
2634 * XXX are there any other show-stopper conditions to check?
2636 if (pg_class_aclcheck(RelationGetRelid(fk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
2637 return false;
2638 if (pg_class_aclcheck(RelationGetRelid(pk_rel), GetUserId(), ACL_SELECT) != ACLCHECK_OK)
2639 return false;
2641 ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
2643 /*----------
2644 * The query string built is:
2645 * SELECT fk.keycols FROM ONLY relname fk
2646 * LEFT OUTER JOIN ONLY pkrelname pk
2647 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
2648 * WHERE pk.pkkeycol1 IS NULL AND
2649 * For MATCH unspecified:
2650 * (fk.keycol1 IS NOT NULL [AND ...])
2651 * For MATCH FULL:
2652 * (fk.keycol1 IS NOT NULL [OR ...])
2653 *----------
2655 initStringInfo(&querybuf);
2656 appendStringInfo(&querybuf, "SELECT ");
2657 sep = "";
2658 for (i = 0; i < riinfo.nkeys; i++)
2660 quoteOneName(fkattname,
2661 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2662 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
2663 sep = ", ";
2666 quoteRelationName(pkrelname, pk_rel);
2667 quoteRelationName(fkrelname, fk_rel);
2668 appendStringInfo(&querybuf,
2669 " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
2670 fkrelname, pkrelname);
2672 strcpy(pkattname, "pk.");
2673 strcpy(fkattname, "fk.");
2674 sep = "(";
2675 for (i = 0; i < riinfo.nkeys; i++)
2677 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2678 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2680 quoteOneName(pkattname + 3,
2681 RIAttName(pk_rel, riinfo.pk_attnums[i]));
2682 quoteOneName(fkattname + 3,
2683 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2684 ri_GenerateQual(&querybuf, sep,
2685 pkattname, pk_type,
2686 riinfo.pf_eq_oprs[i],
2687 fkattname, fk_type);
2688 sep = "AND";
2692 * It's sufficient to test any one pk attribute for null to detect a join
2693 * failure.
2695 quoteOneName(pkattname, RIAttName(pk_rel, riinfo.pk_attnums[0]));
2696 appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
2698 sep = "";
2699 for (i = 0; i < riinfo.nkeys; i++)
2701 quoteOneName(fkattname, RIAttName(fk_rel, riinfo.fk_attnums[i]));
2702 appendStringInfo(&querybuf,
2703 "%sfk.%s IS NOT NULL",
2704 sep, fkattname);
2705 switch (riinfo.confmatchtype)
2707 case FKCONSTR_MATCH_UNSPECIFIED:
2708 sep = " AND ";
2709 break;
2710 case FKCONSTR_MATCH_FULL:
2711 sep = " OR ";
2712 break;
2713 case FKCONSTR_MATCH_PARTIAL:
2714 ereport(ERROR,
2715 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2716 errmsg("MATCH PARTIAL not yet implemented")));
2717 break;
2718 default:
2719 elog(ERROR, "unrecognized match type: %d",
2720 riinfo.confmatchtype);
2721 break;
2724 appendStringInfo(&querybuf, ")");
2727 * Temporarily increase work_mem so that the check query can be executed
2728 * more efficiently. It seems okay to do this because the query is simple
2729 * enough to not use a multiple of work_mem, and one typically would not
2730 * have many large foreign-key validations happening concurrently. So
2731 * this seems to meet the criteria for being considered a "maintenance"
2732 * operation, and accordingly we use maintenance_work_mem.
2734 * We do the equivalent of "SET LOCAL work_mem" so that transaction abort
2735 * will restore the old value if we lose control due to an error.
2737 old_work_mem = work_mem;
2738 snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
2739 (void) set_config_option("work_mem", workmembuf,
2740 PGC_USERSET, PGC_S_SESSION,
2741 GUC_ACTION_LOCAL, true);
2743 if (SPI_connect() != SPI_OK_CONNECT)
2744 elog(ERROR, "SPI_connect failed");
2747 * Generate the plan. We don't need to cache it, and there are no
2748 * arguments to the plan.
2750 qplan = SPI_prepare(querybuf.data, 0, NULL);
2752 if (qplan == NULL)
2753 elog(ERROR, "SPI_prepare returned %d for %s",
2754 SPI_result, querybuf.data);
2757 * Run the plan. For safety we force a current snapshot to be used. (In
2758 * serializable mode, this arguably violates serializability, but we
2759 * really haven't got much choice.) We don't need to register the
2760 * snapshot, because SPI_execute_snapshot will see to it. We need at most
2761 * one tuple returned, so pass limit = 1.
2763 spi_result = SPI_execute_snapshot(qplan,
2764 NULL, NULL,
2765 GetLatestSnapshot(),
2766 InvalidSnapshot,
2767 true, false, 1);
2769 /* Check result */
2770 if (spi_result != SPI_OK_SELECT)
2771 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
2773 /* Did we find a tuple violating the constraint? */
2774 if (SPI_processed > 0)
2776 HeapTuple tuple = SPI_tuptable->vals[0];
2777 TupleDesc tupdesc = SPI_tuptable->tupdesc;
2778 RI_QueryKey qkey;
2781 * If it's MATCH FULL, and there are any nulls in the FK keys,
2782 * complain about that rather than the lack of a match. MATCH FULL
2783 * disallows partially-null FK rows.
2785 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL)
2787 bool isnull = false;
2789 for (i = 1; i <= riinfo.nkeys; i++)
2791 (void) SPI_getbinval(tuple, tupdesc, i, &isnull);
2792 if (isnull)
2793 break;
2795 if (isnull)
2796 ereport(ERROR,
2797 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
2798 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
2799 RelationGetRelationName(fk_rel),
2800 constrname),
2801 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
2805 * Although we didn't cache the query, we need to set up a fake query
2806 * key to pass to ri_ReportViolation.
2808 MemSet(&qkey, 0, sizeof(qkey));
2809 qkey.constr_queryno = RI_PLAN_CHECK_LOOKUPPK;
2810 qkey.nkeypairs = riinfo.nkeys;
2811 for (i = 0; i < riinfo.nkeys; i++)
2812 qkey.keypair[i][RI_KEYPAIR_FK_IDX] = i + 1;
2814 ri_ReportViolation(&qkey, constrname,
2815 pk_rel, fk_rel,
2816 tuple, tupdesc,
2817 false);
2820 if (SPI_finish() != SPI_OK_FINISH)
2821 elog(ERROR, "SPI_finish failed");
2824 * Restore work_mem for the remainder of the current transaction. This is
2825 * another SET LOCAL, so it won't affect the session value.
2827 snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem);
2828 (void) set_config_option("work_mem", workmembuf,
2829 PGC_USERSET, PGC_S_SESSION,
2830 GUC_ACTION_LOCAL, true);
2832 return true;
2836 /* ----------
2837 * Local functions below
2838 * ----------
2843 * quoteOneName --- safely quote a single SQL name
2845 * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
2847 static void
2848 quoteOneName(char *buffer, const char *name)
2850 /* Rather than trying to be smart, just always quote it. */
2851 *buffer++ = '"';
2852 while (*name)
2854 if (*name == '"')
2855 *buffer++ = '"';
2856 *buffer++ = *name++;
2858 *buffer++ = '"';
2859 *buffer = '\0';
2863 * quoteRelationName --- safely quote a fully qualified relation name
2865 * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
2867 static void
2868 quoteRelationName(char *buffer, Relation rel)
2870 quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
2871 buffer += strlen(buffer);
2872 *buffer++ = '.';
2873 quoteOneName(buffer, RelationGetRelationName(rel));
2877 * ri_GenerateQual --- generate a WHERE clause equating two variables
2879 * The idea is to append " sep leftop op rightop" to buf. The complexity
2880 * comes from needing to be sure that the parser will select the desired
2881 * operator. We always name the operator using OPERATOR(schema.op) syntax
2882 * (readability isn't a big priority here), so as to avoid search-path
2883 * uncertainties. We have to emit casts too, if either input isn't already
2884 * the input type of the operator; else we are at the mercy of the parser's
2885 * heuristics for ambiguous-operator resolution.
2887 static void
2888 ri_GenerateQual(StringInfo buf,
2889 const char *sep,
2890 const char *leftop, Oid leftoptype,
2891 Oid opoid,
2892 const char *rightop, Oid rightoptype)
2894 HeapTuple opertup;
2895 Form_pg_operator operform;
2896 char *oprname;
2897 char *nspname;
2899 opertup = SearchSysCache(OPEROID,
2900 ObjectIdGetDatum(opoid),
2901 0, 0, 0);
2902 if (!HeapTupleIsValid(opertup))
2903 elog(ERROR, "cache lookup failed for operator %u", opoid);
2904 operform = (Form_pg_operator) GETSTRUCT(opertup);
2905 Assert(operform->oprkind == 'b');
2906 oprname = NameStr(operform->oprname);
2908 nspname = get_namespace_name(operform->oprnamespace);
2910 appendStringInfo(buf, " %s %s", sep, leftop);
2911 if (leftoptype != operform->oprleft)
2912 ri_add_cast_to(buf, operform->oprleft);
2913 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
2914 appendStringInfoString(buf, oprname);
2915 appendStringInfo(buf, ") %s", rightop);
2916 if (rightoptype != operform->oprright)
2917 ri_add_cast_to(buf, operform->oprright);
2919 ReleaseSysCache(opertup);
2923 * Add a cast specification to buf. We spell out the type name the hard way,
2924 * intentionally not using format_type_be(). This is to avoid corner cases
2925 * for CHARACTER, BIT, and perhaps other types, where specifying the type
2926 * using SQL-standard syntax results in undesirable data truncation. By
2927 * doing it this way we can be certain that the cast will have default (-1)
2928 * target typmod.
2930 static void
2931 ri_add_cast_to(StringInfo buf, Oid typid)
2933 HeapTuple typetup;
2934 Form_pg_type typform;
2935 char *typname;
2936 char *nspname;
2938 typetup = SearchSysCache(TYPEOID,
2939 ObjectIdGetDatum(typid),
2940 0, 0, 0);
2941 if (!HeapTupleIsValid(typetup))
2942 elog(ERROR, "cache lookup failed for type %u", typid);
2943 typform = (Form_pg_type) GETSTRUCT(typetup);
2945 typname = NameStr(typform->typname);
2946 nspname = get_namespace_name(typform->typnamespace);
2948 appendStringInfo(buf, "::%s.%s",
2949 quote_identifier(nspname), quote_identifier(typname));
2951 ReleaseSysCache(typetup);
2954 /* ----------
2955 * ri_BuildQueryKeyFull -
2957 * Build up a new hashtable key for a prepared SPI plan of a
2958 * constraint trigger of MATCH FULL.
2960 * key: output argument, *key is filled in based on the other arguments
2961 * riinfo: info from pg_constraint entry
2962 * constr_queryno: an internal number of the query inside the proc
2964 * At least for MATCH FULL this builds a unique key per plan.
2965 * ----------
2967 static void
2968 ri_BuildQueryKeyFull(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
2969 int32 constr_queryno)
2971 int i;
2973 MemSet(key, 0, sizeof(RI_QueryKey));
2974 key->constr_type = FKCONSTR_MATCH_FULL;
2975 key->constr_id = riinfo->constraint_id;
2976 key->constr_queryno = constr_queryno;
2977 key->fk_relid = riinfo->fk_relid;
2978 key->pk_relid = riinfo->pk_relid;
2979 key->nkeypairs = riinfo->nkeys;
2980 for (i = 0; i < riinfo->nkeys; i++)
2982 key->keypair[i][RI_KEYPAIR_FK_IDX] = riinfo->fk_attnums[i];
2983 key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
2988 * Check that RI trigger function was called in expected context
2990 static void
2991 ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
2993 TriggerData *trigdata = (TriggerData *) fcinfo->context;
2995 if (!CALLED_AS_TRIGGER(fcinfo))
2996 ereport(ERROR,
2997 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
2998 errmsg("function \"%s\" was not called by trigger manager", funcname)));
3001 * Check proper event
3003 if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
3004 !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
3005 ereport(ERROR,
3006 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3007 errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
3009 switch (tgkind)
3011 case RI_TRIGTYPE_INSERT:
3012 if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
3013 ereport(ERROR,
3014 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3015 errmsg("function \"%s\" must be fired for INSERT", funcname)));
3016 break;
3017 case RI_TRIGTYPE_UPDATE:
3018 if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
3019 ereport(ERROR,
3020 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3021 errmsg("function \"%s\" must be fired for UPDATE", funcname)));
3022 break;
3023 case RI_TRIGTYPE_INUP:
3024 if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
3025 !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
3026 ereport(ERROR,
3027 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3028 errmsg("function \"%s\" must be fired for INSERT or UPDATE",
3029 funcname)));
3030 break;
3031 case RI_TRIGTYPE_DELETE:
3032 if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
3033 ereport(ERROR,
3034 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3035 errmsg("function \"%s\" must be fired for DELETE", funcname)));
3036 break;
3042 * Fetch the pg_constraint entry for the FK constraint, and fill *riinfo
3044 static void
3045 ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
3046 Trigger *trigger, Relation trig_rel, bool rel_is_pk)
3048 Oid constraintOid = trigger->tgconstraint;
3049 HeapTuple tup;
3050 Form_pg_constraint conForm;
3051 Datum adatum;
3052 bool isNull;
3053 ArrayType *arr;
3054 int numkeys;
3057 * Check that the FK constraint's OID is available; it might not be if
3058 * we've been invoked via an ordinary trigger or an old-style "constraint
3059 * trigger".
3061 if (!OidIsValid(constraintOid))
3062 ereport(ERROR,
3063 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3064 errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
3065 trigger->tgname, RelationGetRelationName(trig_rel)),
3066 errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
3068 /* OK, fetch the tuple */
3069 tup = SearchSysCache(CONSTROID,
3070 ObjectIdGetDatum(constraintOid),
3071 0, 0, 0);
3072 if (!HeapTupleIsValid(tup)) /* should not happen */
3073 elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
3074 conForm = (Form_pg_constraint) GETSTRUCT(tup);
3076 /* Do some easy cross-checks against the trigger call data */
3077 if (rel_is_pk)
3079 if (conForm->contype != CONSTRAINT_FOREIGN ||
3080 conForm->conrelid != trigger->tgconstrrelid ||
3081 conForm->confrelid != RelationGetRelid(trig_rel))
3082 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
3083 trigger->tgname, RelationGetRelationName(trig_rel));
3085 else
3087 if (conForm->contype != CONSTRAINT_FOREIGN ||
3088 conForm->conrelid != RelationGetRelid(trig_rel) ||
3089 conForm->confrelid != trigger->tgconstrrelid)
3090 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
3091 trigger->tgname, RelationGetRelationName(trig_rel));
3094 /* And extract data */
3095 riinfo->constraint_id = constraintOid;
3096 memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
3097 riinfo->pk_relid = conForm->confrelid;
3098 riinfo->fk_relid = conForm->conrelid;
3099 riinfo->confupdtype = conForm->confupdtype;
3100 riinfo->confdeltype = conForm->confdeltype;
3101 riinfo->confmatchtype = conForm->confmatchtype;
3104 * We expect the arrays to be 1-D arrays of the right types; verify that.
3105 * We don't need to use deconstruct_array() since the array data is just
3106 * going to look like a C array of values.
3108 adatum = SysCacheGetAttr(CONSTROID, tup,
3109 Anum_pg_constraint_conkey, &isNull);
3110 if (isNull)
3111 elog(ERROR, "null conkey for constraint %u", constraintOid);
3112 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3113 numkeys = ARR_DIMS(arr)[0];
3114 if (ARR_NDIM(arr) != 1 ||
3115 numkeys < 0 ||
3116 numkeys > RI_MAX_NUMKEYS ||
3117 ARR_HASNULL(arr) ||
3118 ARR_ELEMTYPE(arr) != INT2OID)
3119 elog(ERROR, "conkey is not a 1-D smallint array");
3120 riinfo->nkeys = numkeys;
3121 memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
3122 if ((Pointer) arr != DatumGetPointer(adatum))
3123 pfree(arr); /* free de-toasted copy, if any */
3125 adatum = SysCacheGetAttr(CONSTROID, tup,
3126 Anum_pg_constraint_confkey, &isNull);
3127 if (isNull)
3128 elog(ERROR, "null confkey for constraint %u", constraintOid);
3129 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3130 numkeys = ARR_DIMS(arr)[0];
3131 if (ARR_NDIM(arr) != 1 ||
3132 numkeys != riinfo->nkeys ||
3133 numkeys > RI_MAX_NUMKEYS ||
3134 ARR_HASNULL(arr) ||
3135 ARR_ELEMTYPE(arr) != INT2OID)
3136 elog(ERROR, "confkey is not a 1-D smallint array");
3137 memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
3138 if ((Pointer) arr != DatumGetPointer(adatum))
3139 pfree(arr); /* free de-toasted copy, if any */
3141 adatum = SysCacheGetAttr(CONSTROID, tup,
3142 Anum_pg_constraint_conpfeqop, &isNull);
3143 if (isNull)
3144 elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
3145 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3146 numkeys = ARR_DIMS(arr)[0];
3147 if (ARR_NDIM(arr) != 1 ||
3148 numkeys != riinfo->nkeys ||
3149 numkeys > RI_MAX_NUMKEYS ||
3150 ARR_HASNULL(arr) ||
3151 ARR_ELEMTYPE(arr) != OIDOID)
3152 elog(ERROR, "conpfeqop is not a 1-D Oid array");
3153 memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3154 if ((Pointer) arr != DatumGetPointer(adatum))
3155 pfree(arr); /* free de-toasted copy, if any */
3157 adatum = SysCacheGetAttr(CONSTROID, tup,
3158 Anum_pg_constraint_conppeqop, &isNull);
3159 if (isNull)
3160 elog(ERROR, "null conppeqop for constraint %u", constraintOid);
3161 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3162 numkeys = ARR_DIMS(arr)[0];
3163 if (ARR_NDIM(arr) != 1 ||
3164 numkeys != riinfo->nkeys ||
3165 numkeys > RI_MAX_NUMKEYS ||
3166 ARR_HASNULL(arr) ||
3167 ARR_ELEMTYPE(arr) != OIDOID)
3168 elog(ERROR, "conppeqop is not a 1-D Oid array");
3169 memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3170 if ((Pointer) arr != DatumGetPointer(adatum))
3171 pfree(arr); /* free de-toasted copy, if any */
3173 adatum = SysCacheGetAttr(CONSTROID, tup,
3174 Anum_pg_constraint_conffeqop, &isNull);
3175 if (isNull)
3176 elog(ERROR, "null conffeqop for constraint %u", constraintOid);
3177 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3178 numkeys = ARR_DIMS(arr)[0];
3179 if (ARR_NDIM(arr) != 1 ||
3180 numkeys != riinfo->nkeys ||
3181 numkeys > RI_MAX_NUMKEYS ||
3182 ARR_HASNULL(arr) ||
3183 ARR_ELEMTYPE(arr) != OIDOID)
3184 elog(ERROR, "conffeqop is not a 1-D Oid array");
3185 memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3186 if ((Pointer) arr != DatumGetPointer(adatum))
3187 pfree(arr); /* free de-toasted copy, if any */
3189 ReleaseSysCache(tup);
3194 * Prepare execution plan for a query to enforce an RI restriction
3196 * If cache_plan is true, the plan is saved into our plan hashtable
3197 * so that we don't need to plan it again.
3199 static SPIPlanPtr
3200 ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
3201 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
3202 bool cache_plan)
3204 SPIPlanPtr qplan;
3205 Relation query_rel;
3206 Oid save_userid;
3207 bool save_secdefcxt;
3210 * The query is always run against the FK table except when this is an
3211 * update/insert trigger on the FK table itself - either
3212 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
3214 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
3215 qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
3216 query_rel = pk_rel;
3217 else
3218 query_rel = fk_rel;
3220 /* Switch to proper UID to perform check as */
3221 GetUserIdAndContext(&save_userid, &save_secdefcxt);
3222 SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);
3224 /* Create the plan */
3225 qplan = SPI_prepare(querystr, nargs, argtypes);
3227 if (qplan == NULL)
3228 elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
3230 /* Restore UID */
3231 SetUserIdAndContext(save_userid, save_secdefcxt);
3233 /* Save the plan if requested */
3234 if (cache_plan)
3236 qplan = SPI_saveplan(qplan);
3237 ri_HashPreparedPlan(qkey, qplan);
3240 return qplan;
3244 * Perform a query to enforce an RI restriction
3246 static bool
3247 ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
3248 Relation fk_rel, Relation pk_rel,
3249 HeapTuple old_tuple, HeapTuple new_tuple,
3250 bool detectNewRows,
3251 int expect_OK, const char *constrname)
3253 Relation query_rel,
3254 source_rel;
3255 int key_idx;
3256 Snapshot test_snapshot;
3257 Snapshot crosscheck_snapshot;
3258 int limit;
3259 int spi_result;
3260 Oid save_userid;
3261 bool save_secdefcxt;
3262 Datum vals[RI_MAX_NUMKEYS * 2];
3263 char nulls[RI_MAX_NUMKEYS * 2];
3266 * The query is always run against the FK table except when this is an
3267 * update/insert trigger on the FK table itself - either
3268 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
3270 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
3271 qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
3272 query_rel = pk_rel;
3273 else
3274 query_rel = fk_rel;
3277 * The values for the query are taken from the table on which the trigger
3278 * is called - it is normally the other one with respect to query_rel. An
3279 * exception is ri_Check_Pk_Match(), which uses the PK table for both (the
3280 * case when constrname == NULL)
3282 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK && constrname != NULL)
3284 source_rel = fk_rel;
3285 key_idx = RI_KEYPAIR_FK_IDX;
3287 else
3289 source_rel = pk_rel;
3290 key_idx = RI_KEYPAIR_PK_IDX;
3293 /* Extract the parameters to be passed into the query */
3294 if (new_tuple)
3296 ri_ExtractValues(qkey, key_idx, source_rel, new_tuple,
3297 vals, nulls);
3298 if (old_tuple)
3299 ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
3300 vals + qkey->nkeypairs, nulls + qkey->nkeypairs);
3302 else
3304 ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
3305 vals, nulls);
3309 * In READ COMMITTED mode, we just need to use an up-to-date regular
3310 * snapshot, and we will see all rows that could be interesting. But in
3311 * SERIALIZABLE mode, we can't change the transaction snapshot. If the
3312 * caller passes detectNewRows == false then it's okay to do the query
3313 * with the transaction snapshot; otherwise we use a current snapshot, and
3314 * tell the executor to error out if it finds any rows under the current
3315 * snapshot that wouldn't be visible per the transaction snapshot. Note
3316 * that SPI_execute_snapshot will register the snapshots, so we don't need
3317 * to bother here.
3319 if (IsXactIsoLevelSerializable && detectNewRows)
3321 CommandCounterIncrement(); /* be sure all my own work is visible */
3322 test_snapshot = GetLatestSnapshot();
3323 crosscheck_snapshot = GetTransactionSnapshot();
3325 else
3327 /* the default SPI behavior is okay */
3328 test_snapshot = InvalidSnapshot;
3329 crosscheck_snapshot = InvalidSnapshot;
3333 * If this is a select query (e.g., for a 'no action' or 'restrict'
3334 * trigger), we only need to see if there is a single row in the table,
3335 * matching the key. Otherwise, limit = 0 - because we want the query to
3336 * affect ALL the matching rows.
3338 limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
3340 /* Switch to proper UID to perform check as */
3341 GetUserIdAndContext(&save_userid, &save_secdefcxt);
3342 SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);
3344 /* Finally we can run the query. */
3345 spi_result = SPI_execute_snapshot(qplan,
3346 vals, nulls,
3347 test_snapshot, crosscheck_snapshot,
3348 false, false, limit);
3350 /* Restore UID */
3351 SetUserIdAndContext(save_userid, save_secdefcxt);
3353 /* Check result */
3354 if (spi_result < 0)
3355 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
3357 if (expect_OK >= 0 && spi_result != expect_OK)
3358 ri_ReportViolation(qkey, constrname ? constrname : "",
3359 pk_rel, fk_rel,
3360 new_tuple ? new_tuple : old_tuple,
3361 NULL,
3362 true);
3364 /* XXX wouldn't it be clearer to do this part at the caller? */
3365 if (constrname && expect_OK == SPI_OK_SELECT &&
3366 (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
3367 ri_ReportViolation(qkey, constrname,
3368 pk_rel, fk_rel,
3369 new_tuple ? new_tuple : old_tuple,
3370 NULL,
3371 false);
3373 return SPI_processed != 0;
3377 * Extract fields from a tuple into Datum/nulls arrays
3379 static void
3380 ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
3381 Relation rel, HeapTuple tuple,
3382 Datum *vals, char *nulls)
3384 int i;
3385 bool isnull;
3387 for (i = 0; i < qkey->nkeypairs; i++)
3389 vals[i] = SPI_getbinval(tuple, rel->rd_att,
3390 qkey->keypair[i][key_idx],
3391 &isnull);
3392 nulls[i] = isnull ? 'n' : ' ';
3397 * Produce an error report
3399 * If the failed constraint was on insert/update to the FK table,
3400 * we want the key names and values extracted from there, and the error
3401 * message to look like 'key blah is not present in PK'.
3402 * Otherwise, the attr names and values come from the PK table and the
3403 * message looks like 'key blah is still referenced from FK'.
3405 static void
3406 ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
3407 Relation pk_rel, Relation fk_rel,
3408 HeapTuple violator, TupleDesc tupdesc,
3409 bool spi_err)
3411 #define BUFLENGTH 512
3412 char key_names[BUFLENGTH];
3413 char key_values[BUFLENGTH];
3414 char *name_ptr = key_names;
3415 char *val_ptr = key_values;
3416 bool onfk;
3417 int idx,
3418 key_idx;
3420 if (spi_err)
3421 ereport(ERROR,
3422 (errcode(ERRCODE_INTERNAL_ERROR),
3423 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
3424 RelationGetRelationName(pk_rel),
3425 constrname,
3426 RelationGetRelationName(fk_rel)),
3427 errhint("This is most likely due to a rule having rewritten the query.")));
3430 * Determine which relation to complain about. If tupdesc wasn't passed
3431 * by caller, assume the violator tuple came from there.
3433 onfk = (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
3434 if (onfk)
3436 key_idx = RI_KEYPAIR_FK_IDX;
3437 if (tupdesc == NULL)
3438 tupdesc = fk_rel->rd_att;
3440 else
3442 key_idx = RI_KEYPAIR_PK_IDX;
3443 if (tupdesc == NULL)
3444 tupdesc = pk_rel->rd_att;
3448 * Special case - if there are no keys at all, this is a 'no column'
3449 * constraint - no need to try to extract the values, and the message in
3450 * this case looks different.
3452 if (qkey->nkeypairs == 0)
3454 ereport(ERROR,
3455 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3456 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
3457 RelationGetRelationName(fk_rel), constrname),
3458 errdetail("No rows were found in \"%s\".",
3459 RelationGetRelationName(pk_rel))));
3462 /* Get printable versions of the keys involved */
3463 for (idx = 0; idx < qkey->nkeypairs; idx++)
3465 int fnum = qkey->keypair[idx][key_idx];
3466 char *name,
3467 *val;
3469 name = SPI_fname(tupdesc, fnum);
3470 val = SPI_getvalue(violator, tupdesc, fnum);
3471 if (!val)
3472 val = "null";
3475 * Go to "..." if name or value doesn't fit in buffer. We reserve 5
3476 * bytes to ensure we can add comma, "...", null.
3478 if (strlen(name) >= (key_names + BUFLENGTH - 5) - name_ptr ||
3479 strlen(val) >= (key_values + BUFLENGTH - 5) - val_ptr)
3481 sprintf(name_ptr, "...");
3482 sprintf(val_ptr, "...");
3483 break;
3486 name_ptr += sprintf(name_ptr, "%s%s", idx > 0 ? "," : "", name);
3487 val_ptr += sprintf(val_ptr, "%s%s", idx > 0 ? "," : "", val);
3490 if (onfk)
3491 ereport(ERROR,
3492 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3493 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
3494 RelationGetRelationName(fk_rel), constrname),
3495 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
3496 key_names, key_values,
3497 RelationGetRelationName(pk_rel))));
3498 else
3499 ereport(ERROR,
3500 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3501 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
3502 RelationGetRelationName(pk_rel),
3503 constrname, RelationGetRelationName(fk_rel)),
3504 errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
3505 key_names, key_values,
3506 RelationGetRelationName(fk_rel))));
3509 /* ----------
3510 * ri_BuildQueryKeyPkCheck -
3512 * Build up a new hashtable key for a prepared SPI plan of a
3513 * check for PK rows in noaction triggers.
3515 * key: output argument, *key is filled in based on the other arguments
3516 * riinfo: info from pg_constraint entry
3517 * constr_queryno: an internal number of the query inside the proc
3519 * At least for MATCH FULL this builds a unique key per plan.
3520 * ----------
3522 static void
3523 ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
3524 int32 constr_queryno)
3526 int i;
3528 MemSet(key, 0, sizeof(RI_QueryKey));
3529 key->constr_type = FKCONSTR_MATCH_FULL;
3530 key->constr_id = riinfo->constraint_id;
3531 key->constr_queryno = constr_queryno;
3532 key->fk_relid = InvalidOid;
3533 key->pk_relid = riinfo->pk_relid;
3534 key->nkeypairs = riinfo->nkeys;
3535 for (i = 0; i < riinfo->nkeys; i++)
3537 key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
3538 key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
3543 /* ----------
3544 * ri_NullCheck -
3546 * Determine the NULL state of all key values in a tuple
3548 * Returns one of RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL or RI_KEYS_SOME_NULL.
3549 * ----------
3551 static int
3552 ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
3554 int i;
3555 bool isnull;
3556 bool allnull = true;
3557 bool nonenull = true;
3559 for (i = 0; i < key->nkeypairs; i++)
3561 isnull = false;
3562 SPI_getbinval(tup, rel->rd_att, key->keypair[i][pairidx], &isnull);
3563 if (isnull)
3564 nonenull = false;
3565 else
3566 allnull = false;
3569 if (allnull)
3570 return RI_KEYS_ALL_NULL;
3572 if (nonenull)
3573 return RI_KEYS_NONE_NULL;
3575 return RI_KEYS_SOME_NULL;
3579 /* ----------
3580 * ri_InitHashTables -
3582 * Initialize our internal hash tables for prepared
3583 * query plans and comparison operators.
3584 * ----------
3586 static void
3587 ri_InitHashTables(void)
3589 HASHCTL ctl;
3591 memset(&ctl, 0, sizeof(ctl));
3592 ctl.keysize = sizeof(RI_QueryKey);
3593 ctl.entrysize = sizeof(RI_QueryHashEntry);
3594 ctl.hash = tag_hash;
3595 ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
3596 &ctl, HASH_ELEM | HASH_FUNCTION);
3598 memset(&ctl, 0, sizeof(ctl));
3599 ctl.keysize = sizeof(RI_CompareKey);
3600 ctl.entrysize = sizeof(RI_CompareHashEntry);
3601 ctl.hash = tag_hash;
3602 ri_compare_cache = hash_create("RI compare cache", RI_INIT_QUERYHASHSIZE,
3603 &ctl, HASH_ELEM | HASH_FUNCTION);
3607 /* ----------
3608 * ri_FetchPreparedPlan -
3610 * Lookup for a query key in our private hash table of prepared
3611 * and saved SPI execution plans. Return the plan if found or NULL.
3612 * ----------
3614 static SPIPlanPtr
3615 ri_FetchPreparedPlan(RI_QueryKey *key)
3617 RI_QueryHashEntry *entry;
3618 SPIPlanPtr plan;
3621 * On the first call initialize the hashtable
3623 if (!ri_query_cache)
3624 ri_InitHashTables();
3627 * Lookup for the key
3629 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3630 (void *) key,
3631 HASH_FIND, NULL);
3632 if (entry == NULL)
3633 return NULL;
3636 * Check whether the plan is still valid. If it isn't, we don't want
3637 * to simply rely on plancache.c to regenerate it; rather we should
3638 * start from scratch and rebuild the query text too. This is to cover
3639 * cases such as table/column renames. We depend on the plancache
3640 * machinery to detect possible invalidations, though.
3642 * CAUTION: this check is only trustworthy if the caller has already
3643 * locked both FK and PK rels.
3645 plan = entry->plan;
3646 if (plan && SPI_plan_is_valid(plan))
3647 return plan;
3650 * Otherwise we might as well flush the cached plan now, to free a
3651 * little memory space before we make a new one.
3653 entry->plan = NULL;
3654 if (plan)
3655 SPI_freeplan(plan);
3657 return NULL;
3661 /* ----------
3662 * ri_HashPreparedPlan -
3664 * Add another plan to our private SPI query plan hashtable.
3665 * ----------
3667 static void
3668 ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
3670 RI_QueryHashEntry *entry;
3671 bool found;
3674 * On the first call initialize the hashtable
3676 if (!ri_query_cache)
3677 ri_InitHashTables();
3680 * Add the new plan. We might be overwriting an entry previously
3681 * found invalid by ri_FetchPreparedPlan.
3683 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3684 (void *) key,
3685 HASH_ENTER, &found);
3686 Assert(!found || entry->plan == NULL);
3687 entry->plan = plan;
3691 /* ----------
3692 * ri_KeysEqual -
3694 * Check if all key values in OLD and NEW are equal.
3695 * ----------
3697 static bool
3698 ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
3699 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3701 TupleDesc tupdesc = RelationGetDescr(rel);
3702 const int16 *attnums;
3703 const Oid *eq_oprs;
3704 int i;
3706 if (rel_is_pk)
3708 attnums = riinfo->pk_attnums;
3709 eq_oprs = riinfo->pp_eq_oprs;
3711 else
3713 attnums = riinfo->fk_attnums;
3714 eq_oprs = riinfo->ff_eq_oprs;
3717 for (i = 0; i < riinfo->nkeys; i++)
3719 Datum oldvalue;
3720 Datum newvalue;
3721 bool isnull;
3724 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3726 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
3727 if (isnull)
3728 return false;
3731 * Get one attribute's newvalue. If it is NULL - they're not equal.
3733 newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
3734 if (isnull)
3735 return false;
3738 * Compare them with the appropriate equality operator.
3740 if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
3741 oldvalue, newvalue))
3742 return false;
3745 return true;
3749 /* ----------
3750 * ri_AllKeysUnequal -
3752 * Check if all key values in OLD and NEW are not equal.
3753 * ----------
3755 static bool
3756 ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
3757 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3759 TupleDesc tupdesc = RelationGetDescr(rel);
3760 const int16 *attnums;
3761 const Oid *eq_oprs;
3762 int i;
3764 if (rel_is_pk)
3766 attnums = riinfo->pk_attnums;
3767 eq_oprs = riinfo->pp_eq_oprs;
3769 else
3771 attnums = riinfo->fk_attnums;
3772 eq_oprs = riinfo->ff_eq_oprs;
3775 for (i = 0; i < riinfo->nkeys; i++)
3777 Datum oldvalue;
3778 Datum newvalue;
3779 bool isnull;
3782 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3784 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
3785 if (isnull)
3786 continue;
3789 * Get one attribute's newvalue. If it is NULL - they're not equal.
3791 newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
3792 if (isnull)
3793 continue;
3796 * Compare them with the appropriate equality operator.
3798 if (ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
3799 oldvalue, newvalue))
3800 return false; /* found two equal items */
3803 return true;
3807 /* ----------
3808 * ri_OneKeyEqual -
3810 * Check if one key value in OLD and NEW is equal. Note column is indexed
3811 * from zero.
3813 * ri_KeysEqual could call this but would run a bit slower. For
3814 * now, let's duplicate the code.
3815 * ----------
3817 static bool
3818 ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
3819 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3821 TupleDesc tupdesc = RelationGetDescr(rel);
3822 const int16 *attnums;
3823 const Oid *eq_oprs;
3824 Datum oldvalue;
3825 Datum newvalue;
3826 bool isnull;
3828 if (rel_is_pk)
3830 attnums = riinfo->pk_attnums;
3831 eq_oprs = riinfo->pp_eq_oprs;
3833 else
3835 attnums = riinfo->fk_attnums;
3836 eq_oprs = riinfo->ff_eq_oprs;
3840 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3842 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[column], &isnull);
3843 if (isnull)
3844 return false;
3847 * Get one attribute's newvalue. If it is NULL - they're not equal.
3849 newvalue = SPI_getbinval(newtup, tupdesc, attnums[column], &isnull);
3850 if (isnull)
3851 return false;
3854 * Compare them with the appropriate equality operator.
3856 if (!ri_AttributesEqual(eq_oprs[column], RIAttType(rel, attnums[column]),
3857 oldvalue, newvalue))
3858 return false;
3860 return true;
3863 /* ----------
3864 * ri_AttributesEqual -
3866 * Call the appropriate equality comparison operator for two values.
3868 * NB: we have already checked that neither value is null.
3869 * ----------
3871 static bool
3872 ri_AttributesEqual(Oid eq_opr, Oid typeid,
3873 Datum oldvalue, Datum newvalue)
3875 RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
3877 /* Do we need to cast the values? */
3878 if (OidIsValid(entry->cast_func_finfo.fn_oid))
3880 oldvalue = FunctionCall3(&entry->cast_func_finfo,
3881 oldvalue,
3882 Int32GetDatum(-1), /* typmod */
3883 BoolGetDatum(false)); /* implicit coercion */
3884 newvalue = FunctionCall3(&entry->cast_func_finfo,
3885 newvalue,
3886 Int32GetDatum(-1), /* typmod */
3887 BoolGetDatum(false)); /* implicit coercion */
3890 /* Apply the comparison operator */
3891 return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
3892 oldvalue, newvalue));
3895 /* ----------
3896 * ri_HashCompareOp -
3898 * See if we know how to compare two values, and create a new hash entry
3899 * if not.
3900 * ----------
3902 static RI_CompareHashEntry *
3903 ri_HashCompareOp(Oid eq_opr, Oid typeid)
3905 RI_CompareKey key;
3906 RI_CompareHashEntry *entry;
3907 bool found;
3910 * On the first call initialize the hashtable
3912 if (!ri_compare_cache)
3913 ri_InitHashTables();
3916 * Find or create a hash entry. Note we're assuming RI_CompareKey
3917 * contains no struct padding.
3919 key.eq_opr = eq_opr;
3920 key.typeid = typeid;
3921 entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
3922 (void *) &key,
3923 HASH_ENTER, &found);
3924 if (!found)
3925 entry->valid = false;
3928 * If not already initialized, do so. Since we'll keep this hash entry
3929 * for the life of the backend, put any subsidiary info for the function
3930 * cache structs into TopMemoryContext.
3932 if (!entry->valid)
3934 Oid lefttype,
3935 righttype,
3936 castfunc;
3937 CoercionPathType pathtype;
3939 /* We always need to know how to call the equality operator */
3940 fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
3941 TopMemoryContext);
3944 * If we chose to use a cast from FK to PK type, we may have to apply
3945 * the cast function to get to the operator's input type.
3947 * XXX eventually it would be good to support array-coercion cases
3948 * here and in ri_AttributesEqual(). At the moment there is no point
3949 * because cases involving nonidentical array types will be rejected
3950 * at constraint creation time.
3952 * XXX perhaps also consider supporting CoerceViaIO? No need at the
3953 * moment since that will never be generated for implicit coercions.
3955 op_input_types(eq_opr, &lefttype, &righttype);
3956 Assert(lefttype == righttype);
3957 if (typeid == lefttype)
3958 castfunc = InvalidOid; /* simplest case */
3959 else
3961 pathtype = find_coercion_pathway(lefttype, typeid,
3962 COERCION_IMPLICIT,
3963 &castfunc);
3964 if (pathtype != COERCION_PATH_FUNC &&
3965 pathtype != COERCION_PATH_RELABELTYPE)
3968 * The declared input type of the eq_opr might be a
3969 * polymorphic type such as ANYARRAY or ANYENUM. If so,
3970 * assume the coercion is valid; otherwise complain.
3972 if (!IsPolymorphicType(lefttype))
3973 elog(ERROR, "no conversion function from %s to %s",
3974 format_type_be(typeid),
3975 format_type_be(lefttype));
3978 if (OidIsValid(castfunc))
3979 fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
3980 TopMemoryContext);
3981 else
3982 entry->cast_func_finfo.fn_oid = InvalidOid;
3983 entry->valid = true;
3986 return entry;
3991 * Given a trigger function OID, determine whether it is an RI trigger,
3992 * and if so whether it is attached to PK or FK relation.
3995 RI_FKey_trigger_type(Oid tgfoid)
3997 switch (tgfoid)
3999 case F_RI_FKEY_CASCADE_DEL:
4000 case F_RI_FKEY_CASCADE_UPD:
4001 case F_RI_FKEY_RESTRICT_DEL:
4002 case F_RI_FKEY_RESTRICT_UPD:
4003 case F_RI_FKEY_SETNULL_DEL:
4004 case F_RI_FKEY_SETNULL_UPD:
4005 case F_RI_FKEY_SETDEFAULT_DEL:
4006 case F_RI_FKEY_SETDEFAULT_UPD:
4007 case F_RI_FKEY_NOACTION_DEL:
4008 case F_RI_FKEY_NOACTION_UPD:
4009 return RI_TRIGGER_PK;
4011 case F_RI_FKEY_CHECK_INS:
4012 case F_RI_FKEY_CHECK_UPD:
4013 return RI_TRIGGER_FK;
4016 return RI_TRIGGER_NONE;