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