4 * Generic trigger procedures for referential integrity constraint
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-2011, PostgreSQL Global Development Group
18 * src/backend/utils/adt/ri_triggers.c
27 * Add MATCH PARTIAL logic.
33 #include "access/xact.h"
34 #include "access/sysattr.h"
35 #include "catalog/pg_collation.h"
36 #include "catalog/pg_constraint.h"
37 #include "catalog/pg_operator.h"
38 #include "catalog/pg_type.h"
39 #include "commands/trigger.h"
40 #include "executor/executor.h"
41 #include "executor/spi.h"
42 #include "parser/parse_coerce.h"
43 #include "parser/parse_relation.h"
44 #include "miscadmin.h"
45 #include "utils/acl.h"
46 #include "utils/builtins.h"
47 #include "utils/fmgroids.h"
48 #include "utils/guc.h"
49 #include "utils/lsyscache.h"
50 #include "utils/memutils.h"
51 #include "utils/rel.h"
52 #include "utils/snapmgr.h"
53 #include "utils/syscache.h"
54 #include "utils/tqual.h"
62 #define RI_MAX_NUMKEYS INDEX_MAX_KEYS
64 #define RI_INIT_QUERYHASHSIZE 128
66 #define RI_KEYS_ALL_NULL 0
67 #define RI_KEYS_SOME_NULL 1
68 #define RI_KEYS_NONE_NULL 2
70 /* queryno values must be distinct for the convenience of ri_PerformCheck */
71 #define RI_PLAN_CHECK_LOOKUPPK_NOCOLS 1
72 #define RI_PLAN_CHECK_LOOKUPPK 2
73 #define RI_PLAN_CASCADE_DEL_DODELETE 3
74 #define RI_PLAN_CASCADE_UPD_DOUPDATE 4
75 #define RI_PLAN_NOACTION_DEL_CHECKREF 5
76 #define RI_PLAN_NOACTION_UPD_CHECKREF 6
77 #define RI_PLAN_RESTRICT_DEL_CHECKREF 7
78 #define RI_PLAN_RESTRICT_UPD_CHECKREF 8
79 #define RI_PLAN_SETNULL_DEL_DOUPDATE 9
80 #define RI_PLAN_SETNULL_UPD_DOUPDATE 10
82 #define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3)
83 #define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2)
85 #define RIAttName(rel, attnum) NameStr(*attnumAttName(rel, attnum))
86 #define RIAttType(rel, attnum) attnumTypeId(rel, attnum)
87 #define RIAttCollation(rel, attnum) attnumCollationId(rel, attnum)
89 #define RI_TRIGTYPE_INSERT 1
90 #define RI_TRIGTYPE_UPDATE 2
91 #define RI_TRIGTYPE_INUP 3
92 #define RI_TRIGTYPE_DELETE 4
94 #define RI_KEYPAIR_FK_IDX 0
95 #define RI_KEYPAIR_PK_IDX 1
101 * Information extracted from an FK pg_constraint entry.
104 typedef struct RI_ConstraintInfo
106 Oid constraint_id; /* OID of pg_constraint entry */
107 NameData conname; /* name of the FK constraint */
108 Oid pk_relid; /* referenced relation */
109 Oid fk_relid; /* referencing relation */
110 char confupdtype; /* foreign key's ON UPDATE action */
111 char confdeltype; /* foreign key's ON DELETE action */
112 char confmatchtype; /* foreign key's match type */
113 int nkeys; /* number of key columns */
114 int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
115 int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
116 Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK =
118 Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK =
120 Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK =
128 * The key identifying a prepared SPI plan in our query hashtable
131 typedef struct RI_QueryKey
135 int32 constr_queryno;
139 int16 keypair[RI_MAX_NUMKEYS][2];
147 typedef struct RI_QueryHashEntry
157 * The key identifying an entry showing how to compare two values
160 typedef struct RI_CompareKey
162 Oid eq_opr; /* the equality operator to apply */
163 Oid typeid; /* the data type to apply it to */
168 * RI_CompareHashEntry
171 typedef struct RI_CompareHashEntry
174 bool valid; /* successfully initialized? */
175 FmgrInfo eq_opr_finfo; /* call info for equality fn */
176 FmgrInfo cast_func_finfo; /* in case we must coerce input */
177 } RI_CompareHashEntry;
184 static HTAB *ri_query_cache = NULL;
185 static HTAB *ri_compare_cache = NULL;
189 * Local function prototypes
192 static void quoteOneName(char *buffer, const char *name);
193 static void quoteRelationName(char *buffer, Relation rel);
194 static void ri_GenerateQual(StringInfo buf,
196 const char *leftop, Oid leftoptype,
198 const char *rightop, Oid rightoptype);
199 static void ri_add_cast_to(StringInfo buf, Oid typid);
200 static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
201 static int ri_NullCheck(Relation rel, HeapTuple tup,
202 RI_QueryKey *key, int pairidx);
203 static void ri_BuildQueryKeyFull(RI_QueryKey *key,
204 const RI_ConstraintInfo *riinfo,
205 int32 constr_queryno);
206 static void ri_BuildQueryKeyPkCheck(RI_QueryKey *key,
207 const RI_ConstraintInfo *riinfo,
208 int32 constr_queryno);
209 static bool ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
210 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
211 static bool ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
212 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
213 static bool ri_OneKeyEqual(Relation rel, int column,
214 HeapTuple oldtup, HeapTuple newtup,
215 const RI_ConstraintInfo *riinfo, bool rel_is_pk);
216 static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
217 Datum oldvalue, Datum newvalue);
218 static bool ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
220 const RI_ConstraintInfo *riinfo);
222 static void ri_InitHashTables(void);
223 static SPIPlanPtr ri_FetchPreparedPlan(RI_QueryKey *key);
224 static void ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan);
225 static RI_CompareHashEntry *ri_HashCompareOp(Oid eq_opr, Oid typeid);
227 static void ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname,
229 static void ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
230 Trigger *trigger, Relation trig_rel, bool rel_is_pk);
231 static SPIPlanPtr ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
232 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
234 static bool ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
235 Relation fk_rel, Relation pk_rel,
236 HeapTuple old_tuple, HeapTuple new_tuple,
238 int expect_OK, const char *constrname);
239 static void ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
240 Relation rel, HeapTuple tuple,
241 Datum *vals, char *nulls);
242 static void ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
243 Relation pk_rel, Relation fk_rel,
244 HeapTuple violator, TupleDesc tupdesc,
251 * Check foreign key existence (combined for INSERT and UPDATE).
255 RI_FKey_check(PG_FUNCTION_ARGS)
257 TriggerData *trigdata = (TriggerData *) fcinfo->context;
258 RI_ConstraintInfo riinfo;
268 * Check that this is a valid trigger call on the right time and event.
270 ri_CheckTrigger(fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP);
275 ri_FetchConstraintInfo(&riinfo,
276 trigdata->tg_trigger, trigdata->tg_relation, false);
278 if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
280 new_row = trigdata->tg_newtuple;
281 new_row_buf = trigdata->tg_newtuplebuf;
285 new_row = trigdata->tg_trigtuple;
286 new_row_buf = trigdata->tg_trigtuplebuf;
290 * We should not even consider checking the row if it is no longer valid,
291 * since it was either deleted (so the deferred check should be skipped)
292 * or updated (in which case only the latest version of the row should be
293 * checked). Test its liveness according to SnapshotSelf.
295 * NOTE: The normal coding rule is that one must acquire the buffer
296 * content lock to call HeapTupleSatisfiesVisibility. We can skip that
297 * here because we know that AfterTriggerExecute just fetched the tuple
298 * successfully, so there cannot be a VACUUM compaction in progress on the
299 * page (either heap_fetch would have waited for the VACUUM, or the
300 * VACUUM's LockBufferForCleanup would be waiting for us to drop pin). And
301 * since this is a row inserted by our open transaction, no one else can
302 * be entitled to change its xmin/xmax.
304 Assert(new_row_buf != InvalidBuffer);
305 if (!HeapTupleSatisfiesVisibility(new_row, SnapshotSelf, new_row_buf))
306 return PointerGetDatum(NULL);
309 * Get the relation descriptors of the FK and PK tables.
311 * pk_rel is opened in RowShareLock mode since that's what our eventual
312 * SELECT FOR SHARE will get on it.
314 fk_rel = trigdata->tg_relation;
315 pk_rel = heap_open(riinfo.pk_relid, RowShareLock);
318 * SQL3 11.9 <referential constraint definition>
319 * General rules 2) a):
320 * If Rf and Rt are empty (no columns to compare given)
321 * constraint is true if 0 < (SELECT COUNT(*) FROM T)
323 * Note: The special case that no columns are given cannot
324 * occur up to now in Postgres, it's just there for
325 * future enhancements.
328 if (riinfo.nkeys == 0)
330 ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK_NOCOLS);
332 if (SPI_connect() != SPI_OK_CONNECT)
333 elog(ERROR, "SPI_connect failed");
335 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
337 char querystr[MAX_QUOTED_REL_NAME_LEN + 100];
338 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
341 * The query string built is
342 * SELECT 1 FROM ONLY <pktable>
345 quoteRelationName(pkrelname, pk_rel);
346 snprintf(querystr, sizeof(querystr),
347 "SELECT 1 FROM ONLY %s x FOR SHARE OF x",
350 /* Prepare and save the plan */
351 qplan = ri_PlanCheck(querystr, 0, NULL,
352 &qkey, fk_rel, pk_rel, true);
358 ri_PerformCheck(&qkey, qplan,
363 NameStr(riinfo.conname));
365 if (SPI_finish() != SPI_OK_FINISH)
366 elog(ERROR, "SPI_finish failed");
368 heap_close(pk_rel, RowShareLock);
370 return PointerGetDatum(NULL);
373 if (riinfo.confmatchtype == FKCONSTR_MATCH_PARTIAL)
375 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
376 errmsg("MATCH PARTIAL not yet implemented")));
378 ri_BuildQueryKeyFull(&qkey, &riinfo, RI_PLAN_CHECK_LOOKUPPK);
380 switch (ri_NullCheck(fk_rel, new_row, &qkey, RI_KEYPAIR_FK_IDX))
382 case RI_KEYS_ALL_NULL:
385 * No check - if NULLs are allowed at all is already checked by
386 * NOT NULL constraint.
388 * This is true for MATCH FULL, MATCH PARTIAL, and MATCH
391 heap_close(pk_rel, RowShareLock);
392 return PointerGetDatum(NULL);
394 case RI_KEYS_SOME_NULL:
397 * This is the only case that differs between the three kinds of
400 switch (riinfo.confmatchtype)
402 case FKCONSTR_MATCH_FULL:
405 * Not allowed - MATCH FULL says either all or none of the
406 * attributes can be NULLs
409 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
410 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
411 RelationGetRelationName(trigdata->tg_relation),
412 NameStr(riinfo.conname)),
413 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
414 heap_close(pk_rel, RowShareLock);
415 return PointerGetDatum(NULL);
417 case FKCONSTR_MATCH_UNSPECIFIED:
420 * MATCH <unspecified> - if ANY column is null, we have a
423 heap_close(pk_rel, RowShareLock);
424 return PointerGetDatum(NULL);
426 case FKCONSTR_MATCH_PARTIAL:
429 * MATCH PARTIAL - all non-null columns must match. (not
430 * implemented, can be done by modifying the query below
431 * to only include non-null columns, or by writing a
432 * special version here)
435 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
436 errmsg("MATCH PARTIAL not yet implemented")));
437 heap_close(pk_rel, RowShareLock);
438 return PointerGetDatum(NULL);
441 case RI_KEYS_NONE_NULL:
444 * Have a full qualified key - continue below for all three kinds
450 if (SPI_connect() != SPI_OK_CONNECT)
451 elog(ERROR, "SPI_connect failed");
454 * Fetch or prepare a saved plan for the real check
456 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
458 StringInfoData querybuf;
459 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
460 char attname[MAX_QUOTED_NAME_LEN];
462 const char *querysep;
463 Oid queryoids[RI_MAX_NUMKEYS];
466 * The query string built is
467 * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
468 * The type id's for the $ parameters are those of the
469 * corresponding FK attributes.
472 initStringInfo(&querybuf);
473 quoteRelationName(pkrelname, pk_rel);
474 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
476 for (i = 0; i < riinfo.nkeys; i++)
478 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
479 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
481 quoteOneName(attname,
482 RIAttName(pk_rel, riinfo.pk_attnums[i]));
483 sprintf(paramname, "$%d", i + 1);
484 ri_GenerateQual(&querybuf, querysep,
486 riinfo.pf_eq_oprs[i],
489 queryoids[i] = fk_type;
491 appendStringInfo(&querybuf, " FOR SHARE OF x");
493 /* Prepare and save the plan */
494 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
495 &qkey, fk_rel, pk_rel, true);
499 * Now check that foreign key exists in PK table
501 ri_PerformCheck(&qkey, qplan,
506 NameStr(riinfo.conname));
508 if (SPI_finish() != SPI_OK_FINISH)
509 elog(ERROR, "SPI_finish failed");
511 heap_close(pk_rel, RowShareLock);
513 return PointerGetDatum(NULL);
518 * RI_FKey_check_ins -
520 * Check foreign key existence at insert event on FK table.
524 RI_FKey_check_ins(PG_FUNCTION_ARGS)
526 return RI_FKey_check(fcinfo);
531 * RI_FKey_check_upd -
533 * Check foreign key existence at update event on FK table.
537 RI_FKey_check_upd(PG_FUNCTION_ARGS)
539 return RI_FKey_check(fcinfo);
546 * Check for matching value of old pk row in current state for
547 * noaction triggers. Returns false if no row was found and a fk row
548 * could potentially be referencing this row, true otherwise.
552 ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
554 const RI_ConstraintInfo *riinfo)
561 ri_BuildQueryKeyPkCheck(&qkey, riinfo, RI_PLAN_CHECK_LOOKUPPK);
563 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
565 case RI_KEYS_ALL_NULL:
568 * No check - nothing could have been referencing this row anyway.
572 case RI_KEYS_SOME_NULL:
575 * This is the only case that differs between the three kinds of
578 switch (riinfo->confmatchtype)
580 case FKCONSTR_MATCH_FULL:
581 case FKCONSTR_MATCH_UNSPECIFIED:
584 * MATCH <unspecified>/FULL - if ANY column is null, we
585 * can't be matching to this row already.
589 case FKCONSTR_MATCH_PARTIAL:
592 * MATCH PARTIAL - all non-null columns must match. (not
593 * implemented, can be done by modifying the query below
594 * to only include non-null columns, or by writing a
595 * special version here)
598 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
599 errmsg("MATCH PARTIAL not yet implemented")));
603 case RI_KEYS_NONE_NULL:
606 * Have a full qualified key - continue below for all three kinds
612 if (SPI_connect() != SPI_OK_CONNECT)
613 elog(ERROR, "SPI_connect failed");
616 * Fetch or prepare a saved plan for the real check
618 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
620 StringInfoData querybuf;
621 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
622 char attname[MAX_QUOTED_NAME_LEN];
624 const char *querysep;
625 Oid queryoids[RI_MAX_NUMKEYS];
628 * The query string built is
629 * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE
630 * The type id's for the $ parameters are those of the
631 * PK attributes themselves.
634 initStringInfo(&querybuf);
635 quoteRelationName(pkrelname, pk_rel);
636 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
638 for (i = 0; i < riinfo->nkeys; i++)
640 Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
642 quoteOneName(attname,
643 RIAttName(pk_rel, riinfo->pk_attnums[i]));
644 sprintf(paramname, "$%d", i + 1);
645 ri_GenerateQual(&querybuf, querysep,
647 riinfo->pp_eq_oprs[i],
650 queryoids[i] = pk_type;
652 appendStringInfo(&querybuf, " FOR SHARE OF x");
654 /* Prepare and save the plan */
655 qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
656 &qkey, fk_rel, pk_rel, true);
660 * We have a plan now. Run it.
662 result = ri_PerformCheck(&qkey, qplan,
665 true, /* treat like update */
666 SPI_OK_SELECT, NULL);
668 if (SPI_finish() != SPI_OK_FINISH)
669 elog(ERROR, "SPI_finish failed");
676 * RI_FKey_noaction_del -
678 * Give an error and roll back the current transaction if the
679 * delete has resulted in a violation of the given referential
680 * integrity constraint.
684 RI_FKey_noaction_del(PG_FUNCTION_ARGS)
686 TriggerData *trigdata = (TriggerData *) fcinfo->context;
687 RI_ConstraintInfo riinfo;
696 * Check that this is a valid trigger call on the right time and event.
698 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE);
703 ri_FetchConstraintInfo(&riinfo,
704 trigdata->tg_trigger, trigdata->tg_relation, true);
707 * Nothing to do if no column names to compare given
709 if (riinfo.nkeys == 0)
710 return PointerGetDatum(NULL);
713 * Get the relation descriptors of the FK and PK tables and the old tuple.
715 * fk_rel is opened in RowShareLock mode since that's what our eventual
716 * SELECT FOR SHARE will get on it.
718 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
719 pk_rel = trigdata->tg_relation;
720 old_row = trigdata->tg_trigtuple;
722 if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
725 * There's either another row, or no row could match this one. In
726 * either case, we don't need to do the check.
728 heap_close(fk_rel, RowShareLock);
729 return PointerGetDatum(NULL);
732 switch (riinfo.confmatchtype)
735 * SQL3 11.9 <referential constraint definition>
736 * Gereral rules 6) a) iv):
737 * MATCH <unspecified> or MATCH FULL
738 * ... ON DELETE CASCADE
741 case FKCONSTR_MATCH_UNSPECIFIED:
742 case FKCONSTR_MATCH_FULL:
743 ri_BuildQueryKeyFull(&qkey, &riinfo,
744 RI_PLAN_NOACTION_DEL_CHECKREF);
746 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
748 case RI_KEYS_ALL_NULL:
749 case RI_KEYS_SOME_NULL:
752 * No check - MATCH FULL means there cannot be any
753 * reference to old key if it contains NULL
755 heap_close(fk_rel, RowShareLock);
756 return PointerGetDatum(NULL);
758 case RI_KEYS_NONE_NULL:
761 * Have a full qualified key - continue below
766 if (SPI_connect() != SPI_OK_CONNECT)
767 elog(ERROR, "SPI_connect failed");
770 * Fetch or prepare a saved plan for the restrict delete lookup if
771 * foreign references exist
773 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
775 StringInfoData querybuf;
776 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
777 char attname[MAX_QUOTED_NAME_LEN];
779 const char *querysep;
780 Oid queryoids[RI_MAX_NUMKEYS];
783 * The query string built is
784 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
785 * The type id's for the $ parameters are those of the
786 * corresponding PK attributes.
789 initStringInfo(&querybuf);
790 quoteRelationName(fkrelname, fk_rel);
791 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
794 for (i = 0; i < riinfo.nkeys; i++)
796 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
797 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
799 quoteOneName(attname,
800 RIAttName(fk_rel, riinfo.fk_attnums[i]));
801 sprintf(paramname, "$%d", i + 1);
802 ri_GenerateQual(&querybuf, querysep,
804 riinfo.pf_eq_oprs[i],
807 queryoids[i] = pk_type;
809 appendStringInfo(&querybuf, " FOR SHARE OF x");
811 /* Prepare and save the plan */
812 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
813 &qkey, fk_rel, pk_rel, true);
817 * We have a plan now. Run it to check for existing references.
819 ri_PerformCheck(&qkey, qplan,
822 true, /* must detect new rows */
824 NameStr(riinfo.conname));
826 if (SPI_finish() != SPI_OK_FINISH)
827 elog(ERROR, "SPI_finish failed");
829 heap_close(fk_rel, RowShareLock);
831 return PointerGetDatum(NULL);
834 * Handle MATCH PARTIAL restrict delete.
836 case FKCONSTR_MATCH_PARTIAL:
838 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
839 errmsg("MATCH PARTIAL not yet implemented")));
840 return PointerGetDatum(NULL);
846 elog(ERROR, "invalid confmatchtype");
847 return PointerGetDatum(NULL);
852 * RI_FKey_noaction_upd -
854 * Give an error and roll back the current transaction if the
855 * update has resulted in a violation of the given referential
856 * integrity constraint.
860 RI_FKey_noaction_upd(PG_FUNCTION_ARGS)
862 TriggerData *trigdata = (TriggerData *) fcinfo->context;
863 RI_ConstraintInfo riinfo;
873 * Check that this is a valid trigger call on the right time and event.
875 ri_CheckTrigger(fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE);
880 ri_FetchConstraintInfo(&riinfo,
881 trigdata->tg_trigger, trigdata->tg_relation, true);
884 * Nothing to do if no column names to compare given
886 if (riinfo.nkeys == 0)
887 return PointerGetDatum(NULL);
890 * Get the relation descriptors of the FK and PK tables and the new and
893 * fk_rel is opened in RowShareLock mode since that's what our eventual
894 * SELECT FOR SHARE will get on it.
896 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
897 pk_rel = trigdata->tg_relation;
898 new_row = trigdata->tg_newtuple;
899 old_row = trigdata->tg_trigtuple;
901 switch (riinfo.confmatchtype)
904 * SQL3 11.9 <referential constraint definition>
905 * Gereral rules 6) a) iv):
906 * MATCH <unspecified> or MATCH FULL
907 * ... ON DELETE CASCADE
910 case FKCONSTR_MATCH_UNSPECIFIED:
911 case FKCONSTR_MATCH_FULL:
912 ri_BuildQueryKeyFull(&qkey, &riinfo,
913 RI_PLAN_NOACTION_UPD_CHECKREF);
915 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
917 case RI_KEYS_ALL_NULL:
918 case RI_KEYS_SOME_NULL:
921 * No check - MATCH FULL means there cannot be any
922 * reference to old key if it contains NULL
924 heap_close(fk_rel, RowShareLock);
925 return PointerGetDatum(NULL);
927 case RI_KEYS_NONE_NULL:
930 * Have a full qualified key - continue below
936 * No need to check anything if old and new keys are equal
938 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
940 heap_close(fk_rel, RowShareLock);
941 return PointerGetDatum(NULL);
944 if (ri_Check_Pk_Match(pk_rel, fk_rel, old_row, &riinfo))
947 * There's either another row, or no row could match this one.
948 * In either case, we don't need to do the check.
950 heap_close(fk_rel, RowShareLock);
951 return PointerGetDatum(NULL);
954 if (SPI_connect() != SPI_OK_CONNECT)
955 elog(ERROR, "SPI_connect failed");
958 * Fetch or prepare a saved plan for the noaction update lookup if
959 * foreign references exist
961 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
963 StringInfoData querybuf;
964 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
965 char attname[MAX_QUOTED_NAME_LEN];
967 const char *querysep;
968 Oid queryoids[RI_MAX_NUMKEYS];
971 * The query string built is
972 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
973 * The type id's for the $ parameters are those of the
974 * corresponding PK attributes.
977 initStringInfo(&querybuf);
978 quoteRelationName(fkrelname, fk_rel);
979 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
982 for (i = 0; i < riinfo.nkeys; i++)
984 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
985 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
987 quoteOneName(attname,
988 RIAttName(fk_rel, riinfo.fk_attnums[i]));
989 sprintf(paramname, "$%d", i + 1);
990 ri_GenerateQual(&querybuf, querysep,
992 riinfo.pf_eq_oprs[i],
995 queryoids[i] = pk_type;
997 appendStringInfo(&querybuf, " FOR SHARE OF x");
999 /* Prepare and save the plan */
1000 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1001 &qkey, fk_rel, pk_rel, true);
1005 * We have a plan now. Run it to check for existing references.
1007 ri_PerformCheck(&qkey, qplan,
1010 true, /* must detect new rows */
1012 NameStr(riinfo.conname));
1014 if (SPI_finish() != SPI_OK_FINISH)
1015 elog(ERROR, "SPI_finish failed");
1017 heap_close(fk_rel, RowShareLock);
1019 return PointerGetDatum(NULL);
1022 * Handle MATCH PARTIAL noaction update.
1024 case FKCONSTR_MATCH_PARTIAL:
1026 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1027 errmsg("MATCH PARTIAL not yet implemented")));
1028 return PointerGetDatum(NULL);
1034 elog(ERROR, "invalid confmatchtype");
1035 return PointerGetDatum(NULL);
1040 * RI_FKey_cascade_del -
1042 * Cascaded delete foreign key references at delete event on PK table.
1046 RI_FKey_cascade_del(PG_FUNCTION_ARGS)
1048 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1049 RI_ConstraintInfo riinfo;
1058 * Check that this is a valid trigger call on the right time and event.
1060 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE);
1065 ri_FetchConstraintInfo(&riinfo,
1066 trigdata->tg_trigger, trigdata->tg_relation, true);
1069 * Nothing to do if no column names to compare given
1071 if (riinfo.nkeys == 0)
1072 return PointerGetDatum(NULL);
1075 * Get the relation descriptors of the FK and PK tables and the old tuple.
1077 * fk_rel is opened in RowExclusiveLock mode since that's what our
1078 * eventual DELETE will get on it.
1080 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1081 pk_rel = trigdata->tg_relation;
1082 old_row = trigdata->tg_trigtuple;
1084 switch (riinfo.confmatchtype)
1087 * SQL3 11.9 <referential constraint definition>
1088 * Gereral rules 6) a) i):
1089 * MATCH <unspecified> or MATCH FULL
1090 * ... ON DELETE CASCADE
1093 case FKCONSTR_MATCH_UNSPECIFIED:
1094 case FKCONSTR_MATCH_FULL:
1095 ri_BuildQueryKeyFull(&qkey, &riinfo,
1096 RI_PLAN_CASCADE_DEL_DODELETE);
1098 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1100 case RI_KEYS_ALL_NULL:
1101 case RI_KEYS_SOME_NULL:
1104 * No check - MATCH FULL means there cannot be any
1105 * reference to old key if it contains NULL
1107 heap_close(fk_rel, RowExclusiveLock);
1108 return PointerGetDatum(NULL);
1110 case RI_KEYS_NONE_NULL:
1113 * Have a full qualified key - continue below
1118 if (SPI_connect() != SPI_OK_CONNECT)
1119 elog(ERROR, "SPI_connect failed");
1122 * Fetch or prepare a saved plan for the cascaded delete
1124 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1126 StringInfoData querybuf;
1127 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1128 char attname[MAX_QUOTED_NAME_LEN];
1130 const char *querysep;
1131 Oid queryoids[RI_MAX_NUMKEYS];
1134 * The query string built is
1135 * DELETE FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1136 * The type id's for the $ parameters are those of the
1137 * corresponding PK attributes.
1140 initStringInfo(&querybuf);
1141 quoteRelationName(fkrelname, fk_rel);
1142 appendStringInfo(&querybuf, "DELETE FROM ONLY %s", fkrelname);
1144 for (i = 0; i < riinfo.nkeys; i++)
1146 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1147 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1149 quoteOneName(attname,
1150 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1151 sprintf(paramname, "$%d", i + 1);
1152 ri_GenerateQual(&querybuf, querysep,
1154 riinfo.pf_eq_oprs[i],
1157 queryoids[i] = pk_type;
1160 /* Prepare and save the plan */
1161 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1162 &qkey, fk_rel, pk_rel, true);
1166 * We have a plan now. Build up the arguments from the key values
1167 * in the deleted PK tuple and delete the referencing rows
1169 ri_PerformCheck(&qkey, qplan,
1172 true, /* must detect new rows */
1174 NameStr(riinfo.conname));
1176 if (SPI_finish() != SPI_OK_FINISH)
1177 elog(ERROR, "SPI_finish failed");
1179 heap_close(fk_rel, RowExclusiveLock);
1181 return PointerGetDatum(NULL);
1184 * Handle MATCH PARTIAL cascaded delete.
1186 case FKCONSTR_MATCH_PARTIAL:
1188 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1189 errmsg("MATCH PARTIAL not yet implemented")));
1190 return PointerGetDatum(NULL);
1196 elog(ERROR, "invalid confmatchtype");
1197 return PointerGetDatum(NULL);
1202 * RI_FKey_cascade_upd -
1204 * Cascaded update/delete foreign key references at update event on PK table.
1208 RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
1210 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1211 RI_ConstraintInfo riinfo;
1222 * Check that this is a valid trigger call on the right time and event.
1224 ri_CheckTrigger(fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE);
1229 ri_FetchConstraintInfo(&riinfo,
1230 trigdata->tg_trigger, trigdata->tg_relation, true);
1233 * Nothing to do if no column names to compare given
1235 if (riinfo.nkeys == 0)
1236 return PointerGetDatum(NULL);
1239 * Get the relation descriptors of the FK and PK tables and the new and
1242 * fk_rel is opened in RowExclusiveLock mode since that's what our
1243 * eventual UPDATE will get on it.
1245 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1246 pk_rel = trigdata->tg_relation;
1247 new_row = trigdata->tg_newtuple;
1248 old_row = trigdata->tg_trigtuple;
1250 switch (riinfo.confmatchtype)
1253 * SQL3 11.9 <referential constraint definition>
1254 * Gereral rules 7) a) i):
1255 * MATCH <unspecified> or MATCH FULL
1256 * ... ON UPDATE CASCADE
1259 case FKCONSTR_MATCH_UNSPECIFIED:
1260 case FKCONSTR_MATCH_FULL:
1261 ri_BuildQueryKeyFull(&qkey, &riinfo,
1262 RI_PLAN_CASCADE_UPD_DOUPDATE);
1264 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1266 case RI_KEYS_ALL_NULL:
1267 case RI_KEYS_SOME_NULL:
1270 * No update - MATCH FULL means there cannot be any
1271 * reference to old key if it contains NULL
1273 heap_close(fk_rel, RowExclusiveLock);
1274 return PointerGetDatum(NULL);
1276 case RI_KEYS_NONE_NULL:
1279 * Have a full qualified key - continue below
1285 * No need to do anything if old and new keys are equal
1287 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
1289 heap_close(fk_rel, RowExclusiveLock);
1290 return PointerGetDatum(NULL);
1293 if (SPI_connect() != SPI_OK_CONNECT)
1294 elog(ERROR, "SPI_connect failed");
1297 * Fetch or prepare a saved plan for the cascaded update of
1298 * foreign references
1300 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1302 StringInfoData querybuf;
1303 StringInfoData qualbuf;
1304 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1305 char attname[MAX_QUOTED_NAME_LEN];
1307 const char *querysep;
1308 const char *qualsep;
1309 Oid queryoids[RI_MAX_NUMKEYS * 2];
1312 * The query string built is
1313 * UPDATE ONLY <fktable> SET fkatt1 = $1 [, ...]
1314 * WHERE $n = fkatt1 [AND ...]
1315 * The type id's for the $ parameters are those of the
1316 * corresponding PK attributes. Note that we are assuming
1317 * there is an assignment cast from the PK to the FK type;
1318 * else the parser will fail.
1321 initStringInfo(&querybuf);
1322 initStringInfo(&qualbuf);
1323 quoteRelationName(fkrelname, fk_rel);
1324 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
1327 for (i = 0, j = riinfo.nkeys; i < riinfo.nkeys; i++, j++)
1329 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1330 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1332 quoteOneName(attname,
1333 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1334 appendStringInfo(&querybuf,
1336 querysep, attname, i + 1);
1337 sprintf(paramname, "$%d", j + 1);
1338 ri_GenerateQual(&qualbuf, qualsep,
1340 riinfo.pf_eq_oprs[i],
1344 queryoids[i] = pk_type;
1345 queryoids[j] = pk_type;
1347 appendStringInfoString(&querybuf, qualbuf.data);
1349 /* Prepare and save the plan */
1350 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys * 2, queryoids,
1351 &qkey, fk_rel, pk_rel, true);
1355 * We have a plan now. Run it to update the existing references.
1357 ri_PerformCheck(&qkey, qplan,
1360 true, /* must detect new rows */
1362 NameStr(riinfo.conname));
1364 if (SPI_finish() != SPI_OK_FINISH)
1365 elog(ERROR, "SPI_finish failed");
1367 heap_close(fk_rel, RowExclusiveLock);
1369 return PointerGetDatum(NULL);
1372 * Handle MATCH PARTIAL cascade update.
1374 case FKCONSTR_MATCH_PARTIAL:
1376 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1377 errmsg("MATCH PARTIAL not yet implemented")));
1378 return PointerGetDatum(NULL);
1384 elog(ERROR, "invalid confmatchtype");
1385 return PointerGetDatum(NULL);
1390 * RI_FKey_restrict_del -
1392 * Restrict delete from PK table to rows unreferenced by foreign key.
1394 * SQL3 intends that this referential action occur BEFORE the
1395 * update is performed, rather than after. This appears to be
1396 * the only difference between "NO ACTION" and "RESTRICT".
1398 * For now, however, we treat "RESTRICT" and "NO ACTION" as
1403 RI_FKey_restrict_del(PG_FUNCTION_ARGS)
1405 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1406 RI_ConstraintInfo riinfo;
1415 * Check that this is a valid trigger call on the right time and event.
1417 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE);
1422 ri_FetchConstraintInfo(&riinfo,
1423 trigdata->tg_trigger, trigdata->tg_relation, true);
1426 * Nothing to do if no column names to compare given
1428 if (riinfo.nkeys == 0)
1429 return PointerGetDatum(NULL);
1432 * Get the relation descriptors of the FK and PK tables and the old tuple.
1434 * fk_rel is opened in RowShareLock mode since that's what our eventual
1435 * SELECT FOR SHARE will get on it.
1437 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
1438 pk_rel = trigdata->tg_relation;
1439 old_row = trigdata->tg_trigtuple;
1441 switch (riinfo.confmatchtype)
1444 * SQL3 11.9 <referential constraint definition>
1445 * Gereral rules 6) a) iv):
1446 * MATCH <unspecified> or MATCH FULL
1447 * ... ON DELETE CASCADE
1450 case FKCONSTR_MATCH_UNSPECIFIED:
1451 case FKCONSTR_MATCH_FULL:
1452 ri_BuildQueryKeyFull(&qkey, &riinfo,
1453 RI_PLAN_RESTRICT_DEL_CHECKREF);
1455 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1457 case RI_KEYS_ALL_NULL:
1458 case RI_KEYS_SOME_NULL:
1461 * No check - MATCH FULL means there cannot be any
1462 * reference to old key if it contains NULL
1464 heap_close(fk_rel, RowShareLock);
1465 return PointerGetDatum(NULL);
1467 case RI_KEYS_NONE_NULL:
1470 * Have a full qualified key - continue below
1475 if (SPI_connect() != SPI_OK_CONNECT)
1476 elog(ERROR, "SPI_connect failed");
1479 * Fetch or prepare a saved plan for the restrict delete lookup if
1480 * foreign references exist
1482 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1484 StringInfoData querybuf;
1485 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1486 char attname[MAX_QUOTED_NAME_LEN];
1488 const char *querysep;
1489 Oid queryoids[RI_MAX_NUMKEYS];
1492 * The query string built is
1493 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1494 * The type id's for the $ parameters are those of the
1495 * corresponding PK attributes.
1498 initStringInfo(&querybuf);
1499 quoteRelationName(fkrelname, fk_rel);
1500 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
1503 for (i = 0; i < riinfo.nkeys; i++)
1505 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1506 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1508 quoteOneName(attname,
1509 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1510 sprintf(paramname, "$%d", i + 1);
1511 ri_GenerateQual(&querybuf, querysep,
1513 riinfo.pf_eq_oprs[i],
1516 queryoids[i] = pk_type;
1518 appendStringInfo(&querybuf, " FOR SHARE OF x");
1520 /* Prepare and save the plan */
1521 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1522 &qkey, fk_rel, pk_rel, true);
1526 * We have a plan now. Run it to check for existing references.
1528 ri_PerformCheck(&qkey, qplan,
1531 true, /* must detect new rows */
1533 NameStr(riinfo.conname));
1535 if (SPI_finish() != SPI_OK_FINISH)
1536 elog(ERROR, "SPI_finish failed");
1538 heap_close(fk_rel, RowShareLock);
1540 return PointerGetDatum(NULL);
1543 * Handle MATCH PARTIAL restrict delete.
1545 case FKCONSTR_MATCH_PARTIAL:
1547 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1548 errmsg("MATCH PARTIAL not yet implemented")));
1549 return PointerGetDatum(NULL);
1555 elog(ERROR, "invalid confmatchtype");
1556 return PointerGetDatum(NULL);
1561 * RI_FKey_restrict_upd -
1563 * Restrict update of PK to rows unreferenced by foreign key.
1565 * SQL3 intends that this referential action occur BEFORE the
1566 * update is performed, rather than after. This appears to be
1567 * the only difference between "NO ACTION" and "RESTRICT".
1569 * For now, however, we treat "RESTRICT" and "NO ACTION" as
1574 RI_FKey_restrict_upd(PG_FUNCTION_ARGS)
1576 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1577 RI_ConstraintInfo riinfo;
1587 * Check that this is a valid trigger call on the right time and event.
1589 ri_CheckTrigger(fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE);
1594 ri_FetchConstraintInfo(&riinfo,
1595 trigdata->tg_trigger, trigdata->tg_relation, true);
1598 * Nothing to do if no column names to compare given
1600 if (riinfo.nkeys == 0)
1601 return PointerGetDatum(NULL);
1604 * Get the relation descriptors of the FK and PK tables and the new and
1607 * fk_rel is opened in RowShareLock mode since that's what our eventual
1608 * SELECT FOR SHARE will get on it.
1610 fk_rel = heap_open(riinfo.fk_relid, RowShareLock);
1611 pk_rel = trigdata->tg_relation;
1612 new_row = trigdata->tg_newtuple;
1613 old_row = trigdata->tg_trigtuple;
1615 switch (riinfo.confmatchtype)
1618 * SQL3 11.9 <referential constraint definition>
1619 * Gereral rules 6) a) iv):
1620 * MATCH <unspecified> or MATCH FULL
1621 * ... ON DELETE CASCADE
1624 case FKCONSTR_MATCH_UNSPECIFIED:
1625 case FKCONSTR_MATCH_FULL:
1626 ri_BuildQueryKeyFull(&qkey, &riinfo,
1627 RI_PLAN_RESTRICT_UPD_CHECKREF);
1629 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1631 case RI_KEYS_ALL_NULL:
1632 case RI_KEYS_SOME_NULL:
1635 * No check - MATCH FULL means there cannot be any
1636 * reference to old key if it contains NULL
1638 heap_close(fk_rel, RowShareLock);
1639 return PointerGetDatum(NULL);
1641 case RI_KEYS_NONE_NULL:
1644 * Have a full qualified key - continue below
1650 * No need to check anything if old and new keys are equal
1652 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
1654 heap_close(fk_rel, RowShareLock);
1655 return PointerGetDatum(NULL);
1658 if (SPI_connect() != SPI_OK_CONNECT)
1659 elog(ERROR, "SPI_connect failed");
1662 * Fetch or prepare a saved plan for the restrict update lookup if
1663 * foreign references exist
1665 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1667 StringInfoData querybuf;
1668 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1669 char attname[MAX_QUOTED_NAME_LEN];
1671 const char *querysep;
1672 Oid queryoids[RI_MAX_NUMKEYS];
1675 * The query string built is
1676 * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...]
1677 * The type id's for the $ parameters are those of the
1678 * corresponding PK attributes.
1681 initStringInfo(&querybuf);
1682 quoteRelationName(fkrelname, fk_rel);
1683 appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x",
1686 for (i = 0; i < riinfo.nkeys; i++)
1688 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1689 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1691 quoteOneName(attname,
1692 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1693 sprintf(paramname, "$%d", i + 1);
1694 ri_GenerateQual(&querybuf, querysep,
1696 riinfo.pf_eq_oprs[i],
1699 queryoids[i] = pk_type;
1701 appendStringInfo(&querybuf, " FOR SHARE OF x");
1703 /* Prepare and save the plan */
1704 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1705 &qkey, fk_rel, pk_rel, true);
1709 * We have a plan now. Run it to check for existing references.
1711 ri_PerformCheck(&qkey, qplan,
1714 true, /* must detect new rows */
1716 NameStr(riinfo.conname));
1718 if (SPI_finish() != SPI_OK_FINISH)
1719 elog(ERROR, "SPI_finish failed");
1721 heap_close(fk_rel, RowShareLock);
1723 return PointerGetDatum(NULL);
1726 * Handle MATCH PARTIAL restrict update.
1728 case FKCONSTR_MATCH_PARTIAL:
1730 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1731 errmsg("MATCH PARTIAL not yet implemented")));
1732 return PointerGetDatum(NULL);
1738 elog(ERROR, "invalid confmatchtype");
1739 return PointerGetDatum(NULL);
1744 * RI_FKey_setnull_del -
1746 * Set foreign key references to NULL values at delete event on PK table.
1750 RI_FKey_setnull_del(PG_FUNCTION_ARGS)
1752 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1753 RI_ConstraintInfo riinfo;
1762 * Check that this is a valid trigger call on the right time and event.
1764 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE);
1769 ri_FetchConstraintInfo(&riinfo,
1770 trigdata->tg_trigger, trigdata->tg_relation, true);
1773 * Nothing to do if no column names to compare given
1775 if (riinfo.nkeys == 0)
1776 return PointerGetDatum(NULL);
1779 * Get the relation descriptors of the FK and PK tables and the old tuple.
1781 * fk_rel is opened in RowExclusiveLock mode since that's what our
1782 * eventual UPDATE will get on it.
1784 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1785 pk_rel = trigdata->tg_relation;
1786 old_row = trigdata->tg_trigtuple;
1788 switch (riinfo.confmatchtype)
1791 * SQL3 11.9 <referential constraint definition>
1792 * Gereral rules 6) a) ii):
1793 * MATCH <UNSPECIFIED> or MATCH FULL
1794 * ... ON DELETE SET NULL
1797 case FKCONSTR_MATCH_UNSPECIFIED:
1798 case FKCONSTR_MATCH_FULL:
1799 ri_BuildQueryKeyFull(&qkey, &riinfo,
1800 RI_PLAN_SETNULL_DEL_DOUPDATE);
1802 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1804 case RI_KEYS_ALL_NULL:
1805 case RI_KEYS_SOME_NULL:
1808 * No update - MATCH FULL means there cannot be any
1809 * reference to old key if it contains NULL
1811 heap_close(fk_rel, RowExclusiveLock);
1812 return PointerGetDatum(NULL);
1814 case RI_KEYS_NONE_NULL:
1817 * Have a full qualified key - continue below
1822 if (SPI_connect() != SPI_OK_CONNECT)
1823 elog(ERROR, "SPI_connect failed");
1826 * Fetch or prepare a saved plan for the set null delete operation
1828 if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
1830 StringInfoData querybuf;
1831 StringInfoData qualbuf;
1832 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
1833 char attname[MAX_QUOTED_NAME_LEN];
1835 const char *querysep;
1836 const char *qualsep;
1837 Oid queryoids[RI_MAX_NUMKEYS];
1840 * The query string built is
1841 * UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
1842 * WHERE $1 = fkatt1 [AND ...]
1843 * The type id's for the $ parameters are those of the
1844 * corresponding PK attributes.
1847 initStringInfo(&querybuf);
1848 initStringInfo(&qualbuf);
1849 quoteRelationName(fkrelname, fk_rel);
1850 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
1853 for (i = 0; i < riinfo.nkeys; i++)
1855 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
1856 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
1858 quoteOneName(attname,
1859 RIAttName(fk_rel, riinfo.fk_attnums[i]));
1860 appendStringInfo(&querybuf,
1863 sprintf(paramname, "$%d", i + 1);
1864 ri_GenerateQual(&qualbuf, qualsep,
1866 riinfo.pf_eq_oprs[i],
1870 queryoids[i] = pk_type;
1872 appendStringInfoString(&querybuf, qualbuf.data);
1874 /* Prepare and save the plan */
1875 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
1876 &qkey, fk_rel, pk_rel, true);
1880 * We have a plan now. Run it to check for existing references.
1882 ri_PerformCheck(&qkey, qplan,
1885 true, /* must detect new rows */
1887 NameStr(riinfo.conname));
1889 if (SPI_finish() != SPI_OK_FINISH)
1890 elog(ERROR, "SPI_finish failed");
1892 heap_close(fk_rel, RowExclusiveLock);
1894 return PointerGetDatum(NULL);
1897 * Handle MATCH PARTIAL set null delete.
1899 case FKCONSTR_MATCH_PARTIAL:
1901 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1902 errmsg("MATCH PARTIAL not yet implemented")));
1903 return PointerGetDatum(NULL);
1909 elog(ERROR, "invalid confmatchtype");
1910 return PointerGetDatum(NULL);
1915 * RI_FKey_setnull_upd -
1917 * Set foreign key references to NULL at update event on PK table.
1921 RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
1923 TriggerData *trigdata = (TriggerData *) fcinfo->context;
1924 RI_ConstraintInfo riinfo;
1932 bool use_cached_query;
1935 * Check that this is a valid trigger call on the right time and event.
1937 ri_CheckTrigger(fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE);
1942 ri_FetchConstraintInfo(&riinfo,
1943 trigdata->tg_trigger, trigdata->tg_relation, true);
1946 * Nothing to do if no column names to compare given
1948 if (riinfo.nkeys == 0)
1949 return PointerGetDatum(NULL);
1952 * Get the relation descriptors of the FK and PK tables and the old tuple.
1954 * fk_rel is opened in RowExclusiveLock mode since that's what our
1955 * eventual UPDATE will get on it.
1957 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
1958 pk_rel = trigdata->tg_relation;
1959 new_row = trigdata->tg_newtuple;
1960 old_row = trigdata->tg_trigtuple;
1962 switch (riinfo.confmatchtype)
1965 * SQL3 11.9 <referential constraint definition>
1966 * Gereral rules 7) a) ii) 2):
1968 * ... ON UPDATE SET NULL
1971 case FKCONSTR_MATCH_UNSPECIFIED:
1972 case FKCONSTR_MATCH_FULL:
1973 ri_BuildQueryKeyFull(&qkey, &riinfo,
1974 RI_PLAN_SETNULL_UPD_DOUPDATE);
1976 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
1978 case RI_KEYS_ALL_NULL:
1979 case RI_KEYS_SOME_NULL:
1982 * No update - MATCH FULL means there cannot be any
1983 * reference to old key if it contains NULL
1985 heap_close(fk_rel, RowExclusiveLock);
1986 return PointerGetDatum(NULL);
1988 case RI_KEYS_NONE_NULL:
1991 * Have a full qualified key - continue below
1997 * No need to do anything if old and new keys are equal
1999 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
2001 heap_close(fk_rel, RowExclusiveLock);
2002 return PointerGetDatum(NULL);
2005 if (SPI_connect() != SPI_OK_CONNECT)
2006 elog(ERROR, "SPI_connect failed");
2009 * "MATCH <unspecified>" only changes columns corresponding to the
2010 * referenced columns that have changed in pk_rel. This means the
2011 * "SET attrn=NULL [, attrn=NULL]" string will be change as well.
2012 * In this case, we need to build a temporary plan rather than use
2013 * our cached plan, unless the update happens to change all
2014 * columns in the key. Fortunately, for the most common case of a
2015 * single-column foreign key, this will be true.
2017 * In case you're wondering, the inequality check works because we
2018 * know that the old key value has no NULLs (see above).
2021 use_cached_query = (riinfo.confmatchtype == FKCONSTR_MATCH_FULL) ||
2022 ri_AllKeysUnequal(pk_rel, old_row, new_row,
2026 * Fetch or prepare a saved plan for the set null update operation
2027 * if possible, or build a temporary plan if not.
2029 if (!use_cached_query ||
2030 (qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
2032 StringInfoData querybuf;
2033 StringInfoData qualbuf;
2034 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2035 char attname[MAX_QUOTED_NAME_LEN];
2037 const char *querysep;
2038 const char *qualsep;
2039 Oid queryoids[RI_MAX_NUMKEYS];
2042 * The query string built is
2043 * UPDATE ONLY <fktable> SET fkatt1 = NULL [, ...]
2044 * WHERE $1 = fkatt1 [AND ...]
2045 * The type id's for the $ parameters are those of the
2046 * corresponding PK attributes.
2049 initStringInfo(&querybuf);
2050 initStringInfo(&qualbuf);
2051 quoteRelationName(fkrelname, fk_rel);
2052 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2055 for (i = 0; i < riinfo.nkeys; i++)
2057 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2058 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2060 quoteOneName(attname,
2061 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2064 * MATCH <unspecified> - only change columns corresponding
2065 * to changed columns in pk_rel's key
2067 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
2068 !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
2071 appendStringInfo(&querybuf,
2076 sprintf(paramname, "$%d", i + 1);
2077 ri_GenerateQual(&qualbuf, qualsep,
2079 riinfo.pf_eq_oprs[i],
2082 queryoids[i] = pk_type;
2084 appendStringInfoString(&querybuf, qualbuf.data);
2087 * Prepare the plan. Save it only if we're building the
2090 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2091 &qkey, fk_rel, pk_rel,
2096 * We have a plan now. Run it to update the existing references.
2098 ri_PerformCheck(&qkey, qplan,
2101 true, /* must detect new rows */
2103 NameStr(riinfo.conname));
2105 if (SPI_finish() != SPI_OK_FINISH)
2106 elog(ERROR, "SPI_finish failed");
2108 heap_close(fk_rel, RowExclusiveLock);
2110 return PointerGetDatum(NULL);
2113 * Handle MATCH PARTIAL set null update.
2115 case FKCONSTR_MATCH_PARTIAL:
2117 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2118 errmsg("MATCH PARTIAL not yet implemented")));
2119 return PointerGetDatum(NULL);
2125 elog(ERROR, "invalid confmatchtype");
2126 return PointerGetDatum(NULL);
2131 * RI_FKey_setdefault_del -
2133 * Set foreign key references to defaults at delete event on PK table.
2137 RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
2139 TriggerData *trigdata = (TriggerData *) fcinfo->context;
2140 RI_ConstraintInfo riinfo;
2148 * Check that this is a valid trigger call on the right time and event.
2150 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE);
2155 ri_FetchConstraintInfo(&riinfo,
2156 trigdata->tg_trigger, trigdata->tg_relation, true);
2159 * Nothing to do if no column names to compare given
2161 if (riinfo.nkeys == 0)
2162 return PointerGetDatum(NULL);
2165 * Get the relation descriptors of the FK and PK tables and the old tuple.
2167 * fk_rel is opened in RowExclusiveLock mode since that's what our
2168 * eventual UPDATE will get on it.
2170 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
2171 pk_rel = trigdata->tg_relation;
2172 old_row = trigdata->tg_trigtuple;
2174 switch (riinfo.confmatchtype)
2177 * SQL3 11.9 <referential constraint definition>
2178 * Gereral rules 6) a) iii):
2179 * MATCH <UNSPECIFIED> or MATCH FULL
2180 * ... ON DELETE SET DEFAULT
2183 case FKCONSTR_MATCH_UNSPECIFIED:
2184 case FKCONSTR_MATCH_FULL:
2185 ri_BuildQueryKeyFull(&qkey, &riinfo,
2186 RI_PLAN_SETNULL_DEL_DOUPDATE);
2188 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
2190 case RI_KEYS_ALL_NULL:
2191 case RI_KEYS_SOME_NULL:
2194 * No update - MATCH FULL means there cannot be any
2195 * reference to old key if it contains NULL
2197 heap_close(fk_rel, RowExclusiveLock);
2198 return PointerGetDatum(NULL);
2200 case RI_KEYS_NONE_NULL:
2203 * Have a full qualified key - continue below
2208 if (SPI_connect() != SPI_OK_CONNECT)
2209 elog(ERROR, "SPI_connect failed");
2212 * Prepare a plan for the set default delete operation.
2213 * Unfortunately we need to do it on every invocation because the
2214 * default value could potentially change between calls.
2217 StringInfoData querybuf;
2218 StringInfoData qualbuf;
2219 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2220 char attname[MAX_QUOTED_NAME_LEN];
2222 const char *querysep;
2223 const char *qualsep;
2224 Oid queryoids[RI_MAX_NUMKEYS];
2228 * The query string built is
2229 * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
2230 * WHERE $1 = fkatt1 [AND ...]
2231 * The type id's for the $ parameters are those of the
2232 * corresponding PK attributes.
2235 initStringInfo(&querybuf);
2236 initStringInfo(&qualbuf);
2237 quoteRelationName(fkrelname, fk_rel);
2238 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2241 for (i = 0; i < riinfo.nkeys; i++)
2243 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2244 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2246 quoteOneName(attname,
2247 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2248 appendStringInfo(&querybuf,
2251 sprintf(paramname, "$%d", i + 1);
2252 ri_GenerateQual(&qualbuf, qualsep,
2254 riinfo.pf_eq_oprs[i],
2258 queryoids[i] = pk_type;
2260 appendStringInfoString(&querybuf, qualbuf.data);
2262 /* Prepare the plan, don't save it */
2263 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2264 &qkey, fk_rel, pk_rel, false);
2268 * We have a plan now. Run it to update the existing references.
2270 ri_PerformCheck(&qkey, qplan,
2273 true, /* must detect new rows */
2275 NameStr(riinfo.conname));
2277 if (SPI_finish() != SPI_OK_FINISH)
2278 elog(ERROR, "SPI_finish failed");
2280 heap_close(fk_rel, RowExclusiveLock);
2283 * In the case we delete the row who's key is equal to the default
2284 * values AND a referencing row in the foreign key table exists,
2285 * we would just have updated it to the same values. We need to do
2286 * another lookup now and in case a reference exists, abort the
2287 * operation. That is already implemented in the NO ACTION
2290 RI_FKey_noaction_del(fcinfo);
2292 return PointerGetDatum(NULL);
2295 * Handle MATCH PARTIAL set null delete.
2297 case FKCONSTR_MATCH_PARTIAL:
2299 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2300 errmsg("MATCH PARTIAL not yet implemented")));
2301 return PointerGetDatum(NULL);
2307 elog(ERROR, "invalid confmatchtype");
2308 return PointerGetDatum(NULL);
2313 * RI_FKey_setdefault_upd -
2315 * Set foreign key references to defaults at update event on PK table.
2319 RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
2321 TriggerData *trigdata = (TriggerData *) fcinfo->context;
2322 RI_ConstraintInfo riinfo;
2331 * Check that this is a valid trigger call on the right time and event.
2333 ri_CheckTrigger(fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE);
2338 ri_FetchConstraintInfo(&riinfo,
2339 trigdata->tg_trigger, trigdata->tg_relation, true);
2342 * Nothing to do if no column names to compare given
2344 if (riinfo.nkeys == 0)
2345 return PointerGetDatum(NULL);
2348 * Get the relation descriptors of the FK and PK tables and the old tuple.
2350 * fk_rel is opened in RowExclusiveLock mode since that's what our
2351 * eventual UPDATE will get on it.
2353 fk_rel = heap_open(riinfo.fk_relid, RowExclusiveLock);
2354 pk_rel = trigdata->tg_relation;
2355 new_row = trigdata->tg_newtuple;
2356 old_row = trigdata->tg_trigtuple;
2358 switch (riinfo.confmatchtype)
2361 * SQL3 11.9 <referential constraint definition>
2362 * Gereral rules 7) a) iii):
2363 * MATCH <UNSPECIFIED> or MATCH FULL
2364 * ... ON UPDATE SET DEFAULT
2367 case FKCONSTR_MATCH_UNSPECIFIED:
2368 case FKCONSTR_MATCH_FULL:
2369 ri_BuildQueryKeyFull(&qkey, &riinfo,
2370 RI_PLAN_SETNULL_DEL_DOUPDATE);
2372 switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX))
2374 case RI_KEYS_ALL_NULL:
2375 case RI_KEYS_SOME_NULL:
2378 * No update - MATCH FULL means there cannot be any
2379 * reference to old key if it contains NULL
2381 heap_close(fk_rel, RowExclusiveLock);
2382 return PointerGetDatum(NULL);
2384 case RI_KEYS_NONE_NULL:
2387 * Have a full qualified key - continue below
2393 * No need to do anything if old and new keys are equal
2395 if (ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true))
2397 heap_close(fk_rel, RowExclusiveLock);
2398 return PointerGetDatum(NULL);
2401 if (SPI_connect() != SPI_OK_CONNECT)
2402 elog(ERROR, "SPI_connect failed");
2405 * Prepare a plan for the set default delete operation.
2406 * Unfortunately we need to do it on every invocation because the
2407 * default value could potentially change between calls.
2410 StringInfoData querybuf;
2411 StringInfoData qualbuf;
2412 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2413 char attname[MAX_QUOTED_NAME_LEN];
2415 const char *querysep;
2416 const char *qualsep;
2417 Oid queryoids[RI_MAX_NUMKEYS];
2421 * The query string built is
2422 * UPDATE ONLY <fktable> SET fkatt1 = DEFAULT [, ...]
2423 * WHERE $1 = fkatt1 [AND ...]
2424 * The type id's for the $ parameters are those of the
2425 * corresponding PK attributes.
2428 initStringInfo(&querybuf);
2429 initStringInfo(&qualbuf);
2430 quoteRelationName(fkrelname, fk_rel);
2431 appendStringInfo(&querybuf, "UPDATE ONLY %s SET", fkrelname);
2434 for (i = 0; i < riinfo.nkeys; i++)
2436 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2437 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2439 quoteOneName(attname,
2440 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2443 * MATCH <unspecified> - only change columns corresponding
2444 * to changed columns in pk_rel's key
2446 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL ||
2447 !ri_OneKeyEqual(pk_rel, i, old_row, new_row,
2450 appendStringInfo(&querybuf,
2455 sprintf(paramname, "$%d", i + 1);
2456 ri_GenerateQual(&qualbuf, qualsep,
2458 riinfo.pf_eq_oprs[i],
2461 queryoids[i] = pk_type;
2463 appendStringInfoString(&querybuf, qualbuf.data);
2465 /* Prepare the plan, don't save it */
2466 qplan = ri_PlanCheck(querybuf.data, riinfo.nkeys, queryoids,
2467 &qkey, fk_rel, pk_rel, false);
2471 * We have a plan now. Run it to update the existing references.
2473 ri_PerformCheck(&qkey, qplan,
2476 true, /* must detect new rows */
2478 NameStr(riinfo.conname));
2480 if (SPI_finish() != SPI_OK_FINISH)
2481 elog(ERROR, "SPI_finish failed");
2483 heap_close(fk_rel, RowExclusiveLock);
2486 * In the case we updated the row who's key was equal to the
2487 * default values AND a referencing row in the foreign key table
2488 * exists, we would just have updated it to the same values. We
2489 * need to do another lookup now and in case a reference exists,
2490 * abort the operation. That is already implemented in the NO
2493 RI_FKey_noaction_upd(fcinfo);
2495 return PointerGetDatum(NULL);
2498 * Handle MATCH PARTIAL set null delete.
2500 case FKCONSTR_MATCH_PARTIAL:
2502 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2503 errmsg("MATCH PARTIAL not yet implemented")));
2504 return PointerGetDatum(NULL);
2510 elog(ERROR, "invalid confmatchtype");
2511 return PointerGetDatum(NULL);
2516 * RI_FKey_keyequal_upd_pk -
2518 * Check if we have a key change on an update to a PK relation. This is
2519 * used by the AFTER trigger queue manager to see if it can skip queuing
2520 * an instance of an RI trigger.
2524 RI_FKey_keyequal_upd_pk(Trigger *trigger, Relation pk_rel,
2525 HeapTuple old_row, HeapTuple new_row)
2527 RI_ConstraintInfo riinfo;
2532 ri_FetchConstraintInfo(&riinfo, trigger, pk_rel, true);
2535 * Nothing to do if no column names to compare given
2537 if (riinfo.nkeys == 0)
2540 switch (riinfo.confmatchtype)
2542 case FKCONSTR_MATCH_UNSPECIFIED:
2543 case FKCONSTR_MATCH_FULL:
2544 /* Return true if keys are equal */
2545 return ri_KeysEqual(pk_rel, old_row, new_row, &riinfo, true);
2547 /* Handle MATCH PARTIAL set null delete. */
2548 case FKCONSTR_MATCH_PARTIAL:
2550 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2551 errmsg("MATCH PARTIAL not yet implemented")));
2556 elog(ERROR, "invalid confmatchtype");
2561 * RI_FKey_keyequal_upd_fk -
2563 * Check if we have a key change on an update to an FK relation. This is
2564 * used by the AFTER trigger queue manager to see if it can skip queuing
2565 * an instance of an RI trigger.
2569 RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel,
2570 HeapTuple old_row, HeapTuple new_row)
2572 RI_ConstraintInfo riinfo;
2577 ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
2580 * Nothing to do if no column names to compare given
2582 if (riinfo.nkeys == 0)
2585 switch (riinfo.confmatchtype)
2587 case FKCONSTR_MATCH_UNSPECIFIED:
2588 case FKCONSTR_MATCH_FULL:
2589 /* Return true if keys are equal */
2590 return ri_KeysEqual(fk_rel, old_row, new_row, &riinfo, false);
2592 /* Handle MATCH PARTIAL set null delete. */
2593 case FKCONSTR_MATCH_PARTIAL:
2595 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2596 errmsg("MATCH PARTIAL not yet implemented")));
2601 elog(ERROR, "invalid confmatchtype");
2606 * RI_Initial_Check -
2608 * Check an entire table for non-matching values using a single query.
2609 * This is not a trigger procedure, but is called during ALTER TABLE
2610 * ADD FOREIGN KEY to validate the initial table contents.
2612 * We expect that the caller has made provision to prevent any problems
2613 * caused by concurrent actions. This could be either by locking rel and
2614 * pkrel at ShareRowExclusiveLock or higher, or by otherwise ensuring
2615 * that triggers implementing the checks are already active.
2616 * Hence, we do not need to lock individual rows for the check.
2618 * If the check fails because the current user doesn't have permissions
2619 * to read both tables, return false to let our caller know that they will
2620 * need to do something else to check the constraint.
2624 RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
2626 RI_ConstraintInfo riinfo;
2627 const char *constrname = trigger->tgname;
2628 StringInfoData querybuf;
2629 char pkrelname[MAX_QUOTED_REL_NAME_LEN];
2630 char fkrelname[MAX_QUOTED_REL_NAME_LEN];
2631 char pkattname[MAX_QUOTED_NAME_LEN + 3];
2632 char fkattname[MAX_QUOTED_NAME_LEN + 3];
2633 RangeTblEntry *pkrte;
2634 RangeTblEntry *fkrte;
2638 char workmembuf[32];
2642 /* Fetch constraint info. */
2643 ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
2646 * Check to make sure current user has enough permissions to do the test
2647 * query. (If not, caller can fall back to the trigger method, which
2648 * works because it changes user IDs on the fly.)
2650 * XXX are there any other show-stopper conditions to check?
2652 pkrte = makeNode(RangeTblEntry);
2653 pkrte->rtekind = RTE_RELATION;
2654 pkrte->relid = RelationGetRelid(pk_rel);
2655 pkrte->relkind = pk_rel->rd_rel->relkind;
2656 pkrte->requiredPerms = ACL_SELECT;
2658 fkrte = makeNode(RangeTblEntry);
2659 fkrte->rtekind = RTE_RELATION;
2660 fkrte->relid = RelationGetRelid(fk_rel);
2661 fkrte->relkind = fk_rel->rd_rel->relkind;
2662 fkrte->requiredPerms = ACL_SELECT;
2664 for (i = 0; i < riinfo.nkeys; i++)
2668 attno = riinfo.pk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
2669 pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno);
2671 attno = riinfo.fk_attnums[i] - FirstLowInvalidHeapAttributeNumber;
2672 fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno);
2675 if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false))
2679 * The query string built is:
2680 * SELECT fk.keycols FROM ONLY relname fk
2681 * LEFT OUTER JOIN ONLY pkrelname pk
2682 * ON (pk.pkkeycol1=fk.keycol1 [AND ...])
2683 * WHERE pk.pkkeycol1 IS NULL AND
2684 * For MATCH unspecified:
2685 * (fk.keycol1 IS NOT NULL [AND ...])
2687 * (fk.keycol1 IS NOT NULL [OR ...])
2689 * We attach COLLATE clauses to the operators when comparing columns
2690 * that have different collations.
2693 initStringInfo(&querybuf);
2694 appendStringInfo(&querybuf, "SELECT ");
2696 for (i = 0; i < riinfo.nkeys; i++)
2698 quoteOneName(fkattname,
2699 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2700 appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
2704 quoteRelationName(pkrelname, pk_rel);
2705 quoteRelationName(fkrelname, fk_rel);
2706 appendStringInfo(&querybuf,
2707 " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
2708 fkrelname, pkrelname);
2710 strcpy(pkattname, "pk.");
2711 strcpy(fkattname, "fk.");
2713 for (i = 0; i < riinfo.nkeys; i++)
2715 Oid pk_type = RIAttType(pk_rel, riinfo.pk_attnums[i]);
2716 Oid fk_type = RIAttType(fk_rel, riinfo.fk_attnums[i]);
2717 Oid pk_coll = RIAttCollation(pk_rel, riinfo.pk_attnums[i]);
2718 Oid fk_coll = RIAttCollation(fk_rel, riinfo.fk_attnums[i]);
2720 quoteOneName(pkattname + 3,
2721 RIAttName(pk_rel, riinfo.pk_attnums[i]));
2722 quoteOneName(fkattname + 3,
2723 RIAttName(fk_rel, riinfo.fk_attnums[i]));
2724 ri_GenerateQual(&querybuf, sep,
2726 riinfo.pf_eq_oprs[i],
2727 fkattname, fk_type);
2728 if (pk_coll != fk_coll)
2729 ri_GenerateQualCollation(&querybuf, pk_coll);
2734 * It's sufficient to test any one pk attribute for null to detect a join
2737 quoteOneName(pkattname, RIAttName(pk_rel, riinfo.pk_attnums[0]));
2738 appendStringInfo(&querybuf, ") WHERE pk.%s IS NULL AND (", pkattname);
2741 for (i = 0; i < riinfo.nkeys; i++)
2743 quoteOneName(fkattname, RIAttName(fk_rel, riinfo.fk_attnums[i]));
2744 appendStringInfo(&querybuf,
2745 "%sfk.%s IS NOT NULL",
2747 switch (riinfo.confmatchtype)
2749 case FKCONSTR_MATCH_UNSPECIFIED:
2752 case FKCONSTR_MATCH_FULL:
2755 case FKCONSTR_MATCH_PARTIAL:
2757 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2758 errmsg("MATCH PARTIAL not yet implemented")));
2761 elog(ERROR, "unrecognized match type: %d",
2762 riinfo.confmatchtype);
2766 appendStringInfo(&querybuf, ")");
2769 * Temporarily increase work_mem so that the check query can be executed
2770 * more efficiently. It seems okay to do this because the query is simple
2771 * enough to not use a multiple of work_mem, and one typically would not
2772 * have many large foreign-key validations happening concurrently. So
2773 * this seems to meet the criteria for being considered a "maintenance"
2774 * operation, and accordingly we use maintenance_work_mem.
2776 * We do the equivalent of "SET LOCAL work_mem" so that transaction abort
2777 * will restore the old value if we lose control due to an error.
2779 old_work_mem = work_mem;
2780 snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
2781 (void) set_config_option("work_mem", workmembuf,
2782 PGC_USERSET, PGC_S_SESSION,
2783 GUC_ACTION_LOCAL, true);
2785 if (SPI_connect() != SPI_OK_CONNECT)
2786 elog(ERROR, "SPI_connect failed");
2789 * Generate the plan. We don't need to cache it, and there are no
2790 * arguments to the plan.
2792 qplan = SPI_prepare(querybuf.data, 0, NULL);
2795 elog(ERROR, "SPI_prepare returned %d for %s",
2796 SPI_result, querybuf.data);
2799 * Run the plan. For safety we force a current snapshot to be used. (In
2800 * transaction-snapshot mode, this arguably violates transaction isolation
2801 * rules, but we really haven't got much choice.) We don't need to
2802 * register the snapshot, because SPI_execute_snapshot will see to it. We
2803 * need at most one tuple returned, so pass limit = 1.
2805 spi_result = SPI_execute_snapshot(qplan,
2807 GetLatestSnapshot(),
2812 if (spi_result != SPI_OK_SELECT)
2813 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
2815 /* Did we find a tuple violating the constraint? */
2816 if (SPI_processed > 0)
2818 HeapTuple tuple = SPI_tuptable->vals[0];
2819 TupleDesc tupdesc = SPI_tuptable->tupdesc;
2823 * If it's MATCH FULL, and there are any nulls in the FK keys,
2824 * complain about that rather than the lack of a match. MATCH FULL
2825 * disallows partially-null FK rows.
2827 if (riinfo.confmatchtype == FKCONSTR_MATCH_FULL)
2829 bool isnull = false;
2831 for (i = 1; i <= riinfo.nkeys; i++)
2833 (void) SPI_getbinval(tuple, tupdesc, i, &isnull);
2839 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
2840 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
2841 RelationGetRelationName(fk_rel),
2843 errdetail("MATCH FULL does not allow mixing of null and nonnull key values.")));
2847 * Although we didn't cache the query, we need to set up a fake query
2848 * key to pass to ri_ReportViolation.
2850 MemSet(&qkey, 0, sizeof(qkey));
2851 qkey.constr_queryno = RI_PLAN_CHECK_LOOKUPPK;
2852 qkey.nkeypairs = riinfo.nkeys;
2853 for (i = 0; i < riinfo.nkeys; i++)
2854 qkey.keypair[i][RI_KEYPAIR_FK_IDX] = i + 1;
2856 ri_ReportViolation(&qkey, constrname,
2862 if (SPI_finish() != SPI_OK_FINISH)
2863 elog(ERROR, "SPI_finish failed");
2866 * Restore work_mem for the remainder of the current transaction. This is
2867 * another SET LOCAL, so it won't affect the session value.
2869 snprintf(workmembuf, sizeof(workmembuf), "%d", old_work_mem);
2870 (void) set_config_option("work_mem", workmembuf,
2871 PGC_USERSET, PGC_S_SESSION,
2872 GUC_ACTION_LOCAL, true);
2879 * Local functions below
2885 * quoteOneName --- safely quote a single SQL name
2887 * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0)
2890 quoteOneName(char *buffer, const char *name)
2892 /* Rather than trying to be smart, just always quote it. */
2898 *buffer++ = *name++;
2905 * quoteRelationName --- safely quote a fully qualified relation name
2907 * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0)
2910 quoteRelationName(char *buffer, Relation rel)
2912 quoteOneName(buffer, get_namespace_name(RelationGetNamespace(rel)));
2913 buffer += strlen(buffer);
2915 quoteOneName(buffer, RelationGetRelationName(rel));
2919 * ri_GenerateQual --- generate a WHERE clause equating two variables
2921 * The idea is to append " sep leftop op rightop" to buf. The complexity
2922 * comes from needing to be sure that the parser will select the desired
2923 * operator. We always name the operator using OPERATOR(schema.op) syntax
2924 * (readability isn't a big priority here), so as to avoid search-path
2925 * uncertainties. We have to emit casts too, if either input isn't already
2926 * the input type of the operator; else we are at the mercy of the parser's
2927 * heuristics for ambiguous-operator resolution.
2930 ri_GenerateQual(StringInfo buf,
2932 const char *leftop, Oid leftoptype,
2934 const char *rightop, Oid rightoptype)
2937 Form_pg_operator operform;
2941 opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
2942 if (!HeapTupleIsValid(opertup))
2943 elog(ERROR, "cache lookup failed for operator %u", opoid);
2944 operform = (Form_pg_operator) GETSTRUCT(opertup);
2945 Assert(operform->oprkind == 'b');
2946 oprname = NameStr(operform->oprname);
2948 nspname = get_namespace_name(operform->oprnamespace);
2950 appendStringInfo(buf, " %s %s", sep, leftop);
2951 if (leftoptype != operform->oprleft)
2952 ri_add_cast_to(buf, operform->oprleft);
2953 appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
2954 appendStringInfoString(buf, oprname);
2955 appendStringInfo(buf, ") %s", rightop);
2956 if (rightoptype != operform->oprright)
2957 ri_add_cast_to(buf, operform->oprright);
2959 ReleaseSysCache(opertup);
2963 * Add a cast specification to buf. We spell out the type name the hard way,
2964 * intentionally not using format_type_be(). This is to avoid corner cases
2965 * for CHARACTER, BIT, and perhaps other types, where specifying the type
2966 * using SQL-standard syntax results in undesirable data truncation. By
2967 * doing it this way we can be certain that the cast will have default (-1)
2971 ri_add_cast_to(StringInfo buf, Oid typid)
2974 Form_pg_type typform;
2978 typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
2979 if (!HeapTupleIsValid(typetup))
2980 elog(ERROR, "cache lookup failed for type %u", typid);
2981 typform = (Form_pg_type) GETSTRUCT(typetup);
2983 typname = NameStr(typform->typname);
2984 nspname = get_namespace_name(typform->typnamespace);
2986 appendStringInfo(buf, "::%s.%s",
2987 quote_identifier(nspname), quote_identifier(typname));
2989 ReleaseSysCache(typetup);
2993 * ri_GenerateQualCollation --- add a COLLATE spec to a WHERE clause
2995 * At present, we intentionally do not use this function for RI queries that
2996 * compare a variable to a $n parameter. Since parameter symbols always have
2997 * default collation, the effect will be to use the variable's collation.
2998 * Now that is only strictly correct when testing the referenced column, since
2999 * the SQL standard specifies that RI comparisons should use the referenced
3000 * column's collation. However, so long as all collations have the same
3001 * notion of equality (which they do, because texteq reduces to bitwise
3002 * equality), there's no visible semantic impact from using the referencing
3003 * column's collation when testing it, and this is a good thing to do because
3004 * it lets us use a normal index on the referencing column. However, we do
3005 * have to use this function when directly comparing the referencing and
3006 * referenced columns, if they are of different collations; else the parser
3007 * will fail to resolve the collation to use.
3010 ri_GenerateQualCollation(StringInfo buf, Oid collation)
3013 Form_pg_collation colltup;
3015 char onename[MAX_QUOTED_NAME_LEN];
3017 /* Nothing to do if it's a noncollatable data type */
3018 if (!OidIsValid(collation))
3021 tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation));
3022 if (!HeapTupleIsValid(tp))
3023 elog(ERROR, "cache lookup failed for collation %u", collation);
3024 colltup = (Form_pg_collation) GETSTRUCT(tp);
3025 collname = NameStr(colltup->collname);
3028 * We qualify the name always, for simplicity and to ensure the query is
3029 * not search-path-dependent.
3031 quoteOneName(onename, get_namespace_name(colltup->collnamespace));
3032 appendStringInfo(buf, " COLLATE %s", onename);
3033 quoteOneName(onename, collname);
3034 appendStringInfo(buf, ".%s", onename);
3036 ReleaseSysCache(tp);
3040 * ri_BuildQueryKeyFull -
3042 * Build up a new hashtable key for a prepared SPI plan of a
3043 * constraint trigger of MATCH FULL.
3045 * key: output argument, *key is filled in based on the other arguments
3046 * riinfo: info from pg_constraint entry
3047 * constr_queryno: an internal number of the query inside the proc
3049 * At least for MATCH FULL this builds a unique key per plan.
3053 ri_BuildQueryKeyFull(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
3054 int32 constr_queryno)
3058 MemSet(key, 0, sizeof(RI_QueryKey));
3059 key->constr_type = FKCONSTR_MATCH_FULL;
3060 key->constr_id = riinfo->constraint_id;
3061 key->constr_queryno = constr_queryno;
3062 key->fk_relid = riinfo->fk_relid;
3063 key->pk_relid = riinfo->pk_relid;
3064 key->nkeypairs = riinfo->nkeys;
3065 for (i = 0; i < riinfo->nkeys; i++)
3067 key->keypair[i][RI_KEYPAIR_FK_IDX] = riinfo->fk_attnums[i];
3068 key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
3073 * Check that RI trigger function was called in expected context
3076 ri_CheckTrigger(FunctionCallInfo fcinfo, const char *funcname, int tgkind)
3078 TriggerData *trigdata = (TriggerData *) fcinfo->context;
3080 if (!CALLED_AS_TRIGGER(fcinfo))
3082 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3083 errmsg("function \"%s\" was not called by trigger manager", funcname)));
3086 * Check proper event
3088 if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) ||
3089 !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
3091 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3092 errmsg("function \"%s\" must be fired AFTER ROW", funcname)));
3096 case RI_TRIGTYPE_INSERT:
3097 if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
3099 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3100 errmsg("function \"%s\" must be fired for INSERT", funcname)));
3102 case RI_TRIGTYPE_UPDATE:
3103 if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
3105 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3106 errmsg("function \"%s\" must be fired for UPDATE", funcname)));
3108 case RI_TRIGTYPE_INUP:
3109 if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) &&
3110 !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
3112 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3113 errmsg("function \"%s\" must be fired for INSERT or UPDATE",
3116 case RI_TRIGTYPE_DELETE:
3117 if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
3119 (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
3120 errmsg("function \"%s\" must be fired for DELETE", funcname)));
3127 * Fetch the pg_constraint entry for the FK constraint, and fill *riinfo
3130 ri_FetchConstraintInfo(RI_ConstraintInfo *riinfo,
3131 Trigger *trigger, Relation trig_rel, bool rel_is_pk)
3133 Oid constraintOid = trigger->tgconstraint;
3135 Form_pg_constraint conForm;
3142 * Check that the FK constraint's OID is available; it might not be if
3143 * we've been invoked via an ordinary trigger or an old-style "constraint
3146 if (!OidIsValid(constraintOid))
3148 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3149 errmsg("no pg_constraint entry for trigger \"%s\" on table \"%s\"",
3150 trigger->tgname, RelationGetRelationName(trig_rel)),
3151 errhint("Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT.")));
3153 /* OK, fetch the tuple */
3154 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3155 if (!HeapTupleIsValid(tup)) /* should not happen */
3156 elog(ERROR, "cache lookup failed for constraint %u", constraintOid);
3157 conForm = (Form_pg_constraint) GETSTRUCT(tup);
3159 /* Do some easy cross-checks against the trigger call data */
3162 if (conForm->contype != CONSTRAINT_FOREIGN ||
3163 conForm->conrelid != trigger->tgconstrrelid ||
3164 conForm->confrelid != RelationGetRelid(trig_rel))
3165 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
3166 trigger->tgname, RelationGetRelationName(trig_rel));
3170 if (conForm->contype != CONSTRAINT_FOREIGN ||
3171 conForm->conrelid != RelationGetRelid(trig_rel) ||
3172 conForm->confrelid != trigger->tgconstrrelid)
3173 elog(ERROR, "wrong pg_constraint entry for trigger \"%s\" on table \"%s\"",
3174 trigger->tgname, RelationGetRelationName(trig_rel));
3177 /* And extract data */
3178 riinfo->constraint_id = constraintOid;
3179 memcpy(&riinfo->conname, &conForm->conname, sizeof(NameData));
3180 riinfo->pk_relid = conForm->confrelid;
3181 riinfo->fk_relid = conForm->conrelid;
3182 riinfo->confupdtype = conForm->confupdtype;
3183 riinfo->confdeltype = conForm->confdeltype;
3184 riinfo->confmatchtype = conForm->confmatchtype;
3187 * We expect the arrays to be 1-D arrays of the right types; verify that.
3188 * We don't need to use deconstruct_array() since the array data is just
3189 * going to look like a C array of values.
3191 adatum = SysCacheGetAttr(CONSTROID, tup,
3192 Anum_pg_constraint_conkey, &isNull);
3194 elog(ERROR, "null conkey for constraint %u", constraintOid);
3195 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3196 numkeys = ARR_DIMS(arr)[0];
3197 if (ARR_NDIM(arr) != 1 ||
3199 numkeys > RI_MAX_NUMKEYS ||
3201 ARR_ELEMTYPE(arr) != INT2OID)
3202 elog(ERROR, "conkey is not a 1-D smallint array");
3203 riinfo->nkeys = numkeys;
3204 memcpy(riinfo->fk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
3205 if ((Pointer) arr != DatumGetPointer(adatum))
3206 pfree(arr); /* free de-toasted copy, if any */
3208 adatum = SysCacheGetAttr(CONSTROID, tup,
3209 Anum_pg_constraint_confkey, &isNull);
3211 elog(ERROR, "null confkey for constraint %u", constraintOid);
3212 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3213 numkeys = ARR_DIMS(arr)[0];
3214 if (ARR_NDIM(arr) != 1 ||
3215 numkeys != riinfo->nkeys ||
3216 numkeys > RI_MAX_NUMKEYS ||
3218 ARR_ELEMTYPE(arr) != INT2OID)
3219 elog(ERROR, "confkey is not a 1-D smallint array");
3220 memcpy(riinfo->pk_attnums, ARR_DATA_PTR(arr), numkeys * sizeof(int16));
3221 if ((Pointer) arr != DatumGetPointer(adatum))
3222 pfree(arr); /* free de-toasted copy, if any */
3224 adatum = SysCacheGetAttr(CONSTROID, tup,
3225 Anum_pg_constraint_conpfeqop, &isNull);
3227 elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
3228 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3229 numkeys = ARR_DIMS(arr)[0];
3230 if (ARR_NDIM(arr) != 1 ||
3231 numkeys != riinfo->nkeys ||
3232 numkeys > RI_MAX_NUMKEYS ||
3234 ARR_ELEMTYPE(arr) != OIDOID)
3235 elog(ERROR, "conpfeqop is not a 1-D Oid array");
3236 memcpy(riinfo->pf_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3237 if ((Pointer) arr != DatumGetPointer(adatum))
3238 pfree(arr); /* free de-toasted copy, if any */
3240 adatum = SysCacheGetAttr(CONSTROID, tup,
3241 Anum_pg_constraint_conppeqop, &isNull);
3243 elog(ERROR, "null conppeqop for constraint %u", constraintOid);
3244 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3245 numkeys = ARR_DIMS(arr)[0];
3246 if (ARR_NDIM(arr) != 1 ||
3247 numkeys != riinfo->nkeys ||
3248 numkeys > RI_MAX_NUMKEYS ||
3250 ARR_ELEMTYPE(arr) != OIDOID)
3251 elog(ERROR, "conppeqop is not a 1-D Oid array");
3252 memcpy(riinfo->pp_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3253 if ((Pointer) arr != DatumGetPointer(adatum))
3254 pfree(arr); /* free de-toasted copy, if any */
3256 adatum = SysCacheGetAttr(CONSTROID, tup,
3257 Anum_pg_constraint_conffeqop, &isNull);
3259 elog(ERROR, "null conffeqop for constraint %u", constraintOid);
3260 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
3261 numkeys = ARR_DIMS(arr)[0];
3262 if (ARR_NDIM(arr) != 1 ||
3263 numkeys != riinfo->nkeys ||
3264 numkeys > RI_MAX_NUMKEYS ||
3266 ARR_ELEMTYPE(arr) != OIDOID)
3267 elog(ERROR, "conffeqop is not a 1-D Oid array");
3268 memcpy(riinfo->ff_eq_oprs, ARR_DATA_PTR(arr), numkeys * sizeof(Oid));
3269 if ((Pointer) arr != DatumGetPointer(adatum))
3270 pfree(arr); /* free de-toasted copy, if any */
3272 ReleaseSysCache(tup);
3277 * Prepare execution plan for a query to enforce an RI restriction
3279 * If cache_plan is true, the plan is saved into our plan hashtable
3280 * so that we don't need to plan it again.
3283 ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
3284 RI_QueryKey *qkey, Relation fk_rel, Relation pk_rel,
3290 int save_sec_context;
3293 * The query is always run against the FK table except when this is an
3294 * update/insert trigger on the FK table itself - either
3295 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
3297 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
3298 qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
3303 /* Switch to proper UID to perform check as */
3304 GetUserIdAndSecContext(&save_userid, &save_sec_context);
3305 SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
3306 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
3308 /* Create the plan */
3309 qplan = SPI_prepare(querystr, nargs, argtypes);
3312 elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr);
3314 /* Restore UID and security context */
3315 SetUserIdAndSecContext(save_userid, save_sec_context);
3317 /* Save the plan if requested */
3320 qplan = SPI_saveplan(qplan);
3321 ri_HashPreparedPlan(qkey, qplan);
3328 * Perform a query to enforce an RI restriction
3331 ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
3332 Relation fk_rel, Relation pk_rel,
3333 HeapTuple old_tuple, HeapTuple new_tuple,
3335 int expect_OK, const char *constrname)
3340 Snapshot test_snapshot;
3341 Snapshot crosscheck_snapshot;
3345 int save_sec_context;
3346 Datum vals[RI_MAX_NUMKEYS * 2];
3347 char nulls[RI_MAX_NUMKEYS * 2];
3350 * The query is always run against the FK table except when this is an
3351 * update/insert trigger on the FK table itself - either
3352 * RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS
3354 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK ||
3355 qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK_NOCOLS)
3361 * The values for the query are taken from the table on which the trigger
3362 * is called - it is normally the other one with respect to query_rel. An
3363 * exception is ri_Check_Pk_Match(), which uses the PK table for both (the
3364 * case when constrname == NULL)
3366 if (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK && constrname != NULL)
3368 source_rel = fk_rel;
3369 key_idx = RI_KEYPAIR_FK_IDX;
3373 source_rel = pk_rel;
3374 key_idx = RI_KEYPAIR_PK_IDX;
3377 /* Extract the parameters to be passed into the query */
3380 ri_ExtractValues(qkey, key_idx, source_rel, new_tuple,
3383 ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
3384 vals + qkey->nkeypairs, nulls + qkey->nkeypairs);
3388 ri_ExtractValues(qkey, key_idx, source_rel, old_tuple,
3393 * In READ COMMITTED mode, we just need to use an up-to-date regular
3394 * snapshot, and we will see all rows that could be interesting. But in
3395 * transaction-snapshot mode, we can't change the transaction snapshot. If
3396 * the caller passes detectNewRows == false then it's okay to do the query
3397 * with the transaction snapshot; otherwise we use a current snapshot, and
3398 * tell the executor to error out if it finds any rows under the current
3399 * snapshot that wouldn't be visible per the transaction snapshot. Note
3400 * that SPI_execute_snapshot will register the snapshots, so we don't need
3403 if (IsolationUsesXactSnapshot() && detectNewRows)
3405 CommandCounterIncrement(); /* be sure all my own work is visible */
3406 test_snapshot = GetLatestSnapshot();
3407 crosscheck_snapshot = GetTransactionSnapshot();
3411 /* the default SPI behavior is okay */
3412 test_snapshot = InvalidSnapshot;
3413 crosscheck_snapshot = InvalidSnapshot;
3417 * If this is a select query (e.g., for a 'no action' or 'restrict'
3418 * trigger), we only need to see if there is a single row in the table,
3419 * matching the key. Otherwise, limit = 0 - because we want the query to
3420 * affect ALL the matching rows.
3422 limit = (expect_OK == SPI_OK_SELECT) ? 1 : 0;
3424 /* Switch to proper UID to perform check as */
3425 GetUserIdAndSecContext(&save_userid, &save_sec_context);
3426 SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
3427 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
3429 /* Finally we can run the query. */
3430 spi_result = SPI_execute_snapshot(qplan,
3432 test_snapshot, crosscheck_snapshot,
3433 false, false, limit);
3435 /* Restore UID and security context */
3436 SetUserIdAndSecContext(save_userid, save_sec_context);
3440 elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
3442 if (expect_OK >= 0 && spi_result != expect_OK)
3443 ri_ReportViolation(qkey, constrname ? constrname : "",
3445 new_tuple ? new_tuple : old_tuple,
3449 /* XXX wouldn't it be clearer to do this part at the caller? */
3450 if (constrname && expect_OK == SPI_OK_SELECT &&
3451 (SPI_processed == 0) == (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK))
3452 ri_ReportViolation(qkey, constrname,
3454 new_tuple ? new_tuple : old_tuple,
3458 return SPI_processed != 0;
3462 * Extract fields from a tuple into Datum/nulls arrays
3465 ri_ExtractValues(RI_QueryKey *qkey, int key_idx,
3466 Relation rel, HeapTuple tuple,
3467 Datum *vals, char *nulls)
3472 for (i = 0; i < qkey->nkeypairs; i++)
3474 vals[i] = SPI_getbinval(tuple, rel->rd_att,
3475 qkey->keypair[i][key_idx],
3477 nulls[i] = isnull ? 'n' : ' ';
3482 * Produce an error report
3484 * If the failed constraint was on insert/update to the FK table,
3485 * we want the key names and values extracted from there, and the error
3486 * message to look like 'key blah is not present in PK'.
3487 * Otherwise, the attr names and values come from the PK table and the
3488 * message looks like 'key blah is still referenced from FK'.
3491 ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
3492 Relation pk_rel, Relation fk_rel,
3493 HeapTuple violator, TupleDesc tupdesc,
3496 StringInfoData key_names;
3497 StringInfoData key_values;
3504 (errcode(ERRCODE_INTERNAL_ERROR),
3505 errmsg("referential integrity query on \"%s\" from constraint \"%s\" on \"%s\" gave unexpected result",
3506 RelationGetRelationName(pk_rel),
3508 RelationGetRelationName(fk_rel)),
3509 errhint("This is most likely due to a rule having rewritten the query.")));
3512 * Determine which relation to complain about. If tupdesc wasn't passed
3513 * by caller, assume the violator tuple came from there.
3515 onfk = (qkey->constr_queryno == RI_PLAN_CHECK_LOOKUPPK);
3518 key_idx = RI_KEYPAIR_FK_IDX;
3519 if (tupdesc == NULL)
3520 tupdesc = fk_rel->rd_att;
3524 key_idx = RI_KEYPAIR_PK_IDX;
3525 if (tupdesc == NULL)
3526 tupdesc = pk_rel->rd_att;
3530 * Special case - if there are no keys at all, this is a 'no column'
3531 * constraint - no need to try to extract the values, and the message in
3532 * this case looks different.
3534 if (qkey->nkeypairs == 0)
3537 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3538 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
3539 RelationGetRelationName(fk_rel), constrname),
3540 errdetail("No rows were found in \"%s\".",
3541 RelationGetRelationName(pk_rel))));
3544 /* Get printable versions of the keys involved */
3545 initStringInfo(&key_names);
3546 initStringInfo(&key_values);
3547 for (idx = 0; idx < qkey->nkeypairs; idx++)
3549 int fnum = qkey->keypair[idx][key_idx];
3553 name = SPI_fname(tupdesc, fnum);
3554 val = SPI_getvalue(violator, tupdesc, fnum);
3560 appendStringInfoString(&key_names, ", ");
3561 appendStringInfoString(&key_values, ", ");
3563 appendStringInfoString(&key_names, name);
3564 appendStringInfoString(&key_values, val);
3569 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3570 errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
3571 RelationGetRelationName(fk_rel), constrname),
3572 errdetail("Key (%s)=(%s) is not present in table \"%s\".",
3573 key_names.data, key_values.data,
3574 RelationGetRelationName(pk_rel))));
3577 (errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
3578 errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
3579 RelationGetRelationName(pk_rel),
3580 constrname, RelationGetRelationName(fk_rel)),
3581 errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
3582 key_names.data, key_values.data,
3583 RelationGetRelationName(fk_rel))));
3587 * ri_BuildQueryKeyPkCheck -
3589 * Build up a new hashtable key for a prepared SPI plan of a
3590 * check for PK rows in noaction triggers.
3592 * key: output argument, *key is filled in based on the other arguments
3593 * riinfo: info from pg_constraint entry
3594 * constr_queryno: an internal number of the query inside the proc
3596 * At least for MATCH FULL this builds a unique key per plan.
3600 ri_BuildQueryKeyPkCheck(RI_QueryKey *key, const RI_ConstraintInfo *riinfo,
3601 int32 constr_queryno)
3605 MemSet(key, 0, sizeof(RI_QueryKey));
3606 key->constr_type = FKCONSTR_MATCH_FULL;
3607 key->constr_id = riinfo->constraint_id;
3608 key->constr_queryno = constr_queryno;
3609 key->fk_relid = InvalidOid;
3610 key->pk_relid = riinfo->pk_relid;
3611 key->nkeypairs = riinfo->nkeys;
3612 for (i = 0; i < riinfo->nkeys; i++)
3614 key->keypair[i][RI_KEYPAIR_FK_IDX] = 0;
3615 key->keypair[i][RI_KEYPAIR_PK_IDX] = riinfo->pk_attnums[i];
3623 * Determine the NULL state of all key values in a tuple
3625 * Returns one of RI_KEYS_ALL_NULL, RI_KEYS_NONE_NULL or RI_KEYS_SOME_NULL.
3629 ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
3633 bool allnull = true;
3634 bool nonenull = true;
3636 for (i = 0; i < key->nkeypairs; i++)
3639 SPI_getbinval(tup, rel->rd_att, key->keypair[i][pairidx], &isnull);
3647 return RI_KEYS_ALL_NULL;
3650 return RI_KEYS_NONE_NULL;
3652 return RI_KEYS_SOME_NULL;
3657 * ri_InitHashTables -
3659 * Initialize our internal hash tables for prepared
3660 * query plans and comparison operators.
3664 ri_InitHashTables(void)
3668 memset(&ctl, 0, sizeof(ctl));
3669 ctl.keysize = sizeof(RI_QueryKey);
3670 ctl.entrysize = sizeof(RI_QueryHashEntry);
3671 ctl.hash = tag_hash;
3672 ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
3673 &ctl, HASH_ELEM | HASH_FUNCTION);
3675 memset(&ctl, 0, sizeof(ctl));
3676 ctl.keysize = sizeof(RI_CompareKey);
3677 ctl.entrysize = sizeof(RI_CompareHashEntry);
3678 ctl.hash = tag_hash;
3679 ri_compare_cache = hash_create("RI compare cache", RI_INIT_QUERYHASHSIZE,
3680 &ctl, HASH_ELEM | HASH_FUNCTION);
3685 * ri_FetchPreparedPlan -
3687 * Lookup for a query key in our private hash table of prepared
3688 * and saved SPI execution plans. Return the plan if found or NULL.
3692 ri_FetchPreparedPlan(RI_QueryKey *key)
3694 RI_QueryHashEntry *entry;
3698 * On the first call initialize the hashtable
3700 if (!ri_query_cache)
3701 ri_InitHashTables();
3704 * Lookup for the key
3706 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3713 * Check whether the plan is still valid. If it isn't, we don't want to
3714 * simply rely on plancache.c to regenerate it; rather we should start
3715 * from scratch and rebuild the query text too. This is to cover cases
3716 * such as table/column renames. We depend on the plancache machinery to
3717 * detect possible invalidations, though.
3719 * CAUTION: this check is only trustworthy if the caller has already
3720 * locked both FK and PK rels.
3723 if (plan && SPI_plan_is_valid(plan))
3727 * Otherwise we might as well flush the cached plan now, to free a little
3728 * memory space before we make a new one.
3739 * ri_HashPreparedPlan -
3741 * Add another plan to our private SPI query plan hashtable.
3745 ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
3747 RI_QueryHashEntry *entry;
3751 * On the first call initialize the hashtable
3753 if (!ri_query_cache)
3754 ri_InitHashTables();
3757 * Add the new plan. We might be overwriting an entry previously found
3758 * invalid by ri_FetchPreparedPlan.
3760 entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
3762 HASH_ENTER, &found);
3763 Assert(!found || entry->plan == NULL);
3771 * Check if all key values in OLD and NEW are equal.
3775 ri_KeysEqual(Relation rel, HeapTuple oldtup, HeapTuple newtup,
3776 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3778 TupleDesc tupdesc = RelationGetDescr(rel);
3779 const int16 *attnums;
3785 attnums = riinfo->pk_attnums;
3786 eq_oprs = riinfo->pp_eq_oprs;
3790 attnums = riinfo->fk_attnums;
3791 eq_oprs = riinfo->ff_eq_oprs;
3794 for (i = 0; i < riinfo->nkeys; i++)
3801 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3803 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
3808 * Get one attribute's newvalue. If it is NULL - they're not equal.
3810 newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
3815 * Compare them with the appropriate equality operator.
3817 if (!ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
3818 oldvalue, newvalue))
3827 * ri_AllKeysUnequal -
3829 * Check if all key values in OLD and NEW are not equal.
3833 ri_AllKeysUnequal(Relation rel, HeapTuple oldtup, HeapTuple newtup,
3834 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3836 TupleDesc tupdesc = RelationGetDescr(rel);
3837 const int16 *attnums;
3843 attnums = riinfo->pk_attnums;
3844 eq_oprs = riinfo->pp_eq_oprs;
3848 attnums = riinfo->fk_attnums;
3849 eq_oprs = riinfo->ff_eq_oprs;
3852 for (i = 0; i < riinfo->nkeys; i++)
3859 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3861 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[i], &isnull);
3866 * Get one attribute's newvalue. If it is NULL - they're not equal.
3868 newvalue = SPI_getbinval(newtup, tupdesc, attnums[i], &isnull);
3873 * Compare them with the appropriate equality operator.
3875 if (ri_AttributesEqual(eq_oprs[i], RIAttType(rel, attnums[i]),
3876 oldvalue, newvalue))
3877 return false; /* found two equal items */
3887 * Check if one key value in OLD and NEW is equal. Note column is indexed
3890 * ri_KeysEqual could call this but would run a bit slower. For
3891 * now, let's duplicate the code.
3895 ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
3896 const RI_ConstraintInfo *riinfo, bool rel_is_pk)
3898 TupleDesc tupdesc = RelationGetDescr(rel);
3899 const int16 *attnums;
3907 attnums = riinfo->pk_attnums;
3908 eq_oprs = riinfo->pp_eq_oprs;
3912 attnums = riinfo->fk_attnums;
3913 eq_oprs = riinfo->ff_eq_oprs;
3917 * Get one attribute's oldvalue. If it is NULL - they're not equal.
3919 oldvalue = SPI_getbinval(oldtup, tupdesc, attnums[column], &isnull);
3924 * Get one attribute's newvalue. If it is NULL - they're not equal.
3926 newvalue = SPI_getbinval(newtup, tupdesc, attnums[column], &isnull);
3931 * Compare them with the appropriate equality operator.
3933 if (!ri_AttributesEqual(eq_oprs[column], RIAttType(rel, attnums[column]),
3934 oldvalue, newvalue))
3941 * ri_AttributesEqual -
3943 * Call the appropriate equality comparison operator for two values.
3945 * NB: we have already checked that neither value is null.
3949 ri_AttributesEqual(Oid eq_opr, Oid typeid,
3950 Datum oldvalue, Datum newvalue)
3952 RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
3954 /* Do we need to cast the values? */
3955 if (OidIsValid(entry->cast_func_finfo.fn_oid))
3957 oldvalue = FunctionCall3(&entry->cast_func_finfo,
3959 Int32GetDatum(-1), /* typmod */
3960 BoolGetDatum(false)); /* implicit coercion */
3961 newvalue = FunctionCall3(&entry->cast_func_finfo,
3963 Int32GetDatum(-1), /* typmod */
3964 BoolGetDatum(false)); /* implicit coercion */
3968 * Apply the comparison operator. We assume it doesn't care about
3971 return DatumGetBool(FunctionCall2(&entry->eq_opr_finfo,
3972 oldvalue, newvalue));
3976 * ri_HashCompareOp -
3978 * See if we know how to compare two values, and create a new hash entry
3982 static RI_CompareHashEntry *
3983 ri_HashCompareOp(Oid eq_opr, Oid typeid)
3986 RI_CompareHashEntry *entry;
3990 * On the first call initialize the hashtable
3992 if (!ri_compare_cache)
3993 ri_InitHashTables();
3996 * Find or create a hash entry. Note we're assuming RI_CompareKey
3997 * contains no struct padding.
3999 key.eq_opr = eq_opr;
4000 key.typeid = typeid;
4001 entry = (RI_CompareHashEntry *) hash_search(ri_compare_cache,
4003 HASH_ENTER, &found);
4005 entry->valid = false;
4008 * If not already initialized, do so. Since we'll keep this hash entry
4009 * for the life of the backend, put any subsidiary info for the function
4010 * cache structs into TopMemoryContext.
4017 CoercionPathType pathtype;
4019 /* We always need to know how to call the equality operator */
4020 fmgr_info_cxt(get_opcode(eq_opr), &entry->eq_opr_finfo,
4024 * If we chose to use a cast from FK to PK type, we may have to apply
4025 * the cast function to get to the operator's input type.
4027 * XXX eventually it would be good to support array-coercion cases
4028 * here and in ri_AttributesEqual(). At the moment there is no point
4029 * because cases involving nonidentical array types will be rejected
4030 * at constraint creation time.
4032 * XXX perhaps also consider supporting CoerceViaIO? No need at the
4033 * moment since that will never be generated for implicit coercions.
4035 op_input_types(eq_opr, &lefttype, &righttype);
4036 Assert(lefttype == righttype);
4037 if (typeid == lefttype)
4038 castfunc = InvalidOid; /* simplest case */
4041 pathtype = find_coercion_pathway(lefttype, typeid,
4044 if (pathtype != COERCION_PATH_FUNC &&
4045 pathtype != COERCION_PATH_RELABELTYPE)
4048 * The declared input type of the eq_opr might be a
4049 * polymorphic type such as ANYARRAY or ANYENUM, or other
4050 * special cases such as RECORD; find_coercion_pathway
4051 * currently doesn't subsume these special cases.
4053 if (!IsPolymorphicType(lefttype) &&
4054 !IsBinaryCoercible(typeid, lefttype))
4055 elog(ERROR, "no conversion function from %s to %s",
4056 format_type_be(typeid),
4057 format_type_be(lefttype));
4060 if (OidIsValid(castfunc))
4061 fmgr_info_cxt(castfunc, &entry->cast_func_finfo,
4064 entry->cast_func_finfo.fn_oid = InvalidOid;
4065 entry->valid = true;
4073 * Given a trigger function OID, determine whether it is an RI trigger,
4074 * and if so whether it is attached to PK or FK relation.
4077 RI_FKey_trigger_type(Oid tgfoid)
4081 case F_RI_FKEY_CASCADE_DEL:
4082 case F_RI_FKEY_CASCADE_UPD:
4083 case F_RI_FKEY_RESTRICT_DEL:
4084 case F_RI_FKEY_RESTRICT_UPD:
4085 case F_RI_FKEY_SETNULL_DEL:
4086 case F_RI_FKEY_SETNULL_UPD:
4087 case F_RI_FKEY_SETDEFAULT_DEL:
4088 case F_RI_FKEY_SETDEFAULT_UPD:
4089 case F_RI_FKEY_NOACTION_DEL:
4090 case F_RI_FKEY_NOACTION_UPD:
4091 return RI_TRIGGER_PK;
4093 case F_RI_FKEY_CHECK_INS:
4094 case F_RI_FKEY_CHECK_UPD:
4095 return RI_TRIGGER_FK;
4098 return RI_TRIGGER_NONE;