OSDN Git Service

Add ALTER object SET SCHEMA capability for a limited but useful set of
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Aug 2005 04:03:59 +0000 (04:03 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Aug 2005 04:03:59 +0000 (04:03 +0000)
object kinds (tables, functions, types).  Documentation is not here yet.
Original code by Bernd Helmle, extensive rework by Bruce Momjian and
Tom Lane.

22 files changed:
src/backend/catalog/namespace.c
src/backend/catalog/pg_constraint.c
src/backend/catalog/pg_depend.c
src/backend/commands/alter.c
src/backend/commands/functioncmds.c
src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/catalog/dependency.h
src/include/catalog/namespace.h
src/include/catalog/pg_constraint.h
src/include/commands/alter.h
src/include/commands/defrem.h
src/include/commands/tablecmds.h
src/include/commands/typecmds.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 6b12bda..e74b331 100644 (file)
@@ -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.
  *
index a383647..b2cc3d5 100644 (file)
@@ -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);
+}
index 6a7b54e..bf910d0 100644 (file)
@@ -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.
index a19b500..996d70e 100644 (file)
@@ -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.
  */
index 7e20589..38912b7 100644 (file)
@@ -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);
+}
index c06c272..7d9a739 100644 (file)
@@ -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 }
  *
index 0979e8a..80d394b 100644 (file)
@@ -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);
+}
index 35fbee6..f3189fc 100644 (file)
@@ -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;
index 6d727a6..126647a 100644 (file)
@@ -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;
index f0475bf..8e79355 100644 (file)
@@ -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 <node>   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
  *
  *****************************************************************************/
 
index 546aa49..10256bb 100644 (file)
@@ -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;
 
index 06fdf02..ab50817 100644 (file)
@@ -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,
index 4176f2c..060849e 100644 (file)
@@ -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);
index 288a6ad..d0495ca 100644 (file)
@@ -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 */
index 1a34030..33588ba 100644 (file)
@@ -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 */
index 10c3438..a0f3dc6 100644 (file)
@@ -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);
index 5ec8c9a..582720d 100644 (file)
@@ -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,
index a940a78..a070a27 100644 (file)
@@ -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 */
index a488d8d..24480f1 100644 (file)
@@ -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,
index 68262d2..fc88dec 100644 (file)
@@ -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;
 
 
index 5420539..b6e4639 100644 (file)
@@ -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
index aa3d17c..8690f61 100644 (file)
@@ -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;