*** ri_triggers.c Fri Feb 28 13:48:56 2003 --- ri_triggers.old Thu Feb 27 13:47:36 2003 *************** *** 64,87 **** #define RI_PLAN_NOACTION_DEL_CHECKREF 1 #define RI_PLAN_NOACTION_UPD_CHECKREF 1 #define RI_PLAN_RESTRICT_DEL_CHECKREF 1 #define RI_PLAN_RESTRICT_UPD_CHECKREF 1 #define RI_PLAN_SETNULL_DEL_DOUPDATE 1 #define RI_PLAN_SETNULL_UPD_DOUPDATE 1 #define MAX_QUOTED_NAME_LEN (NAMEDATALEN*2+3) #define MAX_QUOTED_REL_NAME_LEN (MAX_QUOTED_NAME_LEN*2) - #define RI_TRIGTYPE_INSERT 1 - #define RI_TRIGTYPE_UPDATE 2 - #define RI_TRIGTYPE_INUP 3 - #define RI_TRIGTYPE_DELETE 4 /* ---------- * RI_QueryKey * * The key identifying a prepared SPI plan in our private hashtable * ---------- */ typedef struct RI_QueryKey { int32 constr_type; --- 64,83 ---- *************** *** 145,202 **** static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup, RI_QueryKey *key, int pairidx); static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue); static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, int tgnargs, char **tgargs); static void ri_InitHashTables(void); static void *ri_FetchPreparedPlan(RI_QueryKey *key); static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan); - static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind); - static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel, - Relation pk_rel, HeapTuple old_tuple, - HeapTuple new_tuple, const char *constr); - static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel, - HeapTuple check, HeapTuple upd, - Datum *vals, char *nulls); - static void ri_ReportViolation (const char *constr, Relation pk_rel, - Relation fk_rel, RI_QueryKey *qkey, - HeapTuple violator); /* ---------- * RI_FKey_check - * * Check foreign key existence (combined for INSERT and UPDATE). * ---------- */ static Datum RI_FKey_check(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; int match_type; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_check", RI_TRIGTYPE_INUP); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_check()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()", --- 141,201 ---- static bool ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup, RI_QueryKey *key, int pairidx); static bool ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue); static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, int tgnargs, char **tgargs); static void ri_InitHashTables(void); static void *ri_FetchPreparedPlan(RI_QueryKey *key); static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan); /* ---------- * RI_FKey_check - * * Check foreign key existence (combined for INSERT and UPDATE). * ---------- */ static Datum RI_FKey_check(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum check_values[RI_MAX_NUMKEYS]; + char check_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; int match_type; + Oid save_uid; + + save_uid = GetUserId(); ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_check() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_check() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) && ! !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_check() must be fired for INSERT or UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_check()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_check()", *************** *** 284,305 **** qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* * Execute the plan */ if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_check()"); ! ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,NULL, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_check()"); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); } --- 283,314 ---- qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* * Execute the plan */ if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_check()"); ! SetUserId(RelationGetForm(pk_rel)->relowner); ! ! if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) ! elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); ! ! SetUserId(save_uid); ! ! if (SPI_processed == 0) ! elog(ERROR, "%s referential integrity violation - " ! "no rows found in %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(pk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_check()"); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); } *************** *** 431,455 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* * Now check that foreign key exists in PK table */ ! ri_PerformCheck (&qkey,qplan,fk_rel,pk_rel,NULL,new_row, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_check()"); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Never reached --- 440,499 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* + * We have a plan now. Build up the arguments for SPI_execp() from the + * key values in the new FK tuple. + */ + for (i = 0; i < qkey.nkeypairs; i++) + { + /* + * We can implement MATCH PARTIAL by excluding this column from + * the query if it is null. Simple! Unfortunately, the + * referential actions aren't so I've not bothered to do so for + * the moment. + */ + + check_values[i] = SPI_getbinval(new_row, + fk_rel->rd_att, + qkey.keypair[i][RI_KEYPAIR_FK_IDX], + &isnull); + if (isnull) + check_nulls[i] = 'n'; + else + check_nulls[i] = ' '; + } + check_nulls[i] = '\0'; + + /* * Now check that foreign key exists in PK table */ ! SetUserId(RelationGetForm(pk_rel)->relowner); ! ! if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) ! elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); ! ! SetUserId(save_uid); ! ! if (SPI_processed == 0) ! elog(ERROR, "%s referential integrity violation - " ! "key referenced from %s not found in %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(fk_rel), ! RelationGetRelationName(pk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_check()"); heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Never reached *************** *** 491,513 **** --- 535,563 ---- * Check for matching value of old pk row in current state for * noaction triggers. Returns false if no row was found and a fk row * could potentially be referencing this row, true otherwise. * ---------- */ static bool ri_Check_Pk_Match(Relation pk_rel, HeapTuple old_row, Oid tgoid, int match_type, int tgnargs, char **tgargs) { void *qplan; RI_QueryKey qkey; + bool isnull; + Datum check_values[RI_MAX_NUMKEYS]; + char check_nulls[RI_MAX_NUMKEYS + 1]; int i; + Oid save_uid; bool result; + save_uid = GetUserId(); + ri_BuildQueryKeyPkCheck(&qkey, tgoid, RI_PLAN_CHECK_LOOKUPPK, pk_rel, tgnargs, tgargs); switch (ri_NullCheck(pk_rel, old_row, &qkey, RI_KEYPAIR_PK_IDX)) { case RI_KEYS_ALL_NULL: /* * No check - nothing could have been referencing this row *************** *** 595,617 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! result = ri_PerformCheck (&qkey, qplan,NULL, pk_rel, old_row, NULL, NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()"); return result; } /* ---------- * RI_FKey_noaction_del - --- 645,692 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() from the ! * key values in the new FK tuple. */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! check_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! check_nulls[i] = 'n'; ! else ! check_nulls[i] = ' '; ! } ! check_nulls[i] = '\0'; ! ! /* ! * Now check that foreign key exists in PK table ! */ ! ! SetUserId(RelationGetForm(pk_rel)->relowner); ! ! if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) ! elog(ERROR, "SPI_execp() failed in ri_Check_Pk_Match()"); ! ! SetUserId(save_uid); ! ! result = (SPI_processed != 0); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in ri_Check_Pk_Match()"); return result; } /* ---------- * RI_FKey_noaction_del - *************** *** 625,655 **** RI_FKey_noaction_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; int match_type; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_noaction_del", RI_TRIGTYPE_DELETE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()", --- 700,741 ---- RI_FKey_noaction_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum del_values[RI_MAX_NUMKEYS]; + char del_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; int match_type; + Oid save_uid; + save_uid = GetUserId(); ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_noaction_del() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_noaction_del() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_noaction_del() must be fired for DELETE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_del()", *************** *** 768,792 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan,fk_rel,pk_rel,old_row,NULL, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict delete. --- 854,905 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the deleted PK tuple. ! */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! del_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! del_nulls[i] = 'n'; ! else ! del_nulls[i] = ' '; ! } ! del_nulls[i] = '\0'; ! ! /* ! * Now check for existing references */ + SetUserId(RelationGetForm(pk_rel)->relowner); + + if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT) + elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_del()"); ! SetUserId(save_uid); ! ! if (SPI_processed > 0) ! elog(ERROR, "%s referential integrity violation - " ! "key in %s still referenced from %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(pk_rel), ! RelationGetRelationName(fk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_del()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict delete. *************** *** 817,846 **** { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; int match_type; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_noaction_upd", RI_TRIGTYPE_UPDATE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()", --- 930,971 ---- { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; int match_type; + Oid save_uid; + + save_uid = GetUserId(); ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_noaction_upd() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_noaction_upd() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_noaction_upd() must be fired for UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_noaction_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_noaction_upd()", *************** *** 970,993 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan,fk_rel,pk_rel,old_row,NULL, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL noaction update. --- 1095,1146 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the updated PK tuple. ! */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! upd_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! } ! upd_nulls[i] = '\0'; ! ! /* ! * Now check for existing references */ ! SetUserId(RelationGetForm(pk_rel)->relowner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT) ! elog(ERROR, "SPI_execp() failed in RI_FKey_noaction_upd()"); ! ! SetUserId(save_uid); ! ! if (SPI_processed > 0) ! elog(ERROR, "%s referential integrity violation - " ! "key in %s still referenced from %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(pk_rel), ! RelationGetRelationName(fk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_noaction_upd()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL noaction update. *************** *** 1015,1043 **** RI_FKey_cascade_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_cascade_del", RI_TRIGTYPE_DELETE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()", --- 1168,1207 ---- RI_FKey_cascade_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum del_values[RI_MAX_NUMKEYS]; + char del_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_cascade_del() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_cascade_del() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_cascade_del() must be fired for DELETE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_del()", *************** *** 1058,1077 **** --- 1222,1242 ---- */ if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 6) a) i): * MATCH or MATCH FULL * ... ON DELETE CASCADE * ---------- */ *************** *** 1142,1166 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments ! * from the key values in the deleted PK tuple and delete the ! * referencing rows */ ! ri_PerformCheck (&qkey, qplan,fk_rel,pk_rel,old_row,NULL,NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL cascaded delete. --- 1307,1352 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the deleted PK tuple. */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! del_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! del_nulls[i] = 'n'; ! else ! del_nulls[i] = ' '; ! } ! del_nulls[i] = '\0'; ! ! /* ! * Now delete constraint ! */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, del_values, del_nulls, 0) != SPI_OK_DELETE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_del()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL cascaded delete. *************** *** 1189,1218 **** { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; int j; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_cascade_upd", RI_TRIGTYPE_UPDATE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()", --- 1375,1415 ---- { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS * 2]; + char upd_nulls[RI_MAX_NUMKEYS * 2 + 1]; + bool isnull; int i; int j; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_cascade_upd() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_cascade_upd() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_cascade_upd() must be fired for UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_cascade_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_cascade_upd()", *************** *** 1234,1253 **** --- 1431,1451 ---- if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 7) a) i): * MATCH or MATCH FULL * ... ON UPDATE CASCADE * ---------- */ *************** *** 1339,1361 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan,fk_rel,pk_rel,old_row,new_row,NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL cascade update. --- 1537,1591 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs * 2, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the updated PK tuple. ! */ ! for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++) ! { ! upd_values[i] = SPI_getbinval(new_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! ! upd_values[j] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[j] = 'n'; ! else ! upd_nulls[j] = ' '; ! } ! upd_nulls[j] = '\0'; ! ! /* ! * Now update the existing references */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_cascade_upd()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL cascade update. *************** *** 1390,1418 **** RI_FKey_restrict_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_restrict_del", RI_TRIGTYPE_DELETE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()", --- 1620,1659 ---- RI_FKey_restrict_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum del_values[RI_MAX_NUMKEYS]; + char del_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_restrict_del() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_restrict_del() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_restrict_del() must be fired for DELETE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_del()", *************** *** 1433,1452 **** --- 1674,1694 ---- */ if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 6) a) iv): * MATCH or MATCH FULL * ... ON DELETE CASCADE * ---------- */ *************** *** 1519,1543 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict delete. --- 1761,1813 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the deleted PK tuple. ! */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! del_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! del_nulls[i] = 'n'; ! else ! del_nulls[i] = ' '; ! } ! del_nulls[i] = '\0'; ! ! /* ! * Now check for existing references */ + save_uid = GetUserId(); + SetUserId(fk_owner); + + if (SPI_execp(qplan, del_values, del_nulls, 1) != SPI_OK_SELECT) + elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_del()"); + + SetUserId(save_uid); ! if (SPI_processed > 0) ! elog(ERROR, "%s referential integrity violation - " ! "key in %s still referenced from %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(pk_rel), ! RelationGetRelationName(fk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_del()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict delete. *************** *** 1573,1601 **** { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_restrict_upd", RI_TRIGTYPE_UPDATE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()", --- 1843,1882 ---- { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_restrict_upd() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_restrict_upd() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_restrict_upd() must be fired for UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_restrict_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_restrict_upd()", *************** *** 1617,1636 **** --- 1898,1918 ---- if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 6) a) iv): * MATCH or MATCH FULL * ... ON DELETE CASCADE * ---------- */ *************** *** 1713,1736 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, ! tgargs[RI_CONSTRAINT_NAME_ARGNO]); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict update. --- 1995,2049 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the updated PK tuple. */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! upd_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! } ! upd_nulls[i] = '\0'; ! ! /* ! * Now check for existing references ! */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! SetUserId(RelationGetForm(pk_rel)->relowner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 1) != SPI_OK_SELECT) ! elog(ERROR, "SPI_execp() failed in RI_FKey_restrict_upd()"); ! ! SetUserId(save_uid); ! ! if (SPI_processed > 0) ! elog(ERROR, "%s referential integrity violation - " ! "key in %s still referenced from %s", ! tgargs[RI_CONSTRAINT_NAME_ARGNO], ! RelationGetRelationName(pk_rel), ! RelationGetRelationName(fk_rel)); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_restrict_upd()"); heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL restrict update. *************** *** 1758,1786 **** RI_FKey_setnull_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_setnull_del", RI_TRIGTYPE_DELETE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()", --- 2071,2110 ---- RI_FKey_setnull_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_setnull_del() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setnull_del() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setnull_del() must be fired for DELETE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_del()", *************** *** 1801,1820 **** --- 2125,2145 ---- */ if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 6) a) ii): * MATCH or MATCH FULL * ... ON DELETE SET NULL * ---------- */ *************** *** 1895,1917 **** /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. --- 2220,2265 ---- /* * Prepare, save and remember the new plan. */ qplan = SPI_prepare(querystr, qkey.nkeypairs, queryoids); qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the updated PK tuple. */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! upd_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! } ! upd_nulls[i] = '\0'; ! ! /* ! * Now update the existing references ! */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_del()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. *************** *** 1940,1970 **** { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int i; int match_type; bool use_cached_query; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_setnull_upd", RI_TRIGTYPE_UPDATE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()", --- 2288,2329 ---- { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; int i; int match_type; bool use_cached_query; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_setnull_upd() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setnull_upd() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setnull_upd() must be fired for UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setnull_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setnull_upd()", *************** *** 1987,2006 **** --- 2346,2366 ---- elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]); + fk_owner = RelationGetForm(fk_rel)->relowner; switch (match_type) { /* ---------- * SQL3 11.9 * Gereral rules 7) a) ii) 2): * MATCH FULL * ... ON UPDATE SET NULL * ---------- */ *************** *** 2128,2151 **** * "standard" plan. */ if (use_cached_query) { qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } } /* ! * We have a plan now. * Now update the existing references */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null update. --- 2488,2533 ---- * "standard" plan. */ if (use_cached_query) { qplan = SPI_saveplan(qplan); ri_HashPreparedPlan(&qkey, qplan); } } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the updated PK tuple. ! */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! upd_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! } ! upd_nulls[i] = '\0'; ! ! /* * Now update the existing references */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_setnull_upd()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null update. *************** *** 2173,2200 **** RI_FKey_setdefault_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_del", RI_TRIGTYPE_DELETE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()", --- 2555,2594 ---- RI_FKey_setdefault_del(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; + int i; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_setdefault_del() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setdefault_del() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setdefault_del() must be fired for DELETE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_del()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_del()", *************** *** 2215,2234 **** --- 2609,2629 ---- */ if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; switch (ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO])) { /* ---------- * SQL3 11.9 * Gereral rules 6) a) iii): * MATCH or MATCH FULL * ... ON DELETE SET DEFAULT * ---------- */ *************** *** 2353,2376 **** spi_plan->targetlist); spi_qptle->expr = stringToNode(defval[j].adbin); break; } } } } /* ! * We have a plan now. * Now update the existing references */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. --- 2748,2793 ---- spi_plan->targetlist); spi_qptle->expr = stringToNode(defval[j].adbin); break; } } } } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the deleted PK tuple. ! */ ! for (i = 0; i < qkey.nkeypairs; i++) ! { ! upd_values[i] = SPI_getbinval(old_row, ! pk_rel->rd_att, ! qkey.keypair[i][RI_KEYPAIR_PK_IDX], ! &isnull); ! if (isnull) ! upd_nulls[i] = 'n'; ! else ! upd_nulls[i] = ' '; ! } ! upd_nulls[i] = '\0'; ! ! /* * Now update the existing references */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_del()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. *************** *** 2399,2427 **** { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; int match_type; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! ri_CheckTrigger (fcinfo, "RI_FKey_setdefault_upd", RI_TRIGTYPE_UPDATE); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()", --- 2816,2856 ---- { TriggerData *trigdata = (TriggerData *) fcinfo->context; int tgnargs; char **tgargs; Relation fk_rel; Relation pk_rel; HeapTuple new_row; HeapTuple old_row; RI_QueryKey qkey; void *qplan; + Datum upd_values[RI_MAX_NUMKEYS]; + char upd_nulls[RI_MAX_NUMKEYS + 1]; + bool isnull; + int i; int match_type; + Oid save_uid; + Oid fk_owner; ReferentialIntegritySnapshotOverride = true; /* * Check that this is a valid trigger call on the right time and * event. */ ! if (!CALLED_AS_TRIGGER(fcinfo)) ! elog(ERROR, "RI_FKey_setdefault_upd() not fired by trigger manager"); ! if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || ! !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setdefault_upd() must be fired AFTER ROW"); ! if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) ! elog(ERROR, "RI_FKey_setdefault_upd() must be fired for UPDATE"); /* * Check for the correct # of call arguments */ tgnargs = trigdata->tg_trigger->tgnargs; tgargs = trigdata->tg_trigger->tgargs; if (tgnargs < 4 || (tgnargs % 2) != 0) elog(ERROR, "wrong # of arguments in call to RI_FKey_setdefault_upd()"); if (tgnargs > RI_MAX_ARGUMENTS) elog(ERROR, "too many keys (%d max) in call to RI_FKey_setdefault_upd()", *************** *** 2443,2462 **** --- 2872,2892 ---- if (!OidIsValid(trigdata->tg_trigger->tgconstrrelid)) elog(ERROR, "No target table given for trigger \"%s\" on \"%s\"" "\n\tRemove these RI triggers and do ALTER TABLE ADD CONSTRAINT", trigdata->tg_trigger->tgname, RelationGetRelationName(trigdata->tg_relation)); fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowExclusiveLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; + fk_owner = RelationGetForm(fk_rel)->relowner; match_type = ri_DetermineMatchType(tgargs[RI_MATCH_TYPE_ARGNO]); switch (match_type) { /* ---------- * SQL3 11.9 * Gereral rules 7) a) iii): * MATCH or MATCH FULL * ... ON UPDATE SET DEFAULT *************** *** 2609,2632 **** spi_qptle->expr = stringToNode(defval[j].adbin); break; } } } } } /* ! * We have a plan now. Run it. */ ! ri_PerformCheck (&qkey, qplan, fk_rel, pk_rel, old_row, NULL, NULL); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. --- 3039,3084 ---- spi_qptle->expr = stringToNode(defval[j].adbin); break; } } } } } /* ! * We have a plan now. Build up the arguments for SPI_execp() ! * from the key values in the deleted PK tuple. */ + for (i = 0; i < qkey.nkeypairs; i++) + { + upd_values[i] = SPI_getbinval(old_row, + pk_rel->rd_att, + qkey.keypair[i][RI_KEYPAIR_PK_IDX], + &isnull); + if (isnull) + upd_nulls[i] = 'n'; + else + upd_nulls[i] = ' '; + } + upd_nulls[i] = '\0'; ! /* ! * Now update the existing references ! */ ! save_uid = GetUserId(); ! SetUserId(fk_owner); ! ! if (SPI_execp(qplan, upd_values, upd_nulls, 0) != SPI_OK_UPDATE) ! elog(ERROR, "SPI_execp() failed in RI_FKey_setdefault_upd()"); ! ! SetUserId(save_uid); if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()"); heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); /* * Handle MATCH PARTIAL set null delete. *************** *** 2853,3039 **** fno = SPI_fnumber(pk_rel->rd_att, argv[j + 1]); if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "constraint %s: table %s does not have an attribute %s", argv[RI_CONSTRAINT_NAME_ARGNO], RelationGetRelationName(pk_rel), argv[j + 1]); key->keypair[i][RI_KEYPAIR_PK_IDX] = fno; } } - static void ri_CheckTrigger (PG_FUNCTION_ARGS, const char *name, int tgkind) - { - TriggerData *trigdata = (TriggerData *) fcinfo -> context; - - if (!CALLED_AS_TRIGGER(fcinfo)) - elog(ERROR, "%s() not fired by trigger manager", name); - if (!TRIGGER_FIRED_AFTER(trigdata->tg_event) || - !TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) - elog(ERROR, "%s() must be fired AFTER ROW", name); - - if (tgkind == RI_TRIGTYPE_INUP && - !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event) && - !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) - elog(ERROR, "%s() must be fired for INSERT or UPDATE", name); - else if (tgkind == RI_TRIGTYPE_INSERT && - !TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) - elog (ERROR, "%s() must be fired for INSERT", name); - else if (tgkind == RI_TRIGTYPE_UPDATE && - !TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) - elog (ERROR, "%s() must be fired for UPDATE", name); - else if (tgkind == RI_TRIGTYPE_DELETE && - !TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) - elog (ERROR, "%s() must be fired for DELETE", name); - } - - - static bool ri_PerformCheck (RI_QueryKey *qkey, void *qplan, Relation fk_rel, - Relation pk_rel, HeapTuple old_tuple, - HeapTuple new_tuple, const char *constr) - { - /* The query is always run against the FK table except - * when this is an update/insert trigger on the FK table itself - - * either RI_PLAN_CHECK_LOOKUPPK or RI_PLAN_CHECK_LOOKUPPK_NOCOLS, which - * is equivalent to nkeypairs == 0 - */ - Relation query_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK || - qkey -> nkeypairs == 0 ? pk_rel : fk_rel; - - /** The values for the query are taken from the table on which the trigger - * is called - it is normally the other one with respect to query_rel. - * An exception is ri_Check_Pk_Match(), which uses the PK table for both - * (the case when fk_rel == NULL) - */ - Relation source_rel = qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK && - fk_rel ? fk_rel : pk_rel; - - /** If the vals are taken from fk_rel, then ..FK_IDX, otherwise PK_IDX */ - int key_idx = source_rel == fk_rel ? RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX; - - /** If constr is given, this is a 'noaction' trigger - we only want to check - * if there are any rows that satisfy the query, thus limit=1, also, if - * the query is LOOKUPPK, we are just checking if the row is there... - * Otherwise, we want to perform some action on the matching rows, so - * do not limit the number of results. - */ - int limit = constr || qkey -> constr_queryno==RI_PLAN_CHECK_LOOKUPPK ? 1 : 0; - - Oid save_uid = GetUserId (); - - Datum vals [RI_MAX_NUMKEYS * 2]; - char nulls[RI_MAX_NUMKEYS * 2 + 1]; - - ri_ExtractValues (qkey, key_idx, source_rel, - new_tuple ? new_tuple : old_tuple, - new_tuple ? old_tuple : NULL, vals, nulls); - - SetUserId (RelationGetForm (query_rel) -> relowner); - - if (SPI_execp(qplan, vals, nulls, limit) < 0) - elog(ERROR, "SPI_execp() failed in ri_PerformCheck()"); - - SetUserId(save_uid); - - if (constr && - (SPI_processed==0) == (qkey->constr_queryno==RI_PLAN_CHECK_LOOKUPPK)) - ri_ReportViolation (constr, pk_rel, fk_rel, qkey, - new_tuple ? new_tuple : old_tuple); - - return SPI_processed != 0; - } - - static void ri_ExtractValues (RI_QueryKey *qkey, int key_idx, Relation rel, - HeapTuple check, HeapTuple upd, - Datum *vals, char *nulls) - { - int i,j; - bool isnull; - - for (i = 0, j = qkey -> nkeypairs; i < qkey -> nkeypairs; i++, j++) - { - vals[i]=SPI_getbinval(check,rel->rd_att,qkey->keypair[i][key_idx],&isnull); - nulls[i]=isnull ? 'n' : ' '; - - if (upd) - { - vals[j]=SPI_getbinval(upd,rel->rd_att,qkey->keypair[i][key_idx],&isnull); - nulls[j] = isnull ? 'n' : ' '; - } - } - nulls [upd ? j : i] = '\0'; - } - - static void ri_ReportViolation (const char *constr, Relation pk_rel, - Relation fk_rel, RI_QueryKey *qkey, - HeapTuple violator) - { - static char *null_str = "null"; - - char key_names [512]; - char key_values [512]; - - char *name_ptr = key_names; - char *val_ptr = key_values; - int idx = 0; - - /* If the failed constraint was on insert/update to the FK table, - * we want the key names and values extracted from there, and the error - * message to look like 'key blah referenced from FK not found in PK' - * Otherwise, the attr names and values come from the PK table and the - * message looks like 'key blah in PK still referenced in FK'. - * So, rel is set to where the tuple description is coming from - * (FK in the first case, and PK in the second case), and it also is - * the first relation mentioned in the message, other_rel is respectively - * the other relation. - */ - - bool onfk = (qkey -> constr_queryno == RI_PLAN_CHECK_LOOKUPPK); - - int key_idx = onfk ? RI_KEYPAIR_FK_IDX : RI_KEYPAIR_PK_IDX; - Relation rel = onfk ? fk_rel : pk_rel; - Relation other_rel = onfk ? pk_rel : fk_rel; - - /* Special case - if there are no keys at all, this is a 'no column' - * constraint - no need to try to extract the values, and the message - * in this case looks differently - */ - if (qkey -> nkeypairs == 0) - elog(ERROR, "%s referential integrity violation - no rows found in %s", - constr, RelationGetRelationName(pk_rel)); - - for (idx = 0; idx < qkey->nkeypairs; idx++) - { - int fnum = qkey->keypair[idx][key_idx]; - char *name = SPI_fname (rel->rd_att, fnum); - char *val = SPI_getvalue (violator, rel->rd_att, fnum); - if (!val) - val = null_str; - - if (name_ptr - key_names + strlen(name) + 5 >= 512 || - val_ptr - key_values + strlen (val) + 5 >= 512) - { - sprintf (name_ptr, "..."); - sprintf (val_ptr, "..."); - break; - } - - name_ptr += sprintf (name_ptr, "%s%s", idx > 0 ? "," : "", name); - val_ptr += sprintf (val_ptr, "%s%s", idx > 0 ? "," : "", val); - } - - elog (ERROR, "%s referential integrity violation - key (%s)=(%s) " - "%s %s %s in %s", constr, key_names, key_values, - onfk ? "referenced from" : "in", RelationGetRelationName (rel), - onfk ? "not found" : "still referenced", - RelationGetRelationName (other_rel)); - } - /* ---------- * ri_BuildQueryKeyPkCheck - * * Build up a new hashtable key for a prepared SPI plan of a * check for PK rows in noaction triggers. * * constr_type is FULL * constr_id is the OID of the pg_trigger row that invoked us * constr_queryno is an internal number of the query inside the proc * pk_relid is the OID of referenced relation --- 3305,3324 ---- *************** *** 3378,3402 **** /* * If not found, lookup the operator, then do the function manager * lookup, and remember that info. */ if (!entry) { Oid opr_proc; FmgrInfo finfo; opr_proc = compatible_oper_funcid(makeList1(makeString("=")), ! typeid, typeid, true); if (!OidIsValid(opr_proc)) ! elog (ERROR, ! "ri_AttributesEqual(): cannot find '=' operator for type %u", ! typeid); /* * Since fmgr_info could fail, call it *before* creating the * hashtable entry --- otherwise we could elog leaving an * incomplete entry in the hashtable. Also, because this will be * a permanent table entry, we must make sure any subsidiary * structures of the fmgr record are kept in TopMemoryContext. */ fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext); --- 3663,3687 ---- /* * If not found, lookup the operator, then do the function manager * lookup, and remember that info. */ if (!entry) { Oid opr_proc; FmgrInfo finfo; opr_proc = compatible_oper_funcid(makeList1(makeString("=")), ! typeid, typeid, true); if (!OidIsValid(opr_proc)) ! elog(ERROR, ! "ri_AttributesEqual(): cannot find '=' operator for type %u", ! typeid); /* * Since fmgr_info could fail, call it *before* creating the * hashtable entry --- otherwise we could elog leaving an * incomplete entry in the hashtable. Also, because this will be * a permanent table entry, we must make sure any subsidiary * structures of the fmgr record are kept in TopMemoryContext. */ fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);