From 35508d1cca1630e40b157d67b427174c3e1999aa Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 1 Aug 2005 04:03:59 +0000 Subject: [PATCH] Add ALTER object SET SCHEMA capability for a limited but useful set of object kinds (tables, functions, types). Documentation is not here yet. Original code by Bernd Helmle, extensive rework by Bruce Momjian and Tom Lane. --- src/backend/catalog/namespace.c | 60 +++++- src/backend/catalog/pg_constraint.c | 68 ++++++- src/backend/catalog/pg_depend.c | 101 +++++++++- src/backend/commands/alter.c | 34 +++- src/backend/commands/functioncmds.c | 88 ++++++++- src/backend/commands/tablecmds.c | 296 +++++++++++++++++++++++++++++- src/backend/commands/typecmds.c | 171 ++++++++++++++++- src/backend/nodes/copyfuncs.c | 24 ++- src/backend/nodes/equalfuncs.c | 22 ++- src/backend/parser/gram.y | 67 ++++++- src/backend/tcop/utility.c | 44 ++++- src/include/catalog/dependency.h | 6 +- src/include/catalog/namespace.h | 4 +- src/include/catalog/pg_constraint.h | 5 +- src/include/commands/alter.h | 4 +- src/include/commands/defrem.h | 4 +- src/include/commands/tablecmds.h | 9 +- src/include/commands/typecmds.h | 5 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 21 ++- src/test/regress/expected/alter_table.out | 59 ++++++ src/test/regress/sql/alter_table.sql | 41 +++++ 22 files changed, 1095 insertions(+), 41 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 6b12bda661..e74b331e37 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.76 2005/06/28 05:08:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.77 2005/08/01 04:03:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1236,11 +1236,42 @@ LookupExplicitNamespace(const char *nspname) } /* + * LookupCreationNamespace + * Look up the schema and verify we have CREATE rights on it. + * + * This is just like LookupExplicitNamespace except for the permission check. + */ +Oid +LookupCreationNamespace(const char *nspname) +{ + Oid namespaceId; + AclResult aclresult; + + namespaceId = GetSysCacheOid(NAMESPACENAME, + CStringGetDatum(nspname), + 0, 0, 0); + if (!OidIsValid(namespaceId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", nspname))); + + aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); + + return namespaceId; +} + +/* * QualifiedNameGetCreationNamespace * Given a possibly-qualified name for an object (in List-of-Values * format), determine what namespace the object should be created in. * Also extract and return the object name (last component of list). * + * Note: this does not apply any permissions check. Callers must check + * for CREATE rights on the selected namespace when appropriate. + * * This is *not* used for tables. Hence, the TEMP table namespace is * never selected as the creation target. */ @@ -1277,8 +1308,6 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p) errmsg("no schema has been selected to create in"))); } - /* Note: callers will check for CREATE rights when appropriate */ - *objname_p = objname; return namespaceId; } @@ -1379,19 +1408,16 @@ isTempNamespace(Oid namespaceId) } /* - * isOtherTempNamespace - is the given namespace some other backend's - * temporary-table namespace? + * isAnyTempNamespace - is the given namespace a temporary-table namespace + * (either my own, or another backend's)? */ bool -isOtherTempNamespace(Oid namespaceId) +isAnyTempNamespace(Oid namespaceId) { bool result; char *nspname; - /* If it's my own temp namespace, say "false" */ - if (isTempNamespace(namespaceId)) - return false; - /* Else, if the namespace name starts with "pg_temp_", say "true" */ + /* If the namespace name starts with "pg_temp_", say "true" */ nspname = get_namespace_name(namespaceId); if (!nspname) return false; /* no such namespace? */ @@ -1401,6 +1427,20 @@ isOtherTempNamespace(Oid namespaceId) } /* + * isOtherTempNamespace - is the given namespace some other backend's + * temporary-table namespace? + */ +bool +isOtherTempNamespace(Oid namespaceId) +{ + /* If it's my own temp namespace, say "false" */ + if (isTempNamespace(namespaceId)) + return false; + /* Else, if the namespace name starts with "pg_temp_", say "true" */ + return isAnyTempNamespace(namespaceId); +} + +/* * PushSpecialNamespace - push a "special" namespace onto the front of the * search path. * diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index a383647a93..b2cc3d5c47 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.25 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.26 2005/08/01 04:03:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -599,3 +599,69 @@ GetConstraintNameForTrigger(Oid triggerId) return result; } + +/* + * AlterConstraintNamespaces + * Find any constraints belonging to the specified object, + * and move them to the specified new namespace. + * + * isType indicates whether the owning object is a type or a relation. + */ +void +AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, + Oid newNspId, bool isType) +{ + Relation conRel; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; + + conRel = heap_open(ConstraintRelationId, RowExclusiveLock); + + if (isType) + { + ScanKeyInit(&key[0], + Anum_pg_constraint_contypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ownerId)); + + scan = systable_beginscan(conRel, ConstraintTypidIndexId, true, + SnapshotNow, 1, key); + } + else + { + ScanKeyInit(&key[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ownerId)); + + scan = systable_beginscan(conRel, ConstraintRelidIndexId, true, + SnapshotNow, 1, key); + } + + while (HeapTupleIsValid((tup = systable_getnext(scan)))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(tup); + + if (conform->connamespace == oldNspId) + { + tup = heap_copytuple(tup); + conform = (Form_pg_constraint) GETSTRUCT(tup); + + conform->connamespace = newNspId; + + simple_heap_update(conRel, &tup->t_self, tup); + CatalogUpdateIndexes(conRel, tup); + + /* + * Note: currently, the constraint will not have its own + * dependency on the namespace, so we don't need to do + * changeDependencyFor(). + */ + } + } + + systable_endscan(scan); + + heap_close(conRel, RowExclusiveLock); +} diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 6a7b54e88a..bf910d09a5 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.13 2005/04/14 20:03:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.14 2005/08/01 04:03:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,6 +163,105 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId) } /* + * Adjust dependency record(s) to point to a different object of the same type + * + * classId/objectId specify the referencing object. + * refClassId/oldRefObjectId specify the old referenced object. + * newRefObjectId is the new referenced object (must be of class refClassId). + * + * Note the lack of objsubid parameters. If there are subobject references + * they will all be readjusted. + * + * Returns the number of records updated. + */ +long +changeDependencyFor(Oid classId, Oid objectId, + Oid refClassId, Oid oldRefObjectId, + Oid newRefObjectId) +{ + long count = 0; + Relation depRel; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + ObjectAddress objAddr; + bool newIsPinned; + + depRel = heap_open(DependRelationId, RowExclusiveLock); + + /* + * If oldRefObjectId is pinned, there won't be any dependency entries + * on it --- we can't cope in that case. (This isn't really worth + * expending code to fix, in current usage; it just means you can't + * rename stuff out of pg_catalog, which would likely be a bad move + * anyway.) + */ + objAddr.classId = refClassId; + objAddr.objectId = oldRefObjectId; + objAddr.objectSubId = 0; + + if (isObjectPinned(&objAddr, depRel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot remove dependency on %s because it is a system object", + getObjectDescription(&objAddr)))); + + /* + * We can handle adding a dependency on something pinned, though, + * since that just means deleting the dependency entry. + */ + objAddr.objectId = newRefObjectId; + + newIsPinned = isObjectPinned(&objAddr, depRel); + + /* Now search for dependency records */ + ScanKeyInit(&key[0], + Anum_pg_depend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[1], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(depRel, DependDependerIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid((tup = systable_getnext(scan)))) + { + Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); + + if (depform->refclassid == refClassId && + depform->refobjid == oldRefObjectId) + { + if (newIsPinned) + simple_heap_delete(depRel, &tup->t_self); + else + { + /* make a modifiable copy */ + tup = heap_copytuple(tup); + depform = (Form_pg_depend) GETSTRUCT(tup); + + depform->refobjid = newRefObjectId; + + simple_heap_update(depRel, &tup->t_self, tup); + CatalogUpdateIndexes(depRel, tup); + + heap_freetuple(tup); + } + + count++; + } + } + + systable_endscan(scan); + + heap_close(depRel, RowExclusiveLock); + + return count; +} + +/* * isObjectPinned() * * Test if an object is required for basic database functionality. diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index a19b500152..996d70e163 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.13 2005/06/28 05:08:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.14 2005/08/01 04:03:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -143,6 +143,38 @@ ExecRenameStmt(RenameStmt *stmt) } /* + * Executes an ALTER OBJECT / SET SCHEMA statement. Based on the object + * type, the function appropriate to that type is executed. + */ +void +ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) +{ + switch (stmt->objectType) + { + case OBJECT_AGGREGATE: + case OBJECT_FUNCTION: + AlterFunctionNamespace(stmt->object, stmt->objarg, + stmt->newschema); + break; + + case OBJECT_SEQUENCE: + case OBJECT_TABLE: + CheckRelationOwnership(stmt->relation, true); + AlterTableNamespace(stmt->relation, stmt->newschema); + break; + + case OBJECT_TYPE: + case OBJECT_DOMAIN: + AlterTypeNamespace(stmt->object, stmt->newschema); + break; + + default: + elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d", + (int) stmt->objectType); + } +} + +/* * Executes an ALTER OBJECT / OWNER TO statement. Based on the object * type, the function appropriate to that type is executed. */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 7e205890c9..38912b777d 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.64 2005/07/14 21:46:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.65 2005/08/01 04:03:55 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -40,6 +40,7 @@ #include "catalog/pg_aggregate.h" #include "catalog/pg_cast.h" #include "catalog/pg_language.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -1427,3 +1428,88 @@ DropCastById(Oid castOid) systable_endscan(scan); heap_close(relation, RowExclusiveLock); } + +/* + * Execute ALTER FUNCTION SET SCHEMA + */ +void +AlterFunctionNamespace(List *name, List *argtypes, const char *newschema) +{ + Oid procOid; + Oid oldNspOid; + Oid nspOid; + HeapTuple tup; + Relation procRel; + Form_pg_proc proc; + + procRel = heap_open(ProcedureRelationId, RowExclusiveLock); + + /* get function OID */ + procOid = LookupFuncNameTypeNames(name, argtypes, false); + + /* check permissions on function */ + if (!pg_proc_ownercheck(procOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(name)); + + tup = SearchSysCacheCopy(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for function %u", procOid); + proc = (Form_pg_proc) GETSTRUCT(tup); + + oldNspOid = proc->pronamespace; + + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("function \"%s\" is already in schema \"%s\"", + NameListToString(name), + newschema))); + + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of temporary schemas"))); + + /* same for TOAST schema */ + if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of TOAST schema"))); + + /* check for duplicate name (more friendly than unique-index failure) */ + if (SearchSysCacheExists(PROCNAMEARGSNSP, + CStringGetDatum(NameStr(proc->proname)), + PointerGetDatum(&proc->proargtypes), + ObjectIdGetDatum(nspOid), + 0)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_FUNCTION), + errmsg("function \"%s\" already exists in schema \"%s\"", + NameStr(proc->proname), + newschema))); + + /* OK, modify the pg_proc row */ + + /* tup is a copy, so we can scribble directly on it */ + proc->pronamespace = nspOid; + + simple_heap_update(procRel, &tup->t_self, tup); + CatalogUpdateIndexes(procRel, tup); + + /* Update dependency on schema */ + if (changeDependencyFor(ProcedureRelationId, procOid, + NamespaceRelationId, oldNspOid, nspOid) != 1) + elog(ERROR, "failed to change schema dependency for function \"%s\"", + NameListToString(name)); + + heap_freetuple(tup); + + heap_close(procRel, RowExclusiveLock); +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index c06c272cbd..7d9a73917d 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.164 2005/07/14 21:46:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.165 2005/08/01 04:03:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,6 +163,13 @@ static void StoreCatalogInheritance(Oid relationId, List *supers); static int findAttrByName(const char *attributeName, List *schema); static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass); static bool needs_toast_table(Relation rel); +static void AlterIndexNamespaces(Relation classRel, Relation rel, + Oid oldNspOid, Oid newNspOid); +static void AlterSeqNamespaces(Relation classRel, Relation rel, + Oid oldNspOid, Oid newNspOid, + const char *newNspName); +static void RebuildSerialDefaultExpr(Relation rel, AttrNumber attnum, + const char *seqname, const char *nspname); static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids); static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, @@ -5999,6 +6006,293 @@ needs_toast_table(Relation rel) /* + * Execute ALTER TABLE SET SCHEMA + * + * Note: caller must have checked ownership of the relation already + */ +void +AlterTableNamespace(RangeVar *relation, const char *newschema) +{ + Relation rel; + Oid relid; + Oid oldNspOid; + Oid nspOid; + Relation classRel; + + rel = heap_openrv(relation, AccessExclusiveLock); + + /* heap_openrv allows TOAST, but we don't want to */ + if (rel->rd_rel->relkind == RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a TOAST relation", + RelationGetRelationName(rel)))); + + relid = RelationGetRelid(rel); + oldNspOid = RelationGetNamespace(rel); + + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("relation \"%s\" is already in schema \"%s\"", + RelationGetRelationName(rel), + newschema))); + + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of temporary schemas"))); + + /* same for TOAST schema */ + if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of TOAST schema"))); + + /* OK, modify the pg_class row and pg_depend entry */ + classRel = heap_open(RelationRelationId, RowExclusiveLock); + + AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true); + + /* Fix the table's rowtype too */ + AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false); + + /* Fix other dependent stuff */ + if (rel->rd_rel->relkind == RELKIND_RELATION) + { + AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid); + AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, newschema); + AlterConstraintNamespaces(relid, oldNspOid, nspOid, false); + } + + heap_close(classRel, RowExclusiveLock); + + /* close rel, but keep lock until commit */ + relation_close(rel, NoLock); +} + +/* + * The guts of relocating a relation to another namespace: fix the pg_class + * entry, and the pg_depend entry if any. Caller must already have + * opened and write-locked pg_class. + */ +void +AlterRelationNamespaceInternal(Relation classRel, Oid relOid, + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry) +{ + HeapTuple classTup; + Form_pg_class classForm; + + classTup = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(classTup)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + classForm = (Form_pg_class) GETSTRUCT(classTup); + + Assert(classForm->relnamespace == oldNspOid); + + /* check for duplicate name (more friendly than unique-index failure) */ + if (get_relname_relid(NameStr(classForm->relname), + newNspOid) != InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("relation \"%s\" already exists in schema \"%s\"", + NameStr(classForm->relname), + get_namespace_name(newNspOid)))); + + /* classTup is a copy, so OK to scribble on */ + classForm->relnamespace = newNspOid; + + simple_heap_update(classRel, &classTup->t_self, classTup); + CatalogUpdateIndexes(classRel, classTup); + + /* Update dependency on schema if caller said so */ + if (hasDependEntry && + changeDependencyFor(RelationRelationId, relOid, + NamespaceRelationId, oldNspOid, newNspOid) != 1) + elog(ERROR, "failed to change schema dependency for relation \"%s\"", + NameStr(classForm->relname)); + + heap_freetuple(classTup); +} + +/* + * Move all indexes for the specified relation to another namespace. + * + * Note: we assume adequate permission checking was done by the caller, + * and that the caller has a suitable lock on the owning relation. + */ +static void +AlterIndexNamespaces(Relation classRel, Relation rel, + Oid oldNspOid, Oid newNspOid) +{ + List *indexList; + ListCell *l; + + indexList = RelationGetIndexList(rel); + + foreach(l, indexList) + { + Oid indexOid = lfirst_oid(l); + + /* + * Note: currently, the index will not have its own dependency + * on the namespace, so we don't need to do changeDependencyFor(). + * There's no rowtype in pg_type, either. + */ + AlterRelationNamespaceInternal(classRel, indexOid, + oldNspOid, newNspOid, + false); + } + + list_free(indexList); +} + +/* + * Move all SERIAL-column sequences of the specified relation to another + * namespace. + * + * Note: we assume adequate permission checking was done by the caller, + * and that the caller has a suitable lock on the owning relation. + */ +static void +AlterSeqNamespaces(Relation classRel, Relation rel, + Oid oldNspOid, Oid newNspOid, const char *newNspName) +{ + Relation depRel; + SysScanDesc scan; + ScanKeyData key[2]; + HeapTuple tup; + + /* + * SERIAL sequences are those having an internal dependency on one + * of the table's columns (we don't care *which* column, exactly). + */ + depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationRelationId)); + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + /* we leave refobjsubid unspecified */ + + scan = systable_beginscan(depRel, DependReferenceIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup); + Relation seqRel; + + /* skip dependencies other than internal dependencies on columns */ + if (depForm->refobjsubid == 0 || + depForm->classid != RelationRelationId || + depForm->objsubid != 0 || + depForm->deptype != DEPENDENCY_INTERNAL) + continue; + + /* Use relation_open just in case it's an index */ + seqRel = relation_open(depForm->objid, AccessExclusiveLock); + + /* skip non-sequence relations */ + if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE) + { + /* No need to keep the lock */ + relation_close(seqRel, AccessExclusiveLock); + continue; + } + + /* Fix the pg_class and pg_depend entries */ + AlterRelationNamespaceInternal(classRel, depForm->objid, + oldNspOid, newNspOid, + true); + /* + * Sequences have entries in pg_type. We need to be careful + * to move them to the new namespace, too. + */ + AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype, + newNspOid, false); + /* + * And we need to rebuild the column default expression that + * relies on this sequence. + */ + if (depForm->refobjsubid > 0) + RebuildSerialDefaultExpr(rel, + depForm->refobjsubid, + RelationGetRelationName(seqRel), + newNspName); + + /* Now we can close it. Keep the lock till end of transaction. */ + relation_close(seqRel, NoLock); + } + + systable_endscan(scan); + + relation_close(depRel, AccessShareLock); +} + +/* + * Rebuild the default expression for a SERIAL column identified by rel + * and attnum. This is annoying, but we have to do it because the + * stored expression has the schema name as a text constant. + * + * The caller must be sure the specified column is really a SERIAL column, + * because no further checks are done here. + */ +static void +RebuildSerialDefaultExpr(Relation rel, AttrNumber attnum, + const char *seqname, const char *nspname) +{ + char *qstring; + A_Const *snamenode; + FuncCall *funccallnode; + RawColumnDefault *rawEnt; + + /* + * Create raw parse tree for the updated column default expression. + * This should match transformColumnDefinition() in parser/analyze.c. + */ + qstring = quote_qualified_identifier(nspname, seqname); + snamenode = makeNode(A_Const); + snamenode->val.type = T_String; + snamenode->val.val.str = qstring; + funccallnode = makeNode(FuncCall); + funccallnode->funcname = SystemFuncName("nextval"); + funccallnode->args = list_make1(snamenode); + funccallnode->agg_star = false; + funccallnode->agg_distinct = false; + + /* + * Remove any old default for the column. We use RESTRICT here for + * safety, but at present we do not expect anything to depend on the + * default. + */ + RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false); + + /* Do the equivalent of ALTER TABLE ... SET DEFAULT */ + rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); + rawEnt->attnum = attnum; + rawEnt->raw_default = (Node *) funccallnode; + + /* + * This function is intended for CREATE TABLE, so it processes a + * _list_ of defaults, but we just do one. + */ + AddRelationRawConstraints(rel, list_make1(rawEnt), NIL); +} + + +/* * This code supports * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS } * diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 0979e8aeec..80d394b293 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.76 2005/07/14 21:46:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.77 2005/08/01 04:03:55 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -39,6 +39,7 @@ #include "catalog/namespace.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" +#include "catalog/pg_namespace.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/tablecmds.h" @@ -2100,3 +2101,171 @@ AlterTypeOwner(List *names, Oid newOwnerId) /* Clean up */ heap_close(rel, RowExclusiveLock); } + +/* + * Execute ALTER TYPE SET SCHEMA + */ +void +AlterTypeNamespace(List *names, const char *newschema) +{ + TypeName *typename; + Oid typeOid; + Oid nspOid; + + /* get type OID */ + typename = makeNode(TypeName); + typename->names = names; + typename->typmod = -1; + typename->arrayBounds = NIL; + + typeOid = LookupTypeName(typename); + + if (!OidIsValid(typeOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(typename)))); + + /* check permissions on type */ + if (!pg_type_ownercheck(typeOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + + /* and do the work */ + AlterTypeNamespaceInternal(typeOid, nspOid, true); +} + +/* + * Move specified type to new namespace. + * + * Caller must have already checked privileges. + * + * If errorOnTableType is TRUE, the function errors out if the type is + * a table type. ALTER TABLE has to be used to move a table to a new + * namespace. + */ +void +AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + bool errorOnTableType) +{ + Relation rel; + HeapTuple tup; + Form_pg_type typform; + Oid oldNspOid; + bool isCompositeType; + + rel = heap_open(TypeRelationId, RowExclusiveLock); + + tup = SearchSysCacheCopy(TYPEOID, + ObjectIdGetDatum(typeOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for type %u", typeOid); + typform = (Form_pg_type) GETSTRUCT(tup); + + oldNspOid = typform->typnamespace; + + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type %s is already in schema \"%s\"", + format_type_be(typeOid), + get_namespace_name(nspOid)))); + + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of temporary schemas"))); + + /* same for TOAST schema */ + if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move objects into or out of TOAST schema"))); + + /* check for duplicate name (more friendly than unique-index failure) */ + if (SearchSysCacheExists(TYPENAMENSP, + CStringGetDatum(NameStr(typform->typname)), + ObjectIdGetDatum(nspOid), + 0, 0)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists in schema \"%s\"", + NameStr(typform->typname), + get_namespace_name(nspOid)))); + + /* Detect whether type is a composite type (but not a table rowtype) */ + isCompositeType = + (typform->typtype == 'c' && + get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE); + + /* Enforce not-table-type if requested */ + if (typform->typtype == 'c' && !isCompositeType && errorOnTableType) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s is a table's row type", + format_type_be(typeOid)), + errhint("Use ALTER TABLE SET SCHEMA instead."))); + + /* OK, modify the pg_type row */ + + /* tup is a copy, so we can scribble directly on it */ + typform->typnamespace = nspOid; + + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + /* + * Composite types have pg_class entries. + * + * We need to modify the pg_class tuple as well to + * reflect the change of schema. + */ + if (isCompositeType) + { + Relation classRel; + + classRel = heap_open(RelationRelationId, RowExclusiveLock); + + /* + * The dependency on the schema is listed under the pg_class entry, + * so tell AlterRelationNamespaceInternal to fix it. + */ + AlterRelationNamespaceInternal(classRel, typform->typrelid, + oldNspOid, nspOid, + true); + + heap_close(classRel, RowExclusiveLock); + + /* + * Check for constraints associated with the composite type + * (we don't currently support this, but probably will someday). + */ + AlterConstraintNamespaces(typform->typrelid, oldNspOid, + nspOid, false); + } + else + { + /* If it's a domain, it might have constraints */ + if (typform->typtype == 'd') + AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true); + + /* + * Update dependency on schema, if any --- a table rowtype has not + * got one. + */ + if (typform->typtype != 'c') + if (changeDependencyFor(TypeRelationId, typeOid, + NamespaceRelationId, oldNspOid, nspOid) != 1) + elog(ERROR, "failed to change schema dependency for type %s", + format_type_be(typeOid)); + } + + heap_freetuple(tup); + + heap_close(rel, RowExclusiveLock); +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 35fbee6164..f3189fc14a 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.313 2005/07/31 17:19:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.314 2005/08/01 04:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2035,12 +2035,27 @@ _copyRenameStmt(RenameStmt *from) { RenameStmt *newnode = makeNode(RenameStmt); + COPY_SCALAR_FIELD(renameType); COPY_NODE_FIELD(relation); COPY_NODE_FIELD(object); COPY_NODE_FIELD(objarg); COPY_STRING_FIELD(subname); COPY_STRING_FIELD(newname); - COPY_SCALAR_FIELD(renameType); + + return newnode; +} + +static AlterObjectSchemaStmt * +_copyAlterObjectSchemaStmt(AlterObjectSchemaStmt *from) +{ + AlterObjectSchemaStmt *newnode = makeNode(AlterObjectSchemaStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_NODE_FIELD(objarg); + COPY_STRING_FIELD(addname); + COPY_STRING_FIELD(newschema); return newnode; } @@ -2050,12 +2065,12 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) { AlterOwnerStmt *newnode = makeNode(AlterOwnerStmt); + COPY_SCALAR_FIELD(objectType); COPY_NODE_FIELD(relation); COPY_NODE_FIELD(object); COPY_NODE_FIELD(objarg); COPY_STRING_FIELD(addname); COPY_STRING_FIELD(newowner); - COPY_SCALAR_FIELD(objectType); return newnode; } @@ -2983,6 +2998,9 @@ copyObject(void *from) case T_RenameStmt: retval = _copyRenameStmt(from); break; + case T_AlterObjectSchemaStmt: + retval = _copyAlterObjectSchemaStmt(from); + break; case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 6d727a6327..126647a775 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.250 2005/07/31 17:19:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.251 2005/08/01 04:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1008,12 +1008,25 @@ _equalRemoveOpClassStmt(RemoveOpClassStmt *a, RemoveOpClassStmt *b) static bool _equalRenameStmt(RenameStmt *a, RenameStmt *b) { + COMPARE_SCALAR_FIELD(renameType); COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(object); COMPARE_NODE_FIELD(objarg); COMPARE_STRING_FIELD(subname); COMPARE_STRING_FIELD(newname); - COMPARE_SCALAR_FIELD(renameType); + + return true; +} + +static bool +_equalAlterObjectSchemaStmt(AlterObjectSchemaStmt *a, AlterObjectSchemaStmt *b) +{ + COMPARE_SCALAR_FIELD(objectType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_NODE_FIELD(objarg); + COMPARE_STRING_FIELD(addname); + COMPARE_STRING_FIELD(newschema); return true; } @@ -1021,12 +1034,12 @@ _equalRenameStmt(RenameStmt *a, RenameStmt *b) static bool _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) { + COMPARE_SCALAR_FIELD(objectType); COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(object); COMPARE_NODE_FIELD(objarg); COMPARE_STRING_FIELD(addname); COMPARE_STRING_FIELD(newowner); - COMPARE_SCALAR_FIELD(objectType); return true; } @@ -2029,6 +2042,9 @@ equal(void *a, void *b) case T_RenameStmt: retval = _equalRenameStmt(a, b); break; + case T_AlterObjectSchemaStmt: + retval = _equalAlterObjectSchemaStmt(a, b); + break; case T_AlterOwnerStmt: retval = _equalAlterOwnerStmt(a, b); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index f0475bf2be..8e79355cdd 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.505 2005/07/31 17:19:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.506 2005/08/01 04:03:56 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -132,7 +132,7 @@ static void doNegateFloat(Value *v); %type stmt schema_stmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt - AlterOwnerStmt AlterSeqStmt AlterTableStmt + AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt @@ -493,6 +493,7 @@ stmt : | AlterDomainStmt | AlterFunctionStmt | AlterGroupStmt + | AlterObjectSchemaStmt | AlterOwnerStmt | AlterSeqStmt | AlterTableStmt @@ -3954,10 +3955,10 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name | ALTER TRIGGER name ON relation_expr RENAME TO name { RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_TRIGGER; n->relation = $5; n->subname = $3; n->newname = $8; - n->renameType = OBJECT_TRIGGER; $$ = (Node *)n; } | ALTER ROLE RoleId RENAME TO RoleId @@ -3990,10 +3991,68 @@ opt_column: COLUMN { $$ = COLUMN; } | /*EMPTY*/ { $$ = 0; } ; +/***************************************************************************** + * + * ALTER THING name SET SCHEMA name + * + *****************************************************************************/ + +AlterObjectSchemaStmt: + ALTER AGGREGATE func_name '(' aggr_argtype ')' SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_AGGREGATE; + n->object = $3; + n->objarg = list_make1($5); + n->newschema = $9; + $$ = (Node *)n; + } + | ALTER DOMAIN_P any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_DOMAIN; + n->object = $3; + n->newschema = $6; + $$ = (Node *)n; + } + | ALTER FUNCTION func_name func_args SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_FUNCTION; + n->object = $3; + n->objarg = extractArgTypes($4); + n->newschema = $7; + $$ = (Node *)n; + } + | ALTER SEQUENCE relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $3; + n->newschema = $6; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TABLE; + n->relation = $3; + n->newschema = $6; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_TYPE; + n->object = $3; + n->newschema = $6; + $$ = (Node *)n; + } + ; /***************************************************************************** * - * ALTER THING name OWNER TO newname. + * ALTER THING name OWNER TO newname * *****************************************************************************/ diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 546aa49c2d..10256bbb32 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.242 2005/07/31 17:19:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.243 2005/08/01 04:03:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -281,6 +281,7 @@ check_xact_readonly(Node *parsetree) case T_AlterFunctionStmt: case T_AlterRoleStmt: case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: case T_AlterOwnerStmt: case T_AlterSeqStmt: case T_AlterTableStmt: @@ -625,6 +626,10 @@ ProcessUtility(Node *parsetree, ExecRenameStmt((RenameStmt *) parsetree); break; + case T_AlterObjectSchemaStmt: + ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree); + break; + case T_AlterOwnerStmt: ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; @@ -1358,6 +1363,10 @@ CreateCommandTag(Node *parsetree) case OBJECT_SCHEMA: tag = "ALTER SCHEMA"; break; + case OBJECT_COLUMN: + case OBJECT_TABLE: + tag = "ALTER TABLE"; + break; case OBJECT_TABLESPACE: tag = "ALTER TABLESPACE"; break; @@ -1365,10 +1374,38 @@ CreateCommandTag(Node *parsetree) tag = "ALTER TRIGGER"; break; default: - tag = "ALTER TABLE"; + tag = "???"; + break; } break; + case T_AlterObjectSchemaStmt: + switch (((AlterObjectSchemaStmt *) parsetree)->objectType) + { + case OBJECT_AGGREGATE: + tag = "ALTER AGGREGATE"; + break; + case OBJECT_DOMAIN: + tag = "ALTER DOMAIN"; + break; + case OBJECT_FUNCTION: + tag = "ALTER FUNCTION"; + break; + case OBJECT_SEQUENCE: + tag = "ALTER SEQUENCE"; + break; + case OBJECT_TABLE: + tag = "ALTER TABLE"; + break; + case OBJECT_TYPE: + tag = "ALTER TYPE"; + break; + default: + tag = "???"; + break; + } + break; + case T_AlterOwnerStmt: switch (((AlterOwnerStmt *) parsetree)->objectType) { @@ -1403,7 +1440,8 @@ CreateCommandTag(Node *parsetree) tag = "ALTER TYPE"; break; default: - tag = "ALTER TABLE"; + tag = "???"; + break; } break; diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 06fdf02a10..ab50817cb7 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.15 2005/07/07 20:39:59 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.16 2005/08/01 04:03:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -174,6 +174,10 @@ extern void recordMultipleDependencies(const ObjectAddress *depender, extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); +extern long changeDependencyFor(Oid classId, Oid objectId, + Oid refClassId, Oid oldRefObjectId, + Oid newRefObjectId); + /* in pg_shdepend.c */ extern void recordSharedDependencyOn(ObjectAddress *depender, diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 4176f2c218..060849e8ba 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.35 2004/12/31 22:03:24 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.36 2005/08/01 04:03:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -74,12 +74,14 @@ extern void DeconstructQualifiedName(List *names, char **objname_p); extern Oid LookupExplicitNamespace(const char *nspname); +extern Oid LookupCreationNamespace(const char *nspname); extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p); extern RangeVar *makeRangeVarFromNameList(List *names); extern char *NameListToString(List *names); extern char *NameListToQuotedString(List *names); extern bool isTempNamespace(Oid namespaceId); +extern bool isAnyTempNamespace(Oid namespaceId); extern bool isOtherTempNamespace(Oid namespaceId); extern void PushSpecialNamespace(Oid namespaceId); diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 288a6adfa5..d0495ca0e5 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.16 2005/04/14 01:38:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.17 2005/08/01 04:03:57 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -185,4 +185,7 @@ extern char *ChooseConstraintName(const char *name1, const char *name2, extern char *GetConstraintNameForTrigger(Oid triggerId); +extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, + Oid newNspId, bool isType); + #endif /* PG_CONSTRAINT_H */ diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h index 1a34030bff..33588bad32 100644 --- a/src/include/commands/alter.h +++ b/src/include/commands/alter.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/alter.h,v 1.6 2004/12/31 22:03:28 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/commands/alter.h,v 1.7 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include "nodes/parsenodes.h" extern void ExecRenameStmt(RenameStmt *stmt); - +extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); #endif /* ALTER_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 10c3438065..a0f3dc67d6 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.66 2005/06/28 05:09:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.67 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,6 +55,8 @@ extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); extern void DropCastById(Oid castOid); +extern void AlterFunctionNamespace(List *name, List *argtypes, + const char *newschema); /* commands/operatorcmds.c */ extern void DefineOperator(List *names, List *parameters); diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 5ec8c9a57d..582720df3b 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.22 2005/01/27 03:18:24 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.23 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,7 @@ #define TABLECMDS_H #include "nodes/parsenodes.h" +#include "utils/rel.h" extern Oid DefineRelation(CreateStmt *stmt, char relkind); @@ -27,6 +28,12 @@ extern void AlterTableInternal(Oid relid, List *cmds, bool recurse); extern void AlterTableCreateToastTable(Oid relOid, bool silent); +extern void AlterTableNamespace(RangeVar *relation, const char *newschema); + +extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry); + extern void ExecuteTruncate(List *relations); extern void renameatt(Oid myrelid, diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index a940a78f48..a070a27a29 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.11 2005/06/28 05:09:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.12 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,5 +35,8 @@ extern void AlterDomainDropConstraint(List *names, const char *constrName, extern List *GetDomainConstraints(Oid typeOid); extern void AlterTypeOwner(List *names, Oid newOwnerId); +extern void AlterTypeNamespace(List *names, const char *newschema); +extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + bool errorOnTableType); #endif /* TYPECMDS_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index a488d8dce7..24480f1281 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.173 2005/07/31 17:19:21 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.174 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -284,6 +284,7 @@ typedef enum NodeTag T_DeclareCursorStmt, T_CreateTableSpaceStmt, T_DropTableSpaceStmt, + T_AlterObjectSchemaStmt, T_AlterOwnerStmt, T_A_Expr = 800, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 68262d28f7..fc88dec535 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.287 2005/07/31 17:19:21 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.288 2005/08/01 04:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1477,28 +1477,43 @@ typedef struct RemoveOpClassStmt typedef struct RenameStmt { NodeTag type; + ObjectType renameType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ RangeVar *relation; /* in case it's a table */ List *object; /* in case it's some other object */ List *objarg; /* argument types, if applicable */ char *subname; /* name of contained object (column, rule, * trigger, etc) */ char *newname; /* the new name */ - ObjectType renameType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ } RenameStmt; /* ---------------------- + * ALTER object SET SCHEMA Statement + * ---------------------- + */ +typedef struct AlterObjectSchemaStmt +{ + NodeTag type; + ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ + RangeVar *relation; /* in case it's a table */ + List *object; /* in case it's some other object */ + List *objarg; /* argument types, if applicable */ + char *addname; /* additional name if needed */ + char *newschema; /* the new schema */ +} AlterObjectSchemaStmt; + +/* ---------------------- * Alter Object Owner Statement * ---------------------- */ typedef struct AlterOwnerStmt { NodeTag type; + ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ RangeVar *relation; /* in case it's a table */ List *object; /* in case it's some other object */ List *objarg; /* argument types, if applicable */ char *addname; /* additional name if needed */ char *newowner; /* the new owner */ - ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */ } AlterOwnerStmt; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 54205395e2..b6e46393b4 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1274,3 +1274,62 @@ select non_strict(NULL); (1 row) +-- +-- alter object set schema +-- +create schema alter1; +create schema alter2; +create table alter1.t1(f1 serial primary key, f2 int check (f2 > 0)); +NOTICE: CREATE TABLE will create implicit sequence "t1_f1_seq" for serial column "t1.f1" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1" +create view alter1.v1 as select * from alter1.t1; +create function alter1.plus1(int) returns int as 'select $1+1' language sql; +create domain alter1.posint integer check (value > 0); +create type alter1.ctype as (f1 int, f2 text); +insert into alter1.t1(f2) values(11); +insert into alter1.t1(f2) values(12); +alter table alter1.t1 set schema alter2; +alter table alter1.v1 set schema alter2; +alter function alter1.plus1(int) set schema alter2; +alter domain alter1.posint set schema alter2; +alter type alter1.ctype set schema alter2; +-- this should succeed because nothing is left in alter1 +drop schema alter1; +insert into alter2.t1(f2) values(13); +insert into alter2.t1(f2) values(14); +select * from alter2.t1; + f1 | f2 +----+---- + 1 | 11 + 2 | 12 + 3 | 13 + 4 | 14 +(4 rows) + +select * from alter2.v1; + f1 | f2 +----+---- + 1 | 11 + 2 | 12 + 3 | 13 + 4 | 14 +(4 rows) + +select alter2.plus1(41); + plus1 +------- + 42 +(1 row) + +-- clean up +drop schema alter2 cascade; +NOTICE: drop cascades to composite type alter2.ctype +NOTICE: drop cascades to type alter2.ctype +NOTICE: drop cascades to type alter2.posint +NOTICE: drop cascades to function alter2.plus1(integer) +NOTICE: drop cascades to view alter2.v1 +NOTICE: drop cascades to rule _RETURN on view alter2.v1 +NOTICE: drop cascades to sequence alter2.t1_f1_seq +NOTICE: drop cascades to table alter2.t1 column f1 +NOTICE: drop cascades to table alter2.t1 +NOTICE: drop cascades to constraint t1_f2_check on table alter2.t1 diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index aa3d17ccf7..8690f61dbe 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -998,3 +998,44 @@ create function non_strict(text) returns text as select non_strict(NULL); alter function non_strict(text) returns null on null input; select non_strict(NULL); + +-- +-- alter object set schema +-- + +create schema alter1; +create schema alter2; + +create table alter1.t1(f1 serial primary key, f2 int check (f2 > 0)); + +create view alter1.v1 as select * from alter1.t1; + +create function alter1.plus1(int) returns int as 'select $1+1' language sql; + +create domain alter1.posint integer check (value > 0); + +create type alter1.ctype as (f1 int, f2 text); + +insert into alter1.t1(f2) values(11); +insert into alter1.t1(f2) values(12); + +alter table alter1.t1 set schema alter2; +alter table alter1.v1 set schema alter2; +alter function alter1.plus1(int) set schema alter2; +alter domain alter1.posint set schema alter2; +alter type alter1.ctype set schema alter2; + +-- this should succeed because nothing is left in alter1 +drop schema alter1; + +insert into alter2.t1(f2) values(13); +insert into alter2.t1(f2) values(14); + +select * from alter2.t1; + +select * from alter2.v1; + +select alter2.plus1(41); + +-- clean up +drop schema alter2 cascade; -- 2.11.0