From 02eb8f4f5c93de08bfd916738e4e3c117d9fd968 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 27 Apr 2006 00:33:46 +0000 Subject: [PATCH] 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 --- doc/src/sgml/ref/set_constraints.sgml | 17 +-- src/backend/commands/trigger.c | 176 ++++++++++++++++++-------- src/backend/parser/gram.y | 4 +- src/include/nodes/parsenodes.h | 4 +- 4 files changed, 132 insertions(+), 69 deletions(-) diff --git a/doc/src/sgml/ref/set_constraints.sgml b/doc/src/sgml/ref/set_constraints.sgml index 3bcde91f38..9af82008aa 100644 --- a/doc/src/sgml/ref/set_constraints.sgml +++ b/doc/src/sgml/ref/set_constraints.sgml @@ -1,4 +1,4 @@ - + SET CONSTRAINTS @@ -45,10 +45,10 @@ SET CONSTRAINTS { ALL | name [, ... SET CONSTRAINTS with a list of constraint names changes - the mode of just those constraints (which must all be deferrable). If - there are multiple constraints matching any given name, all are affected. - SET CONSTRAINTS ALL changes the mode of all deferrable - constraints. + the mode of just those constraints (which must all be deferrable). The + current schema search path is used to find the first matching name if + no schema name is specified. SET CONSTRAINTS ALL + changes the mode of all deferrable constraints. @@ -93,13 +93,6 @@ SET CONSTRAINTS { ALL | name [, ... foreign-key constraints. - - The SQL standard says that constraint names appearing in SET - CONSTRAINTS can be schema-qualified. This is not yet - supported by PostgreSQL: the names must - be unqualified, and all constraints matching the command will be - affected no matter which schema they are in. - diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 336aa67fdd..256b2ca411 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"))); - - /* - * Setup to scan pg_trigger by tgconstrname ... - */ - ScanKeyInit(&skey, - Anum_pg_trigger_tgconstrname, - BTEqualStrategyNumber, F_NAMEEQ, - PointerGetDatum(cname)); - - tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true, - SnapshotNow, 1, &skey); - - /* - * ... and search for the constraint trigger row - */ - found = false; - - while (HeapTupleIsValid(htup = systable_getnext(tgscan))) + if (constraint->catalogname) { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); - - /* - * 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", - cname))); - oidlist = lappend_oid(oidlist, HeapTupleGetOid(htup)); - } - found = true; + 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))); } - systable_endscan(tgscan); + /* + * 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. + */ + if (constraint->schemaname) { + Oid namespaceId = LookupExplicitNamespace(constraint->schemaname); + namespaceSearchList = list_make1_oid(namespaceId); + } else { + namespaceSearchList = fetch_search_path(true); + } + + found = false; + foreach(namespaceSearchCell, namespaceSearchList) + { + 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); + + /* + * ... and search for the constraint trigger row + */ + while (HeapTupleIsValid(htup = systable_getnext(tgscan))) + { + 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; + } + + systable_endscan(tgscan); + + /* + * Once we've found a matching constraint we do not search + * later parts of the search path. + */ + if (found) + break; + + } + + 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); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 46aff33bc2..8870261e44 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.542 2006/04/25 14:11:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.543 2006/04/27 00:33:45 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1283,7 +1283,7 @@ ConstraintsSetStmt: constraints_set_list: ALL { $$ = NIL; } - | name_list { $$ = $1; } + | qualified_name_list { $$ = $1; } ; constraints_set_mode: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e4eb385df8..2da0f6605d 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.307 2006/04/15 17:45:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.308 2006/04/27 00:33:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1782,7 +1782,7 @@ typedef struct LockStmt typedef struct ConstraintsSetStmt { NodeTag type; - List *constraints; /* List of names as Value strings */ + List *constraints; /* List of names as RangeVars */ bool deferred; } ConstraintsSetStmt;