diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index 34ab2840ee..8dd9815e0c 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -1,5 +1,5 @@ @@ -374,6 +374,7 @@ typedef struct Trigger int16 tgtype; bool tgenabled; bool tgisconstraint; + Oid tgconstrrelid; bool tgdeferrable; bool tginitdeferred; int16 tgnargs; diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 9bdeb3c7c7..44def03fa4 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.170 2002/04/01 04:35:38 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.171 2002/04/01 22:36:09 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -1639,9 +1639,6 @@ AlterTableAddConstraint(Oid myrelid, !isTempNamespace(RelationGetNamespace(rel))) elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); - /* Don't need pkrel open anymore, but hold lock */ - heap_close(pkrel, NoLock); - /* * First we check for limited correctness of the * constraint. @@ -1651,34 +1648,30 @@ AlterTableAddConstraint(Oid myrelid, * referenced relation, and that the column datatypes * are comparable. * - * Scan through each tuple, calling the RI_FKey_Match_Ins + * Scan through each tuple, calling RI_FKey_check_ins * (insert trigger) as if that tuple had just been * inserted. If any of those fail, it should * elog(ERROR) and that's that. */ - - trig.tgoid = 0; + MemSet(&trig, 0, sizeof(trig)); + trig.tgoid = InvalidOid; if (fkconstraint->constr_name) trig.tgname = fkconstraint->constr_name; else trig.tgname = ""; - trig.tgfoid = 0; - trig.tgtype = 0; trig.tgenabled = TRUE; trig.tgisconstraint = TRUE; - trig.tginitdeferred = FALSE; + trig.tgconstrrelid = RelationGetRelid(pkrel); trig.tgdeferrable = FALSE; + trig.tginitdeferred = FALSE; trig.tgargs = (char **) palloc( sizeof(char *) * (4 + length(fkconstraint->fk_attrs) + length(fkconstraint->pk_attrs))); - if (fkconstraint->constr_name) - trig.tgargs[0] = fkconstraint->constr_name; - else - trig.tgargs[0] = ""; - trig.tgargs[1] = pstrdup(RelationGetRelationName(rel)); - trig.tgargs[2] = fkconstraint->pktable->relname; + trig.tgargs[0] = trig.tgname; + trig.tgargs[1] = RelationGetRelationName(rel); + trig.tgargs[2] = RelationGetRelationName(pkrel); trig.tgargs[3] = fkconstraint->match_type; count = 4; foreach(list, fkconstraint->fk_attrs) @@ -1732,6 +1725,9 @@ AlterTableAddConstraint(Oid myrelid, heap_endscan(scan); pfree(trig.tgargs); + + heap_close(pkrel, NoLock); + break; } default: diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index c20c263717..c05b2ec51a 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.110 2002/03/31 06:26:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.111 2002/04/01 22:36:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -544,6 +544,7 @@ RelationBuildTriggers(Relation relation) build->tgtype = pg_trigger->tgtype; build->tgenabled = pg_trigger->tgenabled; build->tgisconstraint = pg_trigger->tgisconstraint; + build->tgconstrrelid = pg_trigger->tgconstrrelid; build->tgdeferrable = pg_trigger->tgdeferrable; build->tginitdeferred = pg_trigger->tginitdeferred; build->tgnargs = pg_trigger->tgnargs; @@ -763,6 +764,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) return false; if (trig1->tgisconstraint != trig2->tgisconstraint) return false; + if (trig1->tgconstrrelid != trig2->tgconstrrelid) + return false; if (trig1->tgdeferrable != trig2->tgdeferrable) return false; if (trig1->tginitdeferred != trig2->tginitdeferred) diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index e251bc60d3..b073e51457 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 2000-2001, PostgreSQL Global Development Group * Copyright 1999 Jan Wieck * - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.34 2002/04/01 02:02:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.35 2002/04/01 22:36:10 tgl Exp $ * * ---------- */ @@ -33,6 +33,7 @@ #include "postgres.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" #include "commands/trigger.h" #include "executor/spi_priv.h" @@ -68,6 +69,9 @@ #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) + /* ---------- * RI_QueryKey @@ -118,6 +122,8 @@ static HTAB *ri_opreq_cache = (HTAB *) NULL; * Local function prototypes * ---------- */ +static void quoteOneName(char *buffer, const char *name); +static void quoteRelationName(char *buffer, Relation rel); static int ri_DetermineMatchType(char *str); static int ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx); @@ -138,19 +144,6 @@ static void *ri_FetchPreparedPlan(RI_QueryKey *key); static void ri_HashPreparedPlan(RI_QueryKey *key, void *plan); -/* - * very ugly, very temporary hack to allow RI triggers to find tables - * anywhere in the current search path. This is just so that the regression - * tests don't break with new search path code; we MUST find a more bullet - * proof solution before release. - */ -static Relation -kluge_openr(char *relationName, LOCKMODE lockmode) -{ - return heap_openrv(makeRangeVar(NULL, relationName), lockmode); -} - - /* ---------- * RI_FKey_check - * @@ -207,9 +200,12 @@ RI_FKey_check(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the new * tuple. + * + * pk_rel is opened in RowShareLock mode since that's what our + * eventual SELECT FOR UPDATE will get on it. */ fk_rel = trigdata->tg_relation; - pk_rel = kluge_openr(tgargs[RI_PK_RELNAME_ARGNO], NoLock); + pk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { old_row = trigdata->tg_trigtuple; @@ -221,16 +217,17 @@ RI_FKey_check(PG_FUNCTION_ARGS) new_row = trigdata->tg_trigtuple; } - /* - * We should not even consider checking the row if it is no longer - * valid since it was either deleted (doesn't matter) or updated - * (in which case it'll be checked with its final values). - */ - if (new_row) { - if (!HeapTupleSatisfiesItself(new_row->t_data)) { - return PointerGetDatum(NULL); - } - } + /* + * We should not even consider checking the row if it is no longer + * valid since it was either deleted (doesn't matter) or updated + * (in which case it'll be checked with its final values). + */ + if (new_row) { + if (!HeapTupleSatisfiesItself(new_row->t_data)) { + heap_close(pk_rel, RowShareLock); + return PointerGetDatum(NULL); + } + } /* ---------- * SQL3 11.9 @@ -252,16 +249,17 @@ RI_FKey_check(PG_FUNCTION_ARGS) if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char querystr[8192]; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100]; + char pkrelname[MAX_QUOTED_REL_NAME_LEN]; /* --------- * The query string built is * SELECT 1 FROM ONLY * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\" FOR UPDATE OF \"%s\"", - tgargs[RI_PK_RELNAME_ARGNO], - tgargs[RI_PK_RELNAME_ARGNO]); + quoteRelationName(pkrelname, pk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x FOR UPDATE OF x", + pkrelname); /* * Prepare, save and remember the new plan. @@ -278,8 +276,6 @@ RI_FKey_check(PG_FUNCTION_ARGS) elog(WARNING, "SPI_connect() failed in RI_FKey_check()"); SetUserId(RelationGetForm(pk_rel)->relowner); - /* pk_rel is no longer neede OK ? */ - heap_close(pk_rel, NoLock); if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); @@ -290,11 +286,13 @@ RI_FKey_check(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "no rows found in %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_PK_RELNAME_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); } @@ -322,7 +320,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) * This is true for MATCH FULL, MATCH PARTIAL, and MATCH * */ - heap_close(pk_rel, NoLock); + heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_KEYS_SOME_NULL: @@ -343,7 +341,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) "MATCH FULL doesn't allow mixing of NULL " "and NON-NULL key values", tgargs[RI_CONSTRAINT_NAME_ARGNO]); - heap_close(pk_rel, NoLock); + heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_MATCH_TYPE_UNSPECIFIED: @@ -352,7 +350,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) * MATCH - if ANY column is null, we * have a match. */ - heap_close(pk_rel, NoLock); + heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_MATCH_TYPE_PARTIAL: @@ -364,7 +362,7 @@ RI_FKey_check(PG_FUNCTION_ARGS) * writing a special version here) */ elog(ERROR, "MATCH PARTIAL not yet implemented"); - heap_close(pk_rel, NoLock); + heap_close(pk_rel, RowShareLock); return PointerGetDatum(NULL); } @@ -393,9 +391,11 @@ RI_FKey_check(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char pkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -407,21 +407,20 @@ RI_FKey_check(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"", - tgargs[RI_PK_RELNAME_ARGNO]); + quoteRelationName(pkrelname, pk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x", pkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[5 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_PK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(fk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_FK_IDX]); } - sprintf(buf, " FOR UPDATE OF \"%s\"", - tgargs[RI_PK_RELNAME_ARGNO]); - strcat(querystr, buf); + strcat(querystr, " FOR UPDATE OF x"); /* * Prepare, save and remember the new plan. @@ -460,8 +459,6 @@ RI_FKey_check(PG_FUNCTION_ARGS) */ SetUserId(RelationGetForm(pk_rel)->relowner); - /* pk_rel is no longer needed OK ? */ - heap_close(pk_rel, NoLock); if (SPI_execp(qplan, check_values, check_nulls, 1) != SPI_OK_SELECT) elog(ERROR, "SPI_execp() failed in RI_FKey_check()"); @@ -472,12 +469,14 @@ RI_FKey_check(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "key referenced from %s not found in %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_FK_RELNAME_ARGNO], - tgargs[RI_PK_RELNAME_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); /* @@ -575,8 +574,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowShareLock mode since that's what our + * eventual SELECT FOR UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); + fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; old_row = trigdata->tg_trigtuple; @@ -605,7 +607,7 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) * No check - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -615,7 +617,6 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_noaction_del()"); @@ -626,9 +627,11 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -640,21 +643,20 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } - sprintf(buf, " FOR UPDATE OF \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); - strcat(querystr, buf); + strcat(querystr, " FOR UPDATE OF x"); /* * Prepare, save and remember the new plan. @@ -695,12 +697,14 @@ RI_FKey_noaction_del(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "key in %s still referenced from %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_PK_RELNAME_ARGNO], - tgargs[RI_FK_RELNAME_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); /* @@ -781,8 +785,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the new * and old tuple. + * + * fk_rel is opened in RowShareLock mode since that's what our + * eventual SELECT FOR UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); + fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, RowShareLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; @@ -812,7 +819,7 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) * No check - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -822,14 +829,16 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); /* * No need to check anything if old and new keys are equal */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) + { + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); + } if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_noaction_upd()"); @@ -840,9 +849,11 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -854,21 +865,20 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } - sprintf(buf, " FOR UPDATE OF \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); - strcat(querystr, buf); + strcat(querystr, " FOR UPDATE OF x"); /* * Prepare, save and remember the new plan. @@ -909,12 +919,14 @@ RI_FKey_noaction_upd(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "key in %s still referenced from %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_PK_RELNAME_ARGNO], - tgargs[RI_FK_RELNAME_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); /* @@ -991,11 +1003,14 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual DELETE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -1022,7 +1037,7 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) * No check - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -1032,7 +1047,6 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_cascade_del()"); @@ -1042,9 +1056,11 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -1056,14 +1072,15 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "DELETE FROM ONLY \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "DELETE FROM ONLY %s", fkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); @@ -1108,6 +1125,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_del()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -1186,12 +1205,15 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the new * and old tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -1218,7 +1240,7 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -1228,14 +1250,16 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); /* * No need to do anything if old and new keys are equal */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) + { + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); + } if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_cascade_upd()"); @@ -1246,11 +1270,13 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char qualstr[8192]; - char *querysep; - char *qualsep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; + char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; + const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS * 2]; /* ---------- @@ -1263,19 +1289,19 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "UPDATE ONLY \"%s\" SET", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0, j = qkey.nkeypairs; i < qkey.nkeypairs; i++, j++) { - sprintf(buf, "%s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); - sprintf(buf, " %s \"%s\" = $%d", qualsep, - tgargs[4 + i * 2], j + 1); - strcat(qualstr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), "%s %s = $%d", + querysep, attname, i+1); + sprintf(qualstr + strlen(qualstr), " %s %s = $%d", + qualsep, attname, j+1); querysep = ","; qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, @@ -1332,6 +1358,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_cascade_upd()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -1415,11 +1443,14 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowShareLock mode since that's what our + * eventual SELECT FOR UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -1446,7 +1477,7 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) * No check - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -1456,7 +1487,6 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_restrict_del()"); @@ -1467,9 +1497,11 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -1481,21 +1513,20 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } - sprintf(buf, " FOR UPDATE OF \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); - strcat(querystr, buf); + strcat(querystr, " FOR UPDATE OF x"); /* * Prepare, save and remember the new plan. @@ -1537,12 +1568,14 @@ RI_FKey_restrict_del(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "key in %s still referenced from %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_PK_RELNAME_ARGNO], - tgargs[RI_FK_RELNAME_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); /* @@ -1627,12 +1660,15 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the new * and old tuple. + * + * fk_rel is opened in RowShareLock mode since that's what our + * eventual SELECT FOR UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -1659,7 +1695,7 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) * No check - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -1669,14 +1705,16 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); /* * No need to check anything if old and new keys are equal */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) + { + heap_close(fk_rel, RowShareLock); return PointerGetDatum(NULL); + } if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_restrict_upd()"); @@ -1687,9 +1725,11 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char *querysep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -1701,21 +1741,20 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "SELECT 1 FROM ONLY \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "SELECT 1 FROM ONLY %s x", fkrelname); querysep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, " %s \"%s\" = $%d", querysep, - tgargs[4 + i * 2], i + 1); - strcat(querystr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), " %s %s = $%d", + querysep, attname, i+1); querysep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); } - sprintf(buf, " FOR UPDATE OF \"%s\"", - tgargs[RI_FK_RELNAME_ARGNO]); - strcat(querystr, buf); + strcat(querystr, " FOR UPDATE OF x"); /* * Prepare, save and remember the new plan. @@ -1759,12 +1798,14 @@ RI_FKey_restrict_upd(PG_FUNCTION_ARGS) elog(ERROR, "%s referential integrity violation - " "key in %s still referenced from %s", tgargs[RI_CONSTRAINT_NAME_ARGNO], - tgargs[RI_PK_RELNAME_ARGNO], - tgargs[RI_FK_RELNAME_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); /* @@ -1841,11 +1882,14 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -1872,7 +1916,7 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -1882,7 +1926,6 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_setnull_del()"); @@ -1893,11 +1936,13 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) */ if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char qualstr[8192]; - char *querysep; - char *qualsep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; + char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; + const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -1910,19 +1955,19 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "UPDATE ONLY \"%s\" SET", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, "%s \"%s\" = NULL", querysep, - tgargs[4 + i * 2]); - strcat(querystr, buf); - sprintf(buf, " %s \"%s\" = $%d", qualsep, - tgargs[4 + i * 2], i + 1); - strcat(qualstr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), "%s %s = NULL", + querysep, attname); + sprintf(qualstr + strlen(qualstr), " %s %s = $%d", + qualsep, attname, i+1); querysep = ","; qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, @@ -1969,6 +2014,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_del()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -2048,13 +2095,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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) { @@ -2081,7 +2131,7 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -2091,15 +2141,16 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) */ break; } - heap_close(fk_rel, NoLock); - /* * No need to do anything if old and new keys are equal */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) + { + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); + } if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_setnull_upd()"); @@ -2129,11 +2180,13 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) if (!use_cached_query || (qplan = ri_FetchPreparedPlan(&qkey)) == NULL) { - char buf[256]; - char querystr[8192]; - char qualstr[8192]; - char *querysep; - char *qualsep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; + char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; + const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; /* ---------- @@ -2146,13 +2199,15 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "UPDATE ONLY \"%s\" SET", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); /* * MATCH - only change columns * corresponding to changed columns in pk_rel's key @@ -2161,14 +2216,12 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) { - sprintf(buf, "%s \"%s\" = NULL", querysep, - tgargs[4 + i * 2]); - strcat(querystr, buf); + sprintf(querystr + strlen(querystr), "%s %s = NULL", + querysep, attname); querysep = ","; } - sprintf(buf, " %s \"%s\" = $%d", qualsep, - tgargs[4 + i * 2], i + 1); - strcat(qualstr, buf); + sprintf(qualstr + strlen(qualstr), " %s %s = $%d", + qualsep, attname, i+1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); @@ -2222,6 +2275,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setnull_upd()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -2298,11 +2353,14 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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])) { @@ -2329,7 +2387,7 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -2344,16 +2402,18 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) elog(WARNING, "SPI_connect() failed in RI_FKey_setdefault_del()"); /* - * Prepare a plan for the set defalt delete operation. + * Prepare a plan for the set default delete operation. * Unfortunately we need to do it on every invocation because * the default value could potentially change between calls. */ { - char buf[256]; - char querystr[8192]; - char qualstr[8192]; - char *querysep; - char *qualsep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; + char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; + const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; Plan *spi_plan; AttrDefault *defval; @@ -2371,19 +2431,19 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "UPDATE ONLY \"%s\" SET", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { - sprintf(buf, "%s \"%s\" = NULL", querysep, - tgargs[4 + i * 2]); - strcat(querystr, buf); - sprintf(buf, " %s \"%s\" = $%d", qualsep, - tgargs[4 + i * 2], i + 1); - strcat(qualstr, buf); + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); + sprintf(querystr + strlen(querystr), "%s %s = NULL", + querysep, attname); + sprintf(qualstr + strlen(qualstr), " %s %s = $%d", + qualsep, attname, i+1); querysep = ","; qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, @@ -2437,8 +2497,6 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) } } } - /* fk_rel is no longer needed OK ? */ - heap_close(fk_rel, NoLock); /* * We have a plan now. Build up the arguments for SPI_execp() @@ -2471,6 +2529,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_del()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -2549,12 +2609,15 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) /* * Get the relation descriptors of the FK and PK tables and the old * tuple. + * + * fk_rel is opened in RowExclusiveLock mode since that's what our + * eventual UPDATE will get on it. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); - fk_owner = RelationGetForm(fk_rel)->relowner; + 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]); @@ -2583,7 +2646,7 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) * No update - MATCH FULL means there cannot be any * reference to old key if it contains NULL */ - heap_close(fk_rel, NoLock); + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); case RI_KEYS_NONE_NULL: @@ -2599,22 +2662,27 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) */ if (ri_KeysEqual(pk_rel, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) + { + heap_close(fk_rel, RowExclusiveLock); return PointerGetDatum(NULL); + } if (SPI_connect() != SPI_OK_CONNECT) elog(WARNING, "SPI_connect() failed in RI_FKey_setdefault_upd()"); /* - * Prepare a plan for the set defalt delete operation. + * Prepare a plan for the set default delete operation. * Unfortunately we need to do it on every invocation because * the default value could potentially change between calls. */ { - char buf[256]; - char querystr[8192]; - char qualstr[8192]; - char *querysep; - char *qualsep; + char querystr[MAX_QUOTED_REL_NAME_LEN + 100 + + (MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS * 2]; + char qualstr[(MAX_QUOTED_NAME_LEN + 32) * RI_MAX_NUMKEYS]; + char fkrelname[MAX_QUOTED_REL_NAME_LEN]; + char attname[MAX_QUOTED_NAME_LEN]; + const char *querysep; + const char *qualsep; Oid queryoids[RI_MAX_NUMKEYS]; Plan *spi_plan; AttrDefault *defval; @@ -2632,13 +2700,15 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) * how to compare these two types by '='. * ---------- */ - sprintf(querystr, "UPDATE ONLY \"%s\" SET", - tgargs[RI_FK_RELNAME_ARGNO]); + quoteRelationName(fkrelname, fk_rel); + sprintf(querystr, "UPDATE ONLY %s SET", fkrelname); qualstr[0] = '\0'; querysep = ""; qualsep = "WHERE"; for (i = 0; i < qkey.nkeypairs; i++) { + quoteOneName(attname, + tgargs[RI_FIRST_ATTNAME_ARGNO + i * 2 + RI_KEYPAIR_FK_IDX]); /* * MATCH - only change columns * corresponding to changed columns in pk_rel's key @@ -2647,14 +2717,12 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) !ri_OneKeyEqual(pk_rel, i, old_row, new_row, &qkey, RI_KEYPAIR_PK_IDX)) { - sprintf(buf, "%s \"%s\" = NULL", querysep, - tgargs[4 + i * 2]); - strcat(querystr, buf); + sprintf(querystr + strlen(querystr), "%s %s = NULL", + querysep, attname); querysep = ","; } - sprintf(buf, " %s \"%s\" = $%d", qualsep, - tgargs[4 + i * 2], i + 1); - strcat(qualstr, buf); + sprintf(qualstr + strlen(qualstr), " %s %s = $%d", + qualsep, attname, i+1); qualsep = "AND"; queryoids[i] = SPI_gettypeid(pk_rel->rd_att, qkey.keypair[i][RI_KEYPAIR_PK_IDX]); @@ -2713,8 +2781,6 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) } } } - /* fk_rel is no longer needed OK ? */ - heap_close(fk_rel, NoLock); /* * We have a plan now. Build up the arguments for SPI_execp() @@ -2747,6 +2813,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS) if (SPI_finish() != SPI_OK_FINISH) elog(WARNING, "SPI_finish() failed in RI_FKey_setdefault_upd()"); + heap_close(fk_rel, RowExclusiveLock); + return PointerGetDatum(NULL); /* @@ -2805,8 +2873,10 @@ RI_FKey_keyequal_upd(TriggerData *trigdata) /* * Get the relation descriptors of the FK and PK tables and the new * and old tuple. + * + * Use minimal locking for fk_rel here. */ - fk_rel = kluge_openr(tgargs[RI_FK_RELNAME_ARGNO], NoLock); + fk_rel = heap_open(trigdata->tg_trigger->tgconstrrelid, AccessShareLock); pk_rel = trigdata->tg_relation; new_row = trigdata->tg_newtuple; old_row = trigdata->tg_trigtuple; @@ -2822,7 +2892,8 @@ RI_FKey_keyequal_upd(TriggerData *trigdata) 0, fk_rel, pk_rel, tgnargs, tgargs); - heap_close(fk_rel, NoLock); + + heap_close(fk_rel, AccessShareLock); /* * Return if key's are equal @@ -2855,7 +2926,50 @@ RI_FKey_keyequal_upd(TriggerData *trigdata) */ +/* + * quoteOneName --- safely quote a single SQL name + * + * buffer must be MAX_QUOTED_NAME_LEN long (includes room for \0) + */ +static void +quoteOneName(char *buffer, const char *name) +{ + /* Rather than trying to be smart, just always quote it. */ + *buffer++ = '"'; + while (*name) + { + if (*name == '"') + *buffer++ = '"'; + *buffer++ = *name++; + } + *buffer++ = '"'; + *buffer = '\0'; +} +/* + * quoteRelationName --- safely quote a fully qualified relation name + * + * buffer must be MAX_QUOTED_REL_NAME_LEN long (includes room for \0) + */ +static void +quoteRelationName(char *buffer, Relation rel) +{ + HeapTuple tuple; + char *nsname; + + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(RelationGetNamespace(rel)), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "Failed to lookup namespace %u of relation %s", + RelationGetNamespace(rel), RelationGetRelationName(rel)); + nsname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); + quoteOneName(buffer, nsname); + ReleaseSysCache(tuple); + buffer += strlen(buffer); + *buffer++ = '.'; + quoteOneName(buffer, RelationGetRelationName(rel)); +} /* ---------- @@ -2867,11 +2981,11 @@ RI_FKey_keyequal_upd(TriggerData *trigdata) static int ri_DetermineMatchType(char *str) { - if (!strcmp(str, "UNSPECIFIED")) + if (strcmp(str, "UNSPECIFIED") == 0) return RI_MATCH_TYPE_UNSPECIFIED; - if (!strcmp(str, "FULL")) + if (strcmp(str, "FULL") == 0) return RI_MATCH_TYPE_FULL; - if (!strcmp(str, "PARTIAL")) + if (strcmp(str, "PARTIAL") == 0) return RI_MATCH_TYPE_PARTIAL; elog(ERROR, "unrecognized referential integrity MATCH type '%s'", str); @@ -2926,7 +3040,7 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno, if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "constraint %s: table %s does not have an attribute %s", argv[RI_CONSTRAINT_NAME_ARGNO], - argv[RI_FK_RELNAME_ARGNO], + RelationGetRelationName(fk_rel), argv[j]); key->keypair[i][RI_KEYPAIR_FK_IDX] = fno; @@ -2934,12 +3048,10 @@ ri_BuildQueryKeyFull(RI_QueryKey *key, Oid constr_id, int32 constr_queryno, if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "constraint %s: table %s does not have an attribute %s", argv[RI_CONSTRAINT_NAME_ARGNO], - argv[RI_PK_RELNAME_ARGNO], + RelationGetRelationName(pk_rel), argv[j + 1]); key->keypair[i][RI_KEYPAIR_PK_IDX] = fno; } - - return; } diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index c08a4151e3..4166b47bb7 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: trigger.h,v 1.33 2002/03/29 22:10:34 tgl Exp $ + * $Id: trigger.h,v 1.34 2002/04/01 22:36:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -81,6 +81,12 @@ typedef struct TriggerData * constrname\0fkrel\0pkrel\0matchtype\0fkatt\0pkatt\0fkatt\0pkatt\0... * * There are one or more pairs of fkatt/pkatt names. + * + * The relation names are no longer of much use since they are not + * guaranteed unique; they are present only for backwards compatibility. + * Use the tgrelid and tgconstrrelid fields to identify the referenced + * relations, instead. (But note that which is which will depend on which + * trigger you are looking at!) */ #define RI_CONSTRAINT_NAME_ARGNO 0 #define RI_FK_RELNAME_ARGNO 1 @@ -127,9 +133,8 @@ extern void ExecARUpdateTriggers(EState *estate, HeapTuple newtuple); -/* ---------- +/* * Deferred trigger stuff - * ---------- */ typedef struct DeferredTriggerStatusData { @@ -139,14 +144,12 @@ typedef struct DeferredTriggerStatusData typedef struct DeferredTriggerStatusData *DeferredTriggerStatus; - typedef struct DeferredTriggerEventItem { Oid dti_tgoid; int32 dti_state; } DeferredTriggerEventItem; - typedef struct DeferredTriggerEventData *DeferredTriggerEvent; typedef struct DeferredTriggerEventData @@ -173,7 +176,6 @@ extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt); /* * in utils/adt/ri_triggers.c - * */ extern bool RI_FKey_keyequal_upd(TriggerData *trigdata); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 4a3fbd1eb1..4c6e88efbd 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rel.h,v 1.58 2002/03/31 06:26:32 tgl Exp $ + * $Id: rel.h,v 1.59 2002/04/01 22:36:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,12 +49,14 @@ typedef LockInfoData *LockInfo; */ typedef struct Trigger { - Oid tgoid; + Oid tgoid; /* OID of trigger (pg_trigger row) */ + /* Remaining fields are copied from pg_trigger, see pg_trigger.h */ char *tgname; Oid tgfoid; int16 tgtype; bool tgenabled; bool tgisconstraint; + Oid tgconstrrelid; bool tgdeferrable; bool tginitdeferred; int16 tgnargs;