diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/conversioncmds.c | 6 | ||||
-rw-r--r-- | src/backend/commands/explain.c | 6 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 563 | ||||
-rw-r--r-- | src/backend/commands/trigger.c | 125 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 5 |
5 files changed, 193 insertions, 512 deletions
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 349eadf24f0..37cc0e6f571 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.30 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.31 2007/02/14 01:58:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -146,7 +146,7 @@ RenameConversion(List *name, const char *newname) errmsg("conversion \"%s\" does not exist", NameListToString(name)))); - tup = SearchSysCacheCopy(CONOID, + tup = SearchSysCacheCopy(CONVOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ @@ -236,7 +236,7 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) Assert(RelationGetRelid(rel) == ConversionRelationId); - tup = SearchSysCacheCopy(CONOID, + tup = SearchSysCacheCopy(CONVOID, ObjectIdGetDatum(conversionOid), 0, 0, 0); if (!HeapTupleIsValid(tup)) /* should not happen */ diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index ae9839641e5..9ea32d7707a 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.153 2007/01/05 22:19:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.154 2007/02/14 01:58:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -327,8 +327,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt, if (instr->ntuples == 0) continue; - if (trig->tgisconstraint && - (conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL) + if (OidIsValid(trig->tgconstraint) && + (conname = get_constraint_name(trig->tgconstraint)) != NULL) { appendStringInfo(&buf, "Trigger for constraint %s", conname); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 001bc571548..cea8ddeabf9 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.213 2007/02/02 00:07:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.214 2007/02/14 01:58:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ #include "catalog/pg_depend.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_opclass.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "catalog/toasting.h" @@ -139,6 +140,7 @@ typedef struct NewConstraint char *name; /* Constraint name, or NULL if none */ ConstrType contype; /* CHECK or FOREIGN */ Oid refrelid; /* PK rel, if FOREIGN */ + Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or FkConstraint struct */ List *qualstate; /* Execution state for CHECK */ } NewConstraint; @@ -186,10 +188,9 @@ static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, Oid *opclasses); static void validateForeignKeyConstraint(FkConstraint *fkconstraint, - Relation rel, Relation pkrel); + Relation rel, Relation pkrel, Oid constraintOid); static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, - Oid constrOid); -static char *fkMatchTypeToString(char match_type); + Oid constraintOid); static void ATController(Relation rel, List *cmds, bool recurse); static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing); @@ -256,11 +257,6 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, static void ATExecAddInherit(Relation rel, RangeVar *parent); static void ATExecDropInherit(Relation rel, RangeVar *parent); static void copy_relation_data(Relation rel, SMgrRelation dst); -static void update_ri_trigger_args(Oid relid, - const char *oldname, - const char *newname, - bool fk_scan, - bool update_relname); /* ---------------------------------------------------------------- @@ -1615,21 +1611,6 @@ renameatt(Oid myrelid, heap_close(attrelation, RowExclusiveLock); - /* - * Update att name in any RI triggers associated with the relation. - */ - if (targetrelation->rd_rel->reltriggers > 0) - { - /* update tgargs column reference where att is primary key */ - update_ri_trigger_args(RelationGetRelid(targetrelation), - oldattname, newattname, - false, false); - /* update tgargs column reference where att is foreign key */ - update_ri_trigger_args(RelationGetRelid(targetrelation), - oldattname, newattname, - true, false); - } - relation_close(targetrelation, NoLock); /* close rel but keep lock */ } @@ -1709,226 +1690,12 @@ renamerel(Oid myrelid, const char *newrelname) TypeRename(oldrelname, namespaceId, newrelname); /* - * Update rel name in any RI triggers associated with the relation. - */ - if (relhastriggers) - { - /* update tgargs where relname is primary key */ - update_ri_trigger_args(myrelid, - oldrelname, - newrelname, - false, true); - /* update tgargs where relname is foreign key */ - update_ri_trigger_args(myrelid, - oldrelname, - newrelname, - true, true); - } - - /* * Close rel, but keep exclusive lock! */ relation_close(targetrelation, NoLock); } /* - * Scan pg_trigger for RI triggers that are on the specified relation - * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan - * is true). Update RI trigger args fields matching oldname to contain - * newname instead. If update_relname is true, examine the relname - * fields; otherwise examine the attname fields. - */ -static void -update_ri_trigger_args(Oid relid, - const char *oldname, - const char *newname, - bool fk_scan, - bool update_relname) -{ - Relation tgrel; - ScanKeyData skey[1]; - SysScanDesc trigscan; - HeapTuple tuple; - Datum values[Natts_pg_trigger]; - char nulls[Natts_pg_trigger]; - char replaces[Natts_pg_trigger]; - - tgrel = heap_open(TriggerRelationId, RowExclusiveLock); - if (fk_scan) - { - ScanKeyInit(&skey[0], - Anum_pg_trigger_tgconstrrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - trigscan = systable_beginscan(tgrel, TriggerConstrRelidIndexId, - true, SnapshotNow, - 1, skey); - } - else - { - ScanKeyInit(&skey[0], - Anum_pg_trigger_tgrelid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - trigscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, - true, SnapshotNow, - 1, skey); - } - - while ((tuple = systable_getnext(trigscan)) != NULL) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); - bytea *val; - bytea *newtgargs; - bool isnull; - int tg_type; - bool examine_pk; - bool changed; - int tgnargs; - int i; - int newlen; - const char *arga[RI_MAX_ARGUMENTS]; - const char *argp; - - tg_type = RI_FKey_trigger_type(pg_trigger->tgfoid); - if (tg_type == RI_TRIGGER_NONE) - { - /* Not an RI trigger, forget it */ - continue; - } - - /* - * It is an RI trigger, so parse the tgargs bytea. - * - * NB: we assume the field will never be compressed or moved out of - * line; so does trigger.c ... - */ - tgnargs = pg_trigger->tgnargs; - val = DatumGetByteaP(fastgetattr(tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull)); - if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO || - tgnargs > RI_MAX_ARGUMENTS) - { - /* This probably shouldn't happen, but ignore busted triggers */ - continue; - } - argp = (const char *) VARDATA(val); - for (i = 0; i < tgnargs; i++) - { - arga[i] = argp; - argp += strlen(argp) + 1; - } - - /* - * Figure out which item(s) to look at. If the trigger is primary-key - * type and attached to my rel, I should look at the PK fields; if it - * is foreign-key type and attached to my rel, I should look at the FK - * fields. But the opposite rule holds when examining triggers found - * by tgconstrrel search. - */ - examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan); - - changed = false; - if (update_relname) - { - /* Change the relname if needed */ - i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO; - if (strcmp(arga[i], oldname) == 0) - { - arga[i] = newname; - changed = true; - } - } - else - { - /* Change attname(s) if needed */ - i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX : - RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX; - for (; i < tgnargs; i += 2) - { - if (strcmp(arga[i], oldname) == 0) - { - arga[i] = newname; - changed = true; - } - } - } - - if (!changed) - { - /* Don't need to update this tuple */ - continue; - } - - /* - * Construct modified tgargs bytea. - */ - newlen = VARHDRSZ; - for (i = 0; i < tgnargs; i++) - newlen += strlen(arga[i]) + 1; - newtgargs = (bytea *) palloc(newlen); - VARATT_SIZEP(newtgargs) = newlen; - newlen = VARHDRSZ; - for (i = 0; i < tgnargs; i++) - { - strcpy(((char *) newtgargs) + newlen, arga[i]); - newlen += strlen(arga[i]) + 1; - } - - /* - * Build modified tuple. - */ - for (i = 0; i < Natts_pg_trigger; i++) - { - values[i] = (Datum) 0; - replaces[i] = ' '; - nulls[i] = ' '; - } - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs); - replaces[Anum_pg_trigger_tgargs - 1] = 'r'; - - tuple = heap_modifytuple(tuple, RelationGetDescr(tgrel), values, nulls, replaces); - - /* - * Update pg_trigger and its indexes - */ - simple_heap_update(tgrel, &tuple->t_self, tuple); - - CatalogUpdateIndexes(tgrel, tuple); - - /* - * Invalidate trigger's relation's relcache entry so that other - * backends (and this one too!) are sent SI message to make them - * rebuild relcache entries. (Ideally this should happen - * automatically...) - * - * We can skip this for triggers on relid itself, since that relcache - * flush will happen anyway due to the table or column rename. We - * just need to catch the far ends of RI relationships. - */ - pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); - if (pg_trigger->tgrelid != relid) - CacheInvalidateRelcacheByRelid(pg_trigger->tgrelid); - - /* free up our scratch memory */ - pfree(newtgargs); - heap_freetuple(tuple); - } - - systable_endscan(trigscan); - - heap_close(tgrel, RowExclusiveLock); - - /* - * Increment cmd counter to make updates visible; this is needed in case - * the same tuple has to be updated again by next pass (can happen in case - * of a self-referential FK relationship). - */ - CommandCounterIncrement(); -} - -/* * AlterTable * Execute ALTER TABLE, which can be a list of subcommands * @@ -2552,7 +2319,8 @@ ATRewriteTables(List **wqueue) refrel = heap_open(con->refrelid, RowShareLock); - validateForeignKeyConstraint(fkconstraint, rel, refrel); + validateForeignKeyConstraint(fkconstraint, rel, refrel, + con->conid); heap_close(refrel, NoLock); } @@ -4061,6 +3829,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, Oid pktypoid[INDEX_MAX_KEYS]; Oid fktypoid[INDEX_MAX_KEYS]; Oid opclasses[INDEX_MAX_KEYS]; + Oid pfeqoperators[INDEX_MAX_KEYS]; + Oid ppeqoperators[INDEX_MAX_KEYS]; + Oid ffeqoperators[INDEX_MAX_KEYS]; int i; int numfks, numpks; @@ -4138,6 +3909,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, MemSet(pktypoid, 0, sizeof(pktypoid)); MemSet(fktypoid, 0, sizeof(fktypoid)); MemSet(opclasses, 0, sizeof(opclasses)); + MemSet(pfeqoperators, 0, sizeof(pfeqoperators)); + MemSet(ppeqoperators, 0, sizeof(ppeqoperators)); + MemSet(ffeqoperators, 0, sizeof(ffeqoperators)); numfks = transformColumnNameList(RelationGetRelid(rel), fkconstraint->fk_attrs, @@ -4166,7 +3940,15 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, opclasses); } - /* Be sure referencing and referenced column types are comparable */ + /* + * Look up the equality operators to use in the constraint. + * + * Note that we look for operator(s) with the PK type on the left; when + * the types are different this is the right choice because the PK index + * will need operators with the indexkey on the left. Also, we take the + * PK type as being the declared input type of the opclass, which might be + * binary-compatible but not identical to the PK column type. + */ if (numfks != numpks) ereport(ERROR, (errcode(ERRCODE_INVALID_FOREIGN_KEY), @@ -4174,24 +3956,71 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, for (i = 0; i < numpks; i++) { + HeapTuple cla_ht; + Form_pg_opclass cla_tup; + Oid amid; + Oid opfamily; + Oid pktype; + Oid fktype; + Oid pfeqop; + Oid ppeqop; + Oid ffeqop; + int16 eqstrategy; + + /* We need several fields out of the pg_opclass entry */ + cla_ht = SearchSysCache(CLAOID, + ObjectIdGetDatum(opclasses[i]), + 0, 0, 0); + if (!HeapTupleIsValid(cla_ht)) + elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]); + cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht); + amid = cla_tup->opcmethod; + opfamily = cla_tup->opcfamily; + pktype = cla_tup->opcintype; + ReleaseSysCache(cla_ht); + /* - * pktypoid[i] is the primary key table's i'th key's type fktypoid[i] - * is the foreign key table's i'th key's type - * - * Note that we look for an operator with the PK type on the left; - * when the types are different this is critical because the PK index - * will need operators with the indexkey on the left. (Ordinarily both - * commutator operators will exist if either does, but we won't get - * the right answer from the test below on opclass membership unless - * we select the proper operator.) + * Check it's a btree; currently this can never fail since no other + * index AMs support unique indexes. If we ever did have other + * types of unique indexes, we'd need a way to determine which + * operator strategy number is equality. (Is it reasonable to + * insist that every such index AM use btree's number for equality?) + */ + if (amid != BTREE_AM_OID) + elog(ERROR, "only b-tree indexes are supported for foreign keys"); + eqstrategy = BTEqualStrategyNumber; + + /* + * There had better be a PK = PK operator for the index. + */ + ppeqop = get_opfamily_member(opfamily, pktype, pktype, eqstrategy); + + if (!OidIsValid(ppeqop)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + eqstrategy, pktype, pktype, opfamily); + + /* + * Are there equality operators that take exactly the FK type? + * Assume we should look through any domain here. */ - Operator o = oper(NULL, list_make1(makeString("=")), - pktypoid[i], fktypoid[i], - true, -1); + fktype = getBaseType(fktypoid[i]); + + pfeqop = get_opfamily_member(opfamily, pktype, fktype, eqstrategy); + ffeqop = get_opfamily_member(opfamily, fktype, fktype, eqstrategy); - if (o == NULL) + if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop))) + { + /* + * Otherwise, look for an implicit cast from the FK type to + * the PK type, and if found, use the PK type's equality operator. + */ + if (can_coerce_type(1, &fktype, &pktype, COERCION_IMPLICIT)) + pfeqop = ffeqop = ppeqop; + } + + if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop))) ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), + (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("foreign key constraint \"%s\" " "cannot be implemented", fkconstraint->constr_name), @@ -4202,41 +4031,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, format_type_be(fktypoid[i]), format_type_be(pktypoid[i])))); - /* - * Check that the found operator is compatible with the PK index, and - * generate a warning if not, since otherwise costly seqscans will be - * incurred to check FK validity. - */ - if (!op_in_opfamily(oprid(o), get_opclass_family(opclasses[i]))) - ereport(WARNING, - (errmsg("foreign key constraint \"%s\" " - "will require costly sequential scans", - fkconstraint->constr_name), - errdetail("Key columns \"%s\" and \"%s\" " - "are of different types: %s and %s.", - strVal(list_nth(fkconstraint->fk_attrs, i)), - strVal(list_nth(fkconstraint->pk_attrs, i)), - format_type_be(fktypoid[i]), - format_type_be(pktypoid[i])))); - - ReleaseSysCache(o); - } - - /* - * Tell Phase 3 to check that the constraint is satisfied by existing rows - * (we can skip this during table creation). - */ - if (!fkconstraint->skip_validation) - { - NewConstraint *newcon; - - newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); - newcon->name = fkconstraint->constr_name; - newcon->contype = CONSTR_FOREIGN; - newcon->refrelid = RelationGetRelid(pkrel); - newcon->qual = (Node *) fkconstraint; - - tab->constraints = lappend(tab->constraints, newcon); + pfeqoperators[i] = pfeqop; + ppeqoperators[i] = ppeqop; + ffeqoperators[i] = ffeqop; } /* @@ -4254,6 +4051,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, * constraint */ RelationGetRelid(pkrel), pkattnum, + pfeqoperators, + ppeqoperators, + ffeqoperators, numpks, fkconstraint->fk_upd_action, fkconstraint->fk_del_action, @@ -4269,6 +4069,24 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, createForeignKeyTriggers(rel, fkconstraint, constrOid); /* + * Tell Phase 3 to check that the constraint is satisfied by existing rows + * (we can skip this during table creation). + */ + if (!fkconstraint->skip_validation) + { + NewConstraint *newcon; + + newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); + newcon->name = fkconstraint->constr_name; + newcon->contype = CONSTR_FOREIGN; + newcon->refrelid = RelationGetRelid(pkrel); + newcon->conid = constrOid; + newcon->qual = (Node *) fkconstraint; + + tab->constraints = lappend(tab->constraints, newcon); + } + + /* * Close pk table, but keep lock until we've committed. */ heap_close(pkrel, NoLock); @@ -4526,25 +4344,15 @@ transformFkeyCheckAttrs(Relation pkrel, static void validateForeignKeyConstraint(FkConstraint *fkconstraint, Relation rel, - Relation pkrel) + Relation pkrel, + Oid constraintOid) { HeapScanDesc scan; HeapTuple tuple; Trigger trig; - ListCell *list; - int count; /* - * See if we can do it with a single LEFT JOIN query. A FALSE result - * indicates we must proceed with the fire-the-trigger method. - */ - if (RI_Initial_Check(fkconstraint, rel, pkrel)) - return; - - /* - * 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 - * ereport(ERROR) and that's that. + * Build a trigger call structure; we'll need it either way. */ MemSet(&trig, 0, sizeof(trig)); trig.tgoid = InvalidOid; @@ -4552,35 +4360,23 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint, trig.tgenabled = TRUE; trig.tgisconstraint = TRUE; trig.tgconstrrelid = RelationGetRelid(pkrel); + trig.tgconstraint = constraintOid; trig.tgdeferrable = FALSE; trig.tginitdeferred = FALSE; + /* we needn't fill in tgargs */ - trig.tgargs = (char **) palloc(sizeof(char *) * - (4 + list_length(fkconstraint->fk_attrs) - + list_length(fkconstraint->pk_attrs))); - - trig.tgargs[0] = trig.tgname; - trig.tgargs[1] = RelationGetRelationName(rel); - trig.tgargs[2] = RelationGetRelationName(pkrel); - trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype); - count = 4; - foreach(list, fkconstraint->fk_attrs) - { - char *fk_at = strVal(lfirst(list)); - - trig.tgargs[count] = fk_at; - count += 2; - } - count = 5; - foreach(list, fkconstraint->pk_attrs) - { - char *pk_at = strVal(lfirst(list)); - - trig.tgargs[count] = pk_at; - count += 2; - } - trig.tgnargs = count - 1; + /* + * See if we can do it with a single LEFT JOIN query. A FALSE result + * indicates we must proceed with the fire-the-trigger method. + */ + if (RI_Initial_Check(&trig, rel, pkrel)) + return; + /* + * 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 + * ereport(ERROR) and that's that. + */ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) @@ -4613,18 +4409,13 @@ validateForeignKeyConstraint(FkConstraint *fkconstraint, } heap_endscan(scan); - - pfree(trig.tgargs); } static void CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint, - ObjectAddress *constrobj, ObjectAddress *trigobj, - bool on_insert) + Oid constraintOid, bool on_insert) { CreateTrigStmt *fk_trigger; - ListCell *fk_attr; - ListCell *pk_attr; fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = fkconstraint->constr_name; @@ -4649,32 +4440,9 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint, fk_trigger->deferrable = fkconstraint->deferrable; fk_trigger->initdeferred = fkconstraint->initdeferred; fk_trigger->constrrel = fkconstraint->pktable; - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(myRel->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); - if (list_length(fkconstraint->fk_attrs) != list_length(fkconstraint->pk_attrs)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FOREIGN_KEY), - errmsg("number of referencing and referenced columns for foreign key disagree"))); - - forboth(fk_attr, fkconstraint->fk_attrs, - pk_attr, fkconstraint->pk_attrs) - { - fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr)); - fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr)); - } - trigobj->objectId = CreateTrigger(fk_trigger, true); - - /* Register dependency from trigger to constraint */ - recordDependencyOn(trigobj, constrobj, DEPENDENCY_INTERNAL); + (void) CreateTrigger(fk_trigger, constraintOid); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -4685,14 +4453,10 @@ CreateFKCheckTrigger(RangeVar *myRel, FkConstraint *fkconstraint, */ static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, - Oid constrOid) + Oid constraintOid) { RangeVar *myRel; CreateTrigStmt *fk_trigger; - ListCell *fk_attr; - ListCell *pk_attr; - ObjectAddress trigobj, - constrobj; /* * Reconstruct a RangeVar for my relation (not passed in, unfortunately). @@ -4700,15 +4464,6 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), pstrdup(RelationGetRelationName(rel))); - /* - * Preset objectAddress fields - */ - constrobj.classId = ConstraintRelationId; - constrobj.objectId = constrOid; - constrobj.objectSubId = 0; - trigobj.classId = TriggerRelationId; - trigobj.objectSubId = 0; - /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -4716,8 +4471,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, * Build and execute a CREATE CONSTRAINT TRIGGER statement for the CHECK * action for both INSERTs and UPDATEs on the referencing table. */ - CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, true); - CreateFKCheckTrigger(myRel, fkconstraint, &constrobj, &trigobj, false); + CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, true); + CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, false); /* * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON @@ -4765,27 +4520,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, (int) fkconstraint->fk_del_action); break; } - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(myRel->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); - forboth(fk_attr, fkconstraint->fk_attrs, - pk_attr, fkconstraint->pk_attrs) - { - fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr)); - fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr)); - } - - trigobj.objectId = CreateTrigger(fk_trigger, true); - /* Register dependency from trigger to constraint */ - recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); + (void) CreateTrigger(fk_trigger, constraintOid); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -4835,49 +4572,9 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, (int) fkconstraint->fk_upd_action); break; } - fk_trigger->args = NIL; - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->constr_name)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(myRel->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkconstraint->pktable->relname)); - fk_trigger->args = lappend(fk_trigger->args, - makeString(fkMatchTypeToString(fkconstraint->fk_matchtype))); - forboth(fk_attr, fkconstraint->fk_attrs, - pk_attr, fkconstraint->pk_attrs) - { - fk_trigger->args = lappend(fk_trigger->args, lfirst(fk_attr)); - fk_trigger->args = lappend(fk_trigger->args, lfirst(pk_attr)); - } - - trigobj.objectId = CreateTrigger(fk_trigger, true); - /* Register dependency from trigger to constraint */ - recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL); -} - -/* - * fkMatchTypeToString - - * convert FKCONSTR_MATCH_xxx code to string to use in trigger args - */ -static char * -fkMatchTypeToString(char match_type) -{ - switch (match_type) - { - case FKCONSTR_MATCH_FULL: - return pstrdup("FULL"); - case FKCONSTR_MATCH_PARTIAL: - return pstrdup("PARTIAL"); - case FKCONSTR_MATCH_UNSPECIFIED: - return pstrdup("UNSPECIFIED"); - default: - elog(ERROR, "unrecognized match type: %d", - (int) match_type); - } - return NULL; /* can't get here */ + (void) CreateTrigger(fk_trigger, constraintOid); } /* diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index cc7dfc895b8..c08525c2e04 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 - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.212 2007/01/25 04:17:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.213 2007/02/14 01:58:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -57,13 +58,12 @@ static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, /* * Create a trigger. Returns the OID of the created trigger. * - * forConstraint, if true, says that this trigger is being created to - * implement a constraint. The caller will then be expected to make - * a pg_depend entry linking the trigger to that constraint (and thereby - * to the owning relation(s)). + * constraintOid, if nonzero, says that this trigger is being created to + * implement that constraint. A suitable pg_depend entry will be made + * to link the trigger to that constraint. */ Oid -CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) +CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) { int16 tgtype; int2vector *tgattr; @@ -91,51 +91,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) rel = heap_openrv(stmt->relation, AccessExclusiveLock); - if (stmt->constrrel != NULL) - constrrelid = RangeVarGetRelid(stmt->constrrel, false); - else if (stmt->isconstraint) - { - /* - * If this trigger is a constraint (and a foreign key one) then we - * really need a constrrelid. Since we don't have one, we'll try to - * generate one from the argument information. - * - * This is really just a workaround for a long-ago pg_dump bug that - * omitted the FROM clause in dumped CREATE CONSTRAINT TRIGGER - * commands. We don't want to bomb out completely here if we can't - * determine the correct relation, because that would prevent loading - * the dump file. Instead, NOTICE here and ERROR in the trigger. - */ - bool needconstrrelid = false; - void *elem = NULL; - - if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check_", 14) == 0) - { - /* A trigger on FK table. */ - needconstrrelid = true; - if (list_length(stmt->args) > RI_PK_RELNAME_ARGNO) - elem = list_nth(stmt->args, RI_PK_RELNAME_ARGNO); - } - else if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_", 8) == 0) - { - /* A trigger on PK table. */ - needconstrrelid = true; - if (list_length(stmt->args) > RI_FK_RELNAME_ARGNO) - elem = list_nth(stmt->args, RI_FK_RELNAME_ARGNO); - } - if (elem != NULL) - { - RangeVar *rel = makeRangeVar(NULL, strVal(elem)); - - constrrelid = RangeVarGetRelid(rel, true); - } - if (needconstrrelid && constrrelid == InvalidOid) - ereport(NOTICE, - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("could not determine referenced table for constraint \"%s\"", - stmt->trigname))); - } - if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -152,15 +107,17 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) if (stmt->isconstraint) { - /* foreign key constraint trigger */ - + /* constraint trigger */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_REFERENCES); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); - if (constrrelid != InvalidOid) + + if (stmt->constrrel != NULL) { + constrrelid = RangeVarGetRelid(stmt->constrrel, false); + aclresult = pg_class_aclcheck(constrrelid, GetUserId(), ACL_REFERENCES); if (aclresult != ACLCHECK_OK) @@ -170,7 +127,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) } else { - /* real trigger */ + /* regular trigger */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_TRIGGER); if (aclresult != ACLCHECK_OK) @@ -187,23 +144,31 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) trigoid = GetNewOid(tgrel); /* - * If trigger is an RI constraint, use specified trigger name as - * constraint name and build a unique trigger name instead. This is mainly - * for backwards compatibility with CREATE CONSTRAINT TRIGGER commands. + * If trigger is for an RI constraint, the passed-in name is the + * constraint name; save that and build a unique trigger name to avoid + * collisions with user-selected trigger names. */ - if (stmt->isconstraint) + if (OidIsValid(constraintOid)) { snprintf(constrtrigname, sizeof(constrtrigname), "RI_ConstraintTrigger_%u", trigoid); trigname = constrtrigname; constrname = stmt->trigname; } + else if (stmt->isconstraint) + { + /* constraint trigger: trigger name is also constraint name */ + trigname = stmt->trigname; + constrname = stmt->trigname; + } else { + /* regular trigger: use empty constraint name */ trigname = stmt->trigname; constrname = ""; } + /* Compute tgtype */ TRIGGER_CLEAR_TYPE(tgtype); if (stmt->before) TRIGGER_SETT_BEFORE(tgtype); @@ -298,7 +263,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) /* * Build the new pg_trigger tuple. */ - MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char)); + memset(nulls, ' ', Natts_pg_trigger * sizeof(char)); values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel)); values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein, @@ -310,6 +275,7 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein, CStringGetDatum(constrname)); values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid); + values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid); values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable); values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred); @@ -372,10 +338,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) CatalogUpdateIndexes(tgrel, tuple); - myself.classId = TriggerRelationId; - myself.objectId = trigoid; - myself.objectSubId = 0; - heap_freetuple(tuple); heap_close(tgrel, RowExclusiveLock); @@ -412,20 +374,36 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint) /* * Record dependencies for trigger. Always place a normal dependency on - * the function. If we are doing this in response to an explicit CREATE - * TRIGGER command, also make trigger be auto-dropped if its relation is - * dropped or if the FK relation is dropped. (Auto drop is compatible - * with our pre-7.3 behavior.) If the trigger is being made for a - * constraint, we can skip the relation links; the dependency on the - * constraint will indirectly depend on the relations. + * the function. */ + myself.classId = TriggerRelationId; + myself.objectId = trigoid; + myself.objectSubId = 0; + referenced.classId = ProcedureRelationId; referenced.objectId = funcoid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - if (!forConstraint) + if (OidIsValid(constraintOid)) { + /* + * It's for a constraint, so make it an internal dependency of the + * constraint. We can skip depending on the relations, as there'll + * be an indirect dependency via the constraint. + */ + referenced.classId = ConstraintRelationId; + referenced.objectId = constraintOid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); + } + else + { + /* + * Regular CREATE TRIGGER, so place dependencies. We make trigger be + * auto-dropped if its relation is dropped or if the FK relation is + * dropped. (Auto drop is compatible with our pre-7.3 behavior.) + */ referenced.classId = RelationRelationId; referenced.objectId = RelationGetRelid(rel); referenced.objectSubId = 0; @@ -773,7 +751,7 @@ EnableDisableTrigger(Relation rel, const char *tgname, { Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple); - if (oldtrig->tgisconstraint) + if (OidIsValid(oldtrig->tgconstraint)) { /* system trigger ... ok to process? */ if (skip_system) @@ -886,6 +864,7 @@ RelationBuildTriggers(Relation relation) build->tgenabled = pg_trigger->tgenabled; build->tgisconstraint = pg_trigger->tgisconstraint; build->tgconstrrelid = pg_trigger->tgconstrrelid; + build->tgconstraint = pg_trigger->tgconstraint; build->tgdeferrable = pg_trigger->tgdeferrable; build->tginitdeferred = pg_trigger->tginitdeferred; build->tgnargs = pg_trigger->tgnargs; @@ -1220,6 +1199,8 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) return false; if (trig1->tgconstrrelid != trig2->tgconstrrelid) return false; + if (trig1->tgconstraint != trig2->tgconstraint) + return false; if (trig1->tgdeferrable != trig2->tgdeferrable) return false; if (trig1->tginitdeferred != trig2->tginitdeferred) diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 4f88e9561cd..99f964db74d 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.99 2007/01/05 22:19:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.100 2007/02/14 01:58:57 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -1966,6 +1966,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, domainOid, /* domain constraint */ InvalidOid, /* Foreign key fields */ NULL, + NULL, + NULL, + NULL, 0, ' ', ' ', |