FYI, this has already been applied, two days ago.
---------------------------------------------------------------------------
Dmitry Tkach wrote:
>
> Tom Lane wrote:
>
> >Dima Tkach <dmitry@openratings.com> writes:
> >
> >
> >>What drives me crazy though is the way it reports referential integrity
> >>violations:
> >>
> >>
> >
> >
> >
> >>ERROR: <unnamed> referential integrity violation - key referenced from
> >>child not found in parent
> >>
> >>
> >
> >
> >
> >>Now, is it really that hard to tell *which* value for the key was not
> >>found?
> >>
> >>
> >
> >Send a patch ;-)
> >
> >
> Ok :-) Here it is.
> There was also a bug, that I fixed in "no action" triggers - they were
> setting the uid to the owner of the PK table, but actually looking at
> the FK, so it would
> barf if it was owned by a different user, with no 'select' permission to
> the PK-owner....
>
> There are actually *two* different patches - In ri_patch.txt I also took
> the liberty to eliminate some code duplication by extracting the code,
> common between all of the triggers into separate functions... if you
> don't like that for some reason, there is a "lighter" version -
> ri_msg_patch.txt, that only takes care about the error reporting, and
> fixes that table owner problem, leaving everything else intact... Just
> pick the one you like better...
>
> These are both against REL7_3_STABLE. I compiled and tested them, and
> did not see any problems...
>
> If you need a separate version for the HEAD, let me know - I can send
> that too...
>
> Dima
>
> *** 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 <referential constraint definition>
> * Gereral rules 6) a) i):
> * MATCH <unspecified> 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 <referential constraint definition>
> * Gereral rules 7) a) i):
> * MATCH <unspecified> 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 <referential constraint definition>
> * Gereral rules 6) a) iv):
> * MATCH <unspecified> 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 <referential constraint definition>
> * Gereral rules 6) a) iv):
> * MATCH <unspecified> 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 <referential constraint definition>
> * Gereral rules 6) a) ii):
> * MATCH <UNSPECIFIED> 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 <referential constraint definition>
> * 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 <referential constraint definition>
> * Gereral rules 6) a) iii):
> * MATCH <UNSPECIFIED> 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 <referential constraint definition>
> * Gereral rules 7) a) iii):
> * MATCH <UNSPECIFIED> 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);
>
> *** ri_triggers.msg Thu Feb 27 13:53:22 2003
> --- ri_triggers.old Thu Feb 27 13:47:36 2003
> ***************
> *** 141,163 ****
> 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_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;
> --- 141,160 ----
> ***************
> *** 294,315 ****
> 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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
> ! &qkey, NULL);
>
> if (SPI_finish() != SPI_OK_FINISH)
> elog(WARNING, "SPI_finish() failed in RI_FKey_check()");
>
> heap_close(pk_rel, RowShareLock);
>
> return PointerGetDatum(NULL);
>
> }
>
> --- 291,314 ----
> 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);
>
> }
>
> ***************
> *** 476,497 ****
> */
>
> 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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO], pk_rel, fk_rel,
> ! &qkey, new_row);
>
> 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
> --- 475,499 ----
> */
>
> 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
> ***************
> *** 871,900 ****
> if (isnull)
> del_nulls[i] = 'n';
> else
> del_nulls[i] = ' ';
> }
> del_nulls[i] = '\0';
>
> /*
> * Now check for existing references
> */
> ! SetUserId(RelationGetForm(fk_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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> ! pk_rel, fk_rel, &qkey, old_row);
>
> 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.
> --- 873,905 ----
> 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.
> ***************
> *** 1109,1138 ****
> if (isnull)
> upd_nulls[i] = 'n';
> else
> upd_nulls[i] = ' ';
> }
> upd_nulls[i] = '\0';
>
> /*
> * Now check for existing references
> */
> ! SetUserId(RelationGetForm(fk_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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> ! pk_rel, fk_rel, &qkey, old_row);
>
> 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.
> --- 1114,1146 ----
> 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.
> ***************
> *** 1781,1802 ****
> */
> 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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> ! pk_rel, fk_rel, &qkey, old_row);
>
> 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.
> --- 1789,1813 ----
> */
> 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.
> ***************
> *** 2014,2035 ****
> 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)
> ! ri_ReportViolation (tgargs[RI_CONSTRAINT_NAME_ARGNO],
> ! pk_rel, fk_rel, &qkey, old_row);
>
> 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.
> --- 2025,2049 ----
> 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.
> ***************
> *** 3289,3373 ****
> key->keypair[i][RI_KEYPAIR_FK_IDX] = fno;
>
> 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_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
> --- 3303,3322 ----
--
Bruce Momjian | http://candle.pha.pa.us
pgman@candle.pha.pa.us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073