diff options
author | Bruce Momjian <bruce@momjian.us> | 2006-04-27 00:33:46 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 2006-04-27 00:33:46 +0000 |
commit | 02eb8f4f5c93de08bfd916738e4e3c117d9fd968 (patch) | |
tree | df284cbeb4ffb5669d8efbbb811c5153fab61c37 /src/backend/commands/trigger.c | |
parent | 944a17bf9f7f8394b693b12a5861576964cad97b (diff) | |
download | postgresql-02eb8f4f5c93de08bfd916738e4e3c117d9fd968.tar.gz postgresql-02eb8f4f5c93de08bfd916738e4e3c117d9fd968.zip |
Use schema search path to find the first matching contraint name for SET
CONSTRAINT, rather than affecting all constraints in all schemas (which
is what we used to do). Also allow schema specifications.
Kris Jurka
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 158 |
1 files changed, 114 insertions, 44 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 336aa67fdd5..256b2ca411d 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.200 2006/03/05 15:58:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.201 2006/04/27 00:33:41 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "catalog/pg_proc.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" +#include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/trigger.h" #include "executor/executor.h" @@ -37,6 +38,7 @@ #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" +#include "utils/relcache.h" #include "utils/syscache.h" @@ -2922,65 +2924,133 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) foreach(l, stmt->constraints) { - char *cname = strVal(lfirst(l)); + RangeVar *constraint = lfirst(l); ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; bool found; + List *namespaceSearchList; + ListCell *namespaceSearchCell; - /* - * Check that only named constraints are set explicitly - */ - if (strlen(cname) == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("unnamed constraints cannot be set explicitly"))); + if (constraint->catalogname) + { + if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cross-database references are not implemented: \"%s.%s.%s\"", + constraint->catalogname, constraint->schemaname, + constraint->relname))); + } - /* - * Setup to scan pg_trigger by tgconstrname ... + /* + * If we're given the schema name with the constraint, look only + * in that schema. If given a bare constraint name, use the + * search path to find the first matching constraint. */ - ScanKeyInit(&skey, - Anum_pg_trigger_tgconstrname, - BTEqualStrategyNumber, F_NAMEEQ, - PointerGetDatum(cname)); - - tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true, - SnapshotNow, 1, &skey); + if (constraint->schemaname) { + Oid namespaceId = LookupExplicitNamespace(constraint->schemaname); + namespaceSearchList = list_make1_oid(namespaceId); + } else { + namespaceSearchList = fetch_search_path(true); + } - /* - * ... and search for the constraint trigger row - */ found = false; - - while (HeapTupleIsValid(htup = systable_getnext(tgscan))) + foreach(namespaceSearchCell, namespaceSearchList) { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); + Oid searchNamespaceId = lfirst_oid(namespaceSearchCell); + + /* + * Setup to scan pg_trigger by tgconstrname ... + */ + ScanKeyInit(&skey, + Anum_pg_trigger_tgconstrname, + BTEqualStrategyNumber, F_NAMEEQ, + PointerGetDatum(constraint->relname)); + + tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true, + SnapshotNow, 1, &skey); /* - * If we found some, check that they fit the deferrability but - * skip referential action ones, since they are silently never - * deferrable. + * ... and search for the constraint trigger row */ - if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD && - pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL && - pg_trigger->tgfoid != F_RI_FKEY_CASCADE_UPD && - pg_trigger->tgfoid != F_RI_FKEY_CASCADE_DEL && - pg_trigger->tgfoid != F_RI_FKEY_SETNULL_UPD && - pg_trigger->tgfoid != F_RI_FKEY_SETNULL_DEL && - pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_UPD && - pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_DEL) + while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { - if (stmt->deferred && !pg_trigger->tgdeferrable) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("constraint \"%s\" is not deferrable", - cname))); - oidlist = lappend_oid(oidlist, HeapTupleGetOid(htup)); + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); + Relation constraintRel; + Oid constraintNamespaceId; + + /* + * Foreign key constraints have triggers on both the + * parent and child tables. Since these tables may be + * in different schemas we must pick the child table + * because that table "owns" the constraint. + * + * Referential triggers on the parent table other than + * NOACTION_DEL and NOACTION_UPD are ignored below, so + * it is possible to not check them here, but it seems + * safer to always check. + */ + if (pg_trigger->tgfoid == F_RI_FKEY_NOACTION_DEL || + pg_trigger->tgfoid == F_RI_FKEY_NOACTION_UPD || + pg_trigger->tgfoid == F_RI_FKEY_RESTRICT_UPD || + pg_trigger->tgfoid == F_RI_FKEY_RESTRICT_DEL || + pg_trigger->tgfoid == F_RI_FKEY_CASCADE_UPD || + pg_trigger->tgfoid == F_RI_FKEY_CASCADE_DEL || + pg_trigger->tgfoid == F_RI_FKEY_SETNULL_UPD || + pg_trigger->tgfoid == F_RI_FKEY_SETNULL_DEL || + pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_UPD || + pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_DEL) + { + constraintRel = RelationIdGetRelation(pg_trigger->tgconstrrelid); + } else { + constraintRel = RelationIdGetRelation(pg_trigger->tgrelid); + } + constraintNamespaceId = RelationGetNamespace(constraintRel); + RelationClose(constraintRel); + + /* + * If this constraint is not in the schema we're + * currently searching for, keep looking. + */ + if (constraintNamespaceId != searchNamespaceId) + continue; + + /* + * If we found some, check that they fit the deferrability but + * skip referential action ones, since they are silently never + * deferrable. + */ + if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD && + pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL && + pg_trigger->tgfoid != F_RI_FKEY_CASCADE_UPD && + pg_trigger->tgfoid != F_RI_FKEY_CASCADE_DEL && + pg_trigger->tgfoid != F_RI_FKEY_SETNULL_UPD && + pg_trigger->tgfoid != F_RI_FKEY_SETNULL_DEL && + pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_UPD && + pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_DEL) + { + if (stmt->deferred && !pg_trigger->tgdeferrable) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("constraint \"%s\" is not deferrable", + constraint->relname))); + oidlist = lappend_oid(oidlist, HeapTupleGetOid(htup)); + } + found = true; } - found = true; + + systable_endscan(tgscan); + + /* + * Once we've found a matching constraint we do not search + * later parts of the search path. + */ + if (found) + break; + } - systable_endscan(tgscan); + list_free(namespaceSearchList); /* * Not found ? @@ -2989,7 +3059,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" does not exist", - cname))); + constraint->relname))); } heap_close(tgrel, AccessShareLock); |