Add ALTER object SET SCHEMA capability for a limited but useful set of
authorTom Lane
Mon, 1 Aug 2005 04:03:59 +0000 (04:03 +0000)
committerTom Lane
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 6b12bda661548e6f59e72866a6a07627788823eb..e74b331e37653ea590ee8563a269b0e4784066a5 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1235,12 +1235,43 @@ LookupExplicitNamespace(const char *nspname)
    return namespaceId;
 }
 
+/*
+ * 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? */
@@ -1400,6 +1426,20 @@ isOtherTempNamespace(Oid namespaceId)
    return result;
 }
 
+/*
+ * 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 a383647a9380302a0f9b8292d7dfa2f84b3fbbf7..b2cc3d5c474dd91b483f4cf965b86219d649557a 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 6a7b54e88a4da0c824552b8d37e16d2b2b0181db..bf910d09a5daf2001a437d6f85a7bc986062dcfb 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -162,6 +162,105 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId)
    return count;
 }
 
+/*
+ * 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()
  *
index a19b500152da82fb2666c0f1964da076a75fae8e..996d70e16329ef34e0d6e3a54239e258a120793c 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -142,6 +142,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 7e205890c90251cedfec2f009bd6e22c7a3c5a82..38912b777d94f21b54e12c3c30d9147cff6dd326 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 c06c272cbdd1bb3e0d469043994f59d42a4a2f39..7d9a73917ddd2565792d9509b002b0f8ed0f975b 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,
@@ -5998,6 +6005,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 0979e8aeec247700ed659854ed343441ae1505f4..80d394b29339c92f515c6907f93b16717795ac3a 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 35fbee61641e0a296851c2a54a00acb826ddb54d..f3189fc14a95f42ca18ebc925005cbe70c5aa78c 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 6d727a6327831a4ca4599ee2f53c3fbd7b716555..126647a77571813d3b2e16ccb85c05f3eef24d23 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 f0475bf2bee0005175141be7b745e561ededf06c..8e79355cdd5fbb7d56d0f1dfdc05570113745e1b 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    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 546aa49c2d072fcb101282f3624b846dd32899b4..10256bbb3278432ce8e32af1a32bb18ac2ac8f3a 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 06fdf02a1083ac50e2360d795303574e9ce5f29a..ab50817cb79288dc8423e0515ca3b3201cfb31cc 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 4176f2c218d91986dbb0e58331f89632cc0a07d3..060849e8ba9a74d743c666cc1265b9975cacdef0 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 288a6adfa52280c3201cdb6b385751b4e5fa1459..d0495ca0e5002d3cd3938d125e62d4c7b031f40f 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 1a34030bffd25ae4bb9c0b102cb708dbcd8cdba9..33588bad32ac33f08b337ea35a30c92c506818e3 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 10c3438065a76282927f09ab0e5c0926a5529eed..a0f3dc67d6fba57db41268cbc7e461db0d0646d6 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 5ec8c9a57d1596dc5033729efb17b3cf83acb637..582720df3b2be43608b64603b8ac8222fcd6db33 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 a940a78f483a7a0ff1c87bb1512af8361eba2803..a070a27a29213f4df395ab4d8a95004ae76200ed 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 a488d8dce71084fa5e43f37aba44a153e17daa1d..24480f1281a16e41c6014d1850118703aee350e8 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 68262d28f7c2fa6d015bf10db18f32609a9f9ac5..fc88dec5351c1632f6d8c88602efe922aa168ada 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,15 +1477,30 @@ 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
  * ----------------------
@@ -1493,12 +1508,12 @@ typedef struct RenameStmt
 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 54205395e2bb06d22852c8ce9e73bdae52c506d3..b6e46393b4dc2fc0b1a925342992a6ad789120f3 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 aa3d17ccf7db1a975702f68cfd587a2826465866..8690f61dbedae317c49b4767ff8d27aeb2c817cf 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;