Alvaro Herrera <alvhe...@commandprompt.com> writes:
> FWIW I think you should use getObjectDescription, as in the attached
> patch.  (Note the patch is incomplete and does not compile because only
> one caller to CheckSetNamespace has been fixed).

I had to re-add the object name to the CheckSetNamespace prototype to
handle this particular check:

        /* check for duplicate name (more friendly than unique-index failure) */
        if (SearchSysCacheExists2(TYPENAMENSP,
                                                          CStringGetDatum(name),
                                                          
ObjectIdGetDatum(nspOid)))

If you know how to get some struct attribute given a char * holding its
name, in C, I would adapt the patch and work on the refactoring asked
for by Tom.

Apart from that, it was just about adapting the call sites, which is
done in the attached set_schema.5.patch. Thanks!

Also attached, please find the complete version of ALTER EXTENSION ext
SET SCHEMA name; with support for all contrib extensions. That's the
example that allows to see the API (AlterFooNamespace_oid and _internal
functions) in action: that should help devising the best refactoring.

Regards,
-- 
Dimitri Fontaine
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 2706,2711 **** getObjectDescription(const ObjectAddress *object)
--- 2706,2726 ----
  }
  
  /*
+  * getObjectDescriptionOids: as above, except the object is specified by Oids
+  */
+ char *
+ getObjectDescriptionOids(Oid classid, Oid objid)
+ {
+ 	ObjectAddress	address;
+ 
+ 	address.classId = classid;
+ 	address.objectId = objid;
+ 	address.objectSubId = 0;
+ 
+ 	return getObjectDescription(&address);
+ }
+ 
+ /*
   * subroutine for getObjectDescription: describe a relation
   */
  static void
*** a/src/backend/catalog/namespace.c
--- b/src/backend/catalog/namespace.c
***************
*** 2340,2345 **** LookupCreationNamespace(const char *nspname)
--- 2340,2383 ----
  }
  
  /*
+  * Check new namespace validity in ALTER OBJECT ... SET SCHEMA ... and
+  * ereport(ERROR, ...) in case of any problem.
+  */
+ void
+ CheckSetNamespace(Oid oldNspOid, Oid nspOid, Oid classid, Oid objid,
+ 				  const char *name)
+ {
+ 	if (oldNspOid == nspOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("%s already exists in schema \"%s\"",
+ 						getObjectDescriptionOids(classid, objid),
+ 						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 (SearchSysCacheExists2(TYPENAMENSP,
+ 							  CStringGetDatum(name),
+ 							  ObjectIdGetDatum(nspOid)))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("%s already exists in schema \"%s\"",
+ 						getObjectDescriptionOids(classid, objid),
+ 						get_namespace_name(nspOid))));
+ }
+ 
+ /*
   * QualifiedNameGetCreationNamespace
   *		Given a possibly-qualified name for an object (in List-of-Values
   *		format), determine what namespace the object should be created in.
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 182,192 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 182,208 ----
  								   stmt->newschema);
  			break;
  
+ 		case OBJECT_CONVERSION:
+ 			AlterConversionNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
  		case OBJECT_FUNCTION:
  			AlterFunctionNamespace(stmt->object, stmt->objarg, false,
  								   stmt->newschema);
  			break;
  
+ 		case OBJECT_OPERATOR:
+ 			AlterOperatorNamespace(stmt->object, stmt->objarg, stmt->newschema);
+ 			break;
+ 
+ 		case OBJECT_OPCLASS:
+ 			AlterOpClassNamespace(stmt->object, stmt->objarg, stmt->newschema);
+ 			break;
+ 
+ 		case OBJECT_OPFAMILY:
+ 			AlterOpFamilyNamespace(stmt->object, stmt->objarg, stmt->newschema);
+ 			break;
+ 
  		case OBJECT_SEQUENCE:
  		case OBJECT_TABLE:
  		case OBJECT_VIEW:
***************
*** 195,200 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 211,232 ----
  								stmt->objectType, AccessExclusiveLock);
  			break;
  
+ 		case OBJECT_TSPARSER:
+ 			AlterTSParserNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
+ 		case OBJECT_TSDICTIONARY:
+ 			AlterTSDictionaryNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
+ 		case OBJECT_TSTEMPLATE:
+ 			AlterTSTemplateNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
+ 		case OBJECT_TSCONFIGURATION:
+ 			AlterTSConfigurationNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
  		case OBJECT_TYPE:
  		case OBJECT_DOMAIN:
  			AlterTypeNamespace(stmt->object, stmt->newschema);
*** a/src/backend/commands/conversioncmds.c
--- b/src/backend/commands/conversioncmds.c
***************
*** 19,24 ****
--- 19,25 ----
  #include "catalog/indexing.h"
  #include "catalog/pg_conversion.h"
  #include "catalog/pg_conversion_fn.h"
+ #include "catalog/pg_namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/conversioncmds.h"
  #include "mb/pg_wchar.h"
***************
*** 30,37 ****
  #include "utils/rel.h"
  #include "utils/syscache.h"
  
! static void AlterConversionOwner_internal(Relation rel, Oid conversionOid,
  							  Oid newOwnerId);
  
  /*
   * CREATE CONVERSION
--- 31,40 ----
  #include "utils/rel.h"
  #include "utils/syscache.h"
  
! static void AlterConversionOwner_internal(Relation rel, Oid convOid,
  							  Oid newOwnerId);
+ static void AlterConversionNamespace_internal(Relation rel, Oid convOid,
+ 								  Oid nspOid);
  
  /*
   * CREATE CONVERSION
***************
*** 326,328 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
--- 329,414 ----
  
  	heap_freetuple(tup);
  }
+ 
+ /*
+  * Execute ALTER CONVERSION SET SCHEMA
+  */
+ void
+ AlterConversionNamespace(List *name, const char *newschema)
+ {
+ 	Oid			convOid, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(ConversionRelationId, RowExclusiveLock);
+ 
+ 	convOid = get_conversion_oid(name, false);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterConversionNamespace_internal(rel, convOid, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ /*
+  * Change conversion schema, by oid
+  */
+ void
+ AlterConversionNamespace_oid(Oid convOid, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(ConversionRelationId, RowExclusiveLock);
+ 
+ 	AlterConversionNamespace_internal(rel, convOid, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ static void
+ AlterConversionNamespace_internal(Relation rel, Oid convOid, Oid nspOid)
+ {
+ 	Oid			oldNspOid;
+ 	HeapTuple	tup;
+ 	Form_pg_conversion convForm;
+ 
+ 	Assert(RelationGetRelid(rel) == ConversionRelationId);
+ 
+ 	tup = SearchSysCacheCopy1(CONVOID, ObjectIdGetDatum(convOid));
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for conversion %u", convOid);
+ 
+ 	convForm = (Form_pg_conversion) GETSTRUCT(tup);
+ 	oldNspOid = convForm->connamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  ConversionRelationId, convOid, NameStr(convForm->conname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
+ 						   NameStr(convForm->conname));
+ 
+ 		/* owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(convForm->connamespace,
+ 										  GetUserId(),
+ 										  ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 						   get_namespace_name(convForm->connamespace));
+ 	}
+ 
+ 	convForm->connamespace = nspOid;
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(ConversionRelationId, convOid,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ }
*** a/src/backend/commands/functioncmds.c
--- b/src/backend/commands/functioncmds.c
***************
*** 1870,1882 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  					   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 */
  	if (isagg)
--- 1870,1876 ----
***************
*** 1889,1894 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
--- 1883,1903 ----
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
  					   NameListToString(name));
  
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterFunctionNamespace_oid(procOid, nspOid);
+ }
+ 
+ void
+ AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
+ {
+ 	Oid			oldNspOid;
+ 	HeapTuple	tup;
+ 	Relation	procRel;
+ 	Form_pg_proc proc;
+ 
+ 	procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
+ 
  	tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
  	if (!HeapTupleIsValid(tup))
  		elog(ERROR, "cache lookup failed for function %u", procOid);
***************
*** 1896,1910 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  
  	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))
--- 1905,1916 ----
  
  	oldNspOid = proc->pronamespace;
  
  	if (oldNspOid == nspOid)
  		ereport(ERROR,
  				(errcode(ERRCODE_DUPLICATE_FUNCTION),
  				 errmsg("function \"%s\" is already in schema \"%s\"",
! 						NameStr(proc->proname),
! 						get_namespace_name(nspOid))));
  
  	/* disallow renaming into or out of temp schemas */
  	if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
***************
*** 1927,1933 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  				(errcode(ERRCODE_DUPLICATE_FUNCTION),
  				 errmsg("function \"%s\" already exists in schema \"%s\"",
  						NameStr(proc->proname),
! 						newschema)));
  
  	/* OK, modify the pg_proc row */
  
--- 1933,1939 ----
  				(errcode(ERRCODE_DUPLICATE_FUNCTION),
  				 errmsg("function \"%s\" already exists in schema \"%s\"",
  						NameStr(proc->proname),
! 						get_namespace_name(nspOid))));
  
  	/* OK, modify the pg_proc row */
  
***************
*** 1941,1947 **** AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  	if (changeDependencyFor(ProcedureRelationId, procOid,
  							NamespaceRelationId, oldNspOid, nspOid) != 1)
  		elog(ERROR, "failed to change schema dependency for function \"%s\"",
! 			 NameListToString(name));
  
  	heap_freetuple(tup);
  
--- 1947,1953 ----
  	if (changeDependencyFor(ProcedureRelationId, procOid,
  							NamespaceRelationId, oldNspOid, nspOid) != 1)
  		elog(ERROR, "failed to change schema dependency for function \"%s\"",
! 			 NameStr(proc->proname));
  
  	heap_freetuple(tup);
  
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 81,86 **** static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
--- 81,90 ----
  						   Oid newOwnerId);
  static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple,
  							Oid newOwnerId);
+ static void AlterOpClassNamespace_internal(Relation rel, HeapTuple tup,
+ 			   				   Oid nspOid);
+ extern void AlterOpFamilyNamespace_internal(Relation rel, HeapTuple tup,
+ 								Oid nspOid);
  
  
  /*
***************
*** 1912,1917 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
--- 1916,2012 ----
  }
  
  /*
+  * ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name
+  */
+ void
+ AlterOpClassNamespace(List *name, List *argam, const char *newschema)
+ {
+ 	Oid			amOid;
+ 	char       *access_method = linitial(argam);
+ 	Relation	rel;
+ 	HeapTuple	tup, origtup;
+ 	Oid			nspOid;
+ 
+ 	Assert(list_length(argam) == 1);
+ 
+ 	amOid = get_am_oid(access_method, false);
+ 
+ 	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+ 
+ 	/* Look up the opclass. */
+ 	origtup = OpClassCacheLookup(amOid, name, false);
+ 	tup = heap_copytuple(origtup);
+ 	ReleaseSysCache(origtup);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterOpClassNamespace_internal(rel, tup, nspOid);
+ 
+ 	heap_freetuple(tup);
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid)
+ {
+ 	HeapTuple	tup;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCacheCopy1(CLAOID, ObjectIdGetDatum(opclassOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
+ 
+ 	AlterOpClassNamespace_internal(rel, tup, newNspOid);
+ 
+ 	heap_freetuple(tup);
+ 	heap_close(rel, NoLock);
+ }
+ 
+ static void
+ AlterOpClassNamespace_internal(Relation rel, HeapTuple tup, Oid nspOid)
+ {
+ 	Oid			oldNspOid;
+ 	Form_pg_opclass opcForm;
+ 
+ 	opcForm = (Form_pg_opclass) GETSTRUCT(tup);
+ 	oldNspOid = opcForm->opcnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  OperatorClassRelationId, HeapTupleGetOid(tup),
+ 					  NameStr(opcForm->opcname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
+ 						   NameStr(opcForm->opcname));
+ 
+ 		/* New owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 						   get_namespace_name(nspOid));
+ 	}
+ 
+ 	/* tup is a copy, so we can scribble directly on it */
+ 	opcForm->opcnamespace = nspOid;
+ 
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(OperatorClassRelationId, HeapTupleGetOid(tup),
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ }
+ 
+ /*
   * Change opfamily owner by name
   */
  void
***************
*** 2067,2069 **** get_am_oid(const char *amname, bool missing_ok)
--- 2162,2290 ----
  				 errmsg("access method \"%s\" does not exist", amname)));
  	return oid;
  }
+ 
+ /*
+  * ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
+  */
+ void
+ AlterOpFamilyNamespace(List *name, List *argam, const char *newschema)
+ {
+ 	Oid			amOid;
+ 	char       *access_method = linitial(argam);
+ 	Relation	rel;
+ 	HeapTuple	tup;
+ 	char	   *opfname, *schemaname;
+ 	Oid			nspOid;
+ 
+ 	Assert(list_length(argam) == 1);
+ 	amOid = get_am_oid(access_method, false);
+ 
+ 	rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+ 
+ 	/*
+ 	 * Look up the opfamily
+ 	 */
+ 	DeconstructQualifiedName(name, &schemaname, &opfname);
+ 
+ 	if (schemaname)
+ 	{
+ 		Oid			namespaceOid;
+ 
+ 		namespaceOid = LookupExplicitNamespace(schemaname);
+ 
+ 		tup = SearchSysCacheCopy3(OPFAMILYAMNAMENSP,
+ 								  ObjectIdGetDatum(amOid),
+ 								  PointerGetDatum(opfname),
+ 								  ObjectIdGetDatum(namespaceOid));
+ 		if (!HeapTupleIsValid(tup))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ 							opfname, access_method)));
+ 	}
+ 	else
+ 	{
+ 		Oid			opfOid;
+ 
+ 		opfOid = OpfamilynameGetOpfid(amOid, opfname);
+ 		if (!OidIsValid(opfOid))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+ 							opfname, access_method)));
+ 
+ 		tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfOid));
+ 		if (!HeapTupleIsValid(tup))		/* should not happen */
+ 			elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
+ 	}
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterOpFamilyNamespace_internal(rel, tup, nspOid);
+ 
+ 	heap_freetuple(tup);
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid)
+ {
+ 	HeapTuple	tup;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCacheCopy1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
+ 
+ 	AlterOpFamilyNamespace_internal(rel, tup, newNspOid);
+ 
+ 	heap_freetuple(tup);
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterOpFamilyNamespace_internal(Relation rel, HeapTuple tup, Oid nspOid)
+ {
+ 	Oid			oldNspOid;
+ 	Form_pg_opfamily opfForm;
+ 
+ 	Assert(tup->t_tableOid == OperatorFamilyRelationId);
+ 	Assert(RelationGetRelid(rel) == OperatorFamilyRelationId);
+ 
+ 	opfForm = (Form_pg_opfamily) GETSTRUCT(tup);
+ 	oldNspOid = opfForm->opfnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  OperatorFamilyRelationId, HeapTupleGetOid(tup),
+ 					  NameStr(opfForm->opfname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+ 						   NameStr(opfForm->opfname));
+ 
+ 		/* owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 						   get_namespace_name(nspOid));
+ 	}
+ 
+ 	/* tup is a copy, so we can scribble directly on it */
+ 	opfForm->opfnamespace = nspOid;
+ 
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(OperatorFamilyRelationId, HeapTupleGetOid(tup),
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ }
*** a/src/backend/commands/operatorcmds.c
--- b/src/backend/commands/operatorcmds.c
***************
*** 39,44 ****
--- 39,45 ----
  #include "catalog/indexing.h"
  #include "catalog/namespace.h"
  #include "catalog/pg_operator.h"
+ #include "catalog/pg_namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/defrem.h"
  #include "miscadmin.h"
***************
*** 52,57 ****
--- 53,59 ----
  
  
  static void AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId);
+ static void AlterOperatorNamespace_internal(Relation rel, Oid operOid, Oid nspOid);
  
  /*
   * DefineOperator
***************
*** 452,454 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
--- 454,546 ----
  
  	heap_freetuple(tup);
  }
+ 
+ /*
+  * Execute ALTER OPERATOR SET SCHEMA
+  */
+ void
+ AlterOperatorNamespace(List *names, List *argtypes, const char *newschema)
+ {
+ 	List	   *operatorName = names;
+ 	TypeName   *typeName1 = (TypeName *) linitial(argtypes);
+ 	TypeName   *typeName2 = (TypeName *) lsecond(argtypes);
+ 	Oid			operOid, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(OperatorRelationId, RowExclusiveLock);
+ 
+ 	Assert(list_length(argtypes) == 2);
+ 	operOid = LookupOperNameTypeNames(NULL, operatorName,
+ 									  typeName1, typeName2,
+ 									  false, -1);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterOperatorNamespace_internal(rel, operOid, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(OperatorRelationId, RowExclusiveLock);
+ 
+ 	AlterOperatorNamespace_internal(rel, operOid, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ static void
+ AlterOperatorNamespace_internal(Relation rel, Oid operOid, Oid nspOid)
+ {
+ 	Form_pg_operator oprForm;
+ 	Oid			oldNspOid;
+ 	HeapTuple	tup;
+ 
+ 	Assert(RelationGetRelid(rel) == OperatorRelationId);
+ 
+ 	tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(operOid));
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for operator %u", operOid);
+ 
+ 	oprForm = (Form_pg_operator) GETSTRUCT(tup);
+ 	oldNspOid = oprForm->oprnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  OperatorRelationId, operOid, NameStr(oprForm->oprname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_oper_ownercheck(operOid, GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+ 						   NameStr(oprForm->oprname));
+ 
+ 		/* owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(oprForm->oprnamespace,
+ 										  GetUserId(),
+ 										  ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 						   get_namespace_name(oprForm->oprnamespace));
+ 	}
+ 
+ 	/* tup is a copy, so we can scribble directly on it */
+ 	oprForm->oprnamespace = nspOid;
+ 
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(OperatorRelationId, operOid,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ 
+ 	heap_freetuple(tup);
+ }
*** a/src/backend/commands/tsearchcmds.c
--- b/src/backend/commands/tsearchcmds.c
***************
*** 52,57 **** static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
--- 52,65 ----
  						 HeapTuple tup, Relation relMap);
  static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
  						 HeapTuple tup, Relation relMap);
+ static void AlterTSParserNamespace_internal(Relation rel, Oid prsId,
+ 								Oid nspOid);
+ static void AlterTSDictionaryNamespace_internal(Relation rel, Oid dictId,
+ 									Oid nspOid);
+ static void AlterTSTemplateNamespace_internal(Relation rel, Oid tmplId,
+ 								  Oid nspOid);
+ static void AlterTSConfigurationNamespace_internal(Relation rel, Oid cfgId,
+ 									   Oid nspOid);
  
  
  /* --------------------- TS Parser commands ------------------------ */
***************
*** 393,398 **** RenameTSParser(List *oldname, const char *newname)
--- 401,473 ----
  	heap_freetuple(tup);
  }
  
+ /*
+  * ALTER TEXT SEARCH PARSER any_name SET SCHEMA name
+  */
+ void
+ AlterTSParserNamespace(List *name, const char *newschema)
+ {
+ 	Oid			prsId, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSParserRelationId, RowExclusiveLock);
+ 
+ 	prsId = get_ts_parser_oid(name, false);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterTSParserNamespace_internal(rel, prsId, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSParserRelationId, RowExclusiveLock);
+ 
+ 	AlterTSParserNamespace_internal(rel, prsId, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ static void
+ AlterTSParserNamespace_internal(Relation rel, Oid prsId, Oid nspOid)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oldNspOid;
+ 	Form_pg_ts_parser prs;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to rename text search parsers")));
+ 
+ 	tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId));
+ 
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for text search parser %u", prsId);
+ 
+ 	prs = (Form_pg_ts_parser) GETSTRUCT(tup);
+ 	oldNspOid = prs->prsnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  TSParserRelationId, prsId, NameStr(prs->prsname));
+ 
+ 	prs->prsnamespace = nspOid;
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(TSParserRelationId, prsId,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ 
+ 	heap_freetuple(tup);
+ }
+ 
  /* ---------------------- TS Dictionary commands -----------------------*/
  
  /*
***************
*** 620,625 **** RenameTSDictionary(List *oldname, const char *newname)
--- 695,781 ----
  }
  
  /*
+  * ALTER TEXT SEARCH PARSER any_name SET SCHEMA name
+  */
+ void
+ AlterTSDictionaryNamespace(List *name, const char *newschema)
+ {
+ 	Oid			dictId, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+ 
+ 	dictId = get_ts_dict_oid(name, false);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterTSDictionaryNamespace_internal(rel, dictId, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
+ 
+ 	AlterTSDictionaryNamespace_internal(rel, dictId, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSDictionaryNamespace_internal(Relation rel, Oid dictId, Oid nspOid)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oldNspOid;
+ 	Form_pg_ts_dict dict;
+ 
+ 	tup = SearchSysCacheCopy1(TSDICTOID, ObjectIdGetDatum(dictId));
+ 
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for text search dictionary %u",
+ 			 dictId);
+ 
+ 	dict = ((Form_pg_ts_dict) GETSTRUCT(tup));
+ 	oldNspOid = dict->dictnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  TSDictionaryRelationId, dictId,
+ 					  NameStr(dict->dictname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
+ 						   NameStr(dict->dictname));
+ 
+ 		/* owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE);
+ 		if (aclresult != ACLCHECK_OK)
+ 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 						   get_namespace_name(oldNspOid));
+ 	}
+ 
+ 	dict->dictnamespace = nspOid;
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(TSDictionaryRelationId, dictId,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ 
+ 	heap_freetuple(tup);
+ }
+ 
+ /*
   * DROP TEXT SEARCH DICTIONARY
   */
  void
***************
*** 1100,1105 **** RenameTSTemplate(List *oldname, const char *newname)
--- 1256,1330 ----
  }
  
  /*
+  * ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name
+  */
+ void
+ AlterTSTemplateNamespace(List *name, const char *newschema)
+ {
+ 	Oid			tmplId, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
+ 
+ 	tmplId = get_ts_template_oid(name, false);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterTSTemplateNamespace_internal(rel, tmplId, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
+ 
+ 	AlterTSTemplateNamespace_internal(rel, tmplId, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSTemplateNamespace_internal(Relation rel, Oid tmplId, Oid nspOid)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oldNspOid;
+ 	Form_pg_ts_template tmpl;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			   errmsg("must be superuser to rename text search templates")));
+ 
+ 	tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
+ 
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for text search template %u",
+ 			 tmplId);
+ 
+ 	tmpl = (Form_pg_ts_template) GETSTRUCT(tup);
+ 	oldNspOid = tmpl->tmplnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  TSTemplateRelationId, tmplId, NameStr(tmpl->tmplname));
+ 
+ 	tmpl->tmplnamespace = nspOid;
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(TSTemplateRelationId, tmplId,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ 
+ 	heap_freetuple(tup);
+ }
+ 
+ 
+ /*
   * DROP TEXT SEARCH TEMPLATE
   */
  void
***************
*** 1498,1503 **** RenameTSConfiguration(List *oldname, const char *newname)
--- 1723,1807 ----
  }
  
  /*
+  * ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name
+  */
+ void
+ AlterTSConfigurationNamespace(List *name, const char *newschema)
+ {
+ 	Oid			cfgId, nspOid;
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+ 
+ 	cfgId = get_ts_config_oid(name, false);
+ 
+ 	/* get schema OID */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterTSConfigurationNamespace_internal(rel, cfgId, nspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
+ {
+ 	Relation	rel;
+ 
+ 	rel = heap_open(TSConfigRelationId, RowExclusiveLock);
+ 
+ 	AlterTSConfigurationNamespace_internal(rel, cfgId, newNspOid);
+ 
+ 	heap_close(rel, NoLock);
+ }
+ 
+ void
+ AlterTSConfigurationNamespace_internal(Relation rel, Oid cfgId, Oid nspOid)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oldNspOid;
+ 	Form_pg_ts_config cfg;
+ 
+ 	tup = SearchSysCacheCopy1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
+ 
+ 	if (!HeapTupleIsValid(tup)) /* should not happen */
+ 		elog(ERROR, "cache lookup failed for text search configuration %u",
+ 			 cfgId);
+ 
+ 	cfg = (Form_pg_ts_config) GETSTRUCT(tup);
+ 	oldNspOid = cfg->cfgnamespace;
+ 
+ 	CheckSetNamespace(oldNspOid, nspOid,
+ 					  TSConfigRelationId, cfgId, NameStr(cfg->cfgname));
+ 
+ 	/* Superusers can always do it */
+ 	if (!superuser())
+ 	{
+ 		AclResult	aclresult;
+ 
+ 		/* Otherwise, must be owner of the existing object */
+ 		if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
+ 			aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
+ 						   NameStr(cfg->cfgname));
+ 
+ 		/* owner must have CREATE privilege on namespace */
+ 		aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE);
+ 		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ 					   get_namespace_name(oldNspOid));
+ 	}
+ 
+ 	cfg->cfgnamespace = nspOid;
+ 	simple_heap_update(rel, &tup->t_self, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(TSConfigRelationId, cfgId,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ 
+ 	heap_freetuple(tup);
+ }
+ 
+ /*
   * DROP TEXT SEARCH CONFIGURATION
   */
  void
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 6040,6045 **** AlterObjectSchemaStmt:
--- 6040,6053 ----
  					n->newschema = $7;
  					$$ = (Node *)n;
  				}
+ 			| ALTER CONVERSION_P any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_CONVERSION;
+ 					n->object = $3;
+ 					n->newschema = $6;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER DOMAIN_P any_name SET SCHEMA name
  				{
  					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
***************
*** 6057,6062 **** AlterObjectSchemaStmt:
--- 6065,6097 ----
  					n->newschema = $6;
  					$$ = (Node *)n;
  				}
+ 			| ALTER OPERATOR any_operator oper_argtypes SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_OPERATOR;
+ 					n->object = $3;
+ 					n->objarg = $4;
+ 					n->newschema = $7;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER OPERATOR CLASS any_name USING access_method SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_OPCLASS;
+ 					n->object = $4;
+ 					n->objarg = list_make1($6);
+ 					n->newschema = $9;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER OPERATOR FAMILY any_name USING access_method SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_OPFAMILY;
+ 					n->object = $4;
+ 					n->objarg = list_make1($6);
+ 					n->newschema = $9;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER TABLE relation_expr SET SCHEMA name
  				{
  					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
***************
*** 6065,6070 **** AlterObjectSchemaStmt:
--- 6100,6137 ----
  					n->newschema = $6;
  					$$ = (Node *)n;
  				}
+ 			| ALTER TEXT_P SEARCH PARSER any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_TSPARSER;
+ 					n->object = $5;
+ 					n->newschema = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH DICTIONARY any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_TSDICTIONARY;
+ 					n->object = $5;
+ 					n->newschema = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH TEMPLATE any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_TSTEMPLATE;
+ 					n->object = $5;
+ 					n->newschema = $8;
+ 					$$ = (Node *)n;
+ 				}
+ 			| ALTER TEXT_P SEARCH CONFIGURATION any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_TSCONFIGURATION;
+ 					n->object = $5;
+ 					n->newschema = $8;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER SEQUENCE qualified_name SET SCHEMA name
  				{
  					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 1694,1702 **** CreateCommandTag(Node *parsetree)
--- 1694,1714 ----
  				case OBJECT_AGGREGATE:
  					tag = "ALTER AGGREGATE";
  					break;
+ 				case OBJECT_CONVERSION:
+ 					tag = "ALTER CONVERSION";
+ 					break;
  				case OBJECT_DOMAIN:
  					tag = "ALTER DOMAIN";
  					break;
+ 				case OBJECT_OPERATOR:
+ 					tag = "ALTER OPERATOR";
+ 					break;
+ 				case OBJECT_OPCLASS:
+ 					tag = "ALTER OPERATOR CLASS";
+ 					break;
+ 				case OBJECT_OPFAMILY:
+ 					tag = "ALTER OPERATOR FAMILY";
+ 					break;
  				case OBJECT_FUNCTION:
  					tag = "ALTER FUNCTION";
  					break;
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 166,171 **** extern ObjectClass getObjectClass(const ObjectAddress *object);
--- 166,173 ----
  
  extern char *getObjectDescription(const ObjectAddress *object);
  
+ extern char *getObjectDescriptionOids(Oid classid, Oid objid);
+ 
  extern ObjectAddresses *new_object_addresses(void);
  
  extern void add_exact_object_address(const ObjectAddress *object,
*** a/src/include/catalog/namespace.h
--- b/src/include/catalog/namespace.h
***************
*** 94,99 **** extern Oid	LookupExplicitNamespace(const char *nspname);
--- 94,101 ----
  extern Oid	get_namespace_oid(const char *nspname, bool missing_ok);
  
  extern Oid	LookupCreationNamespace(const char *nspname);
+ extern void CheckSetNamespace(Oid oldNspOid, Oid nspOid, Oid classid,
+ 							  Oid objid, const char *name);
  extern Oid	QualifiedNameGetCreationNamespace(List *names, char **objname_p);
  extern RangeVar *makeRangeVarFromNameList(List *names);
  extern char *NameListToString(List *names);
*** a/src/include/commands/conversioncmds.h
--- b/src/include/commands/conversioncmds.h
***************
*** 22,26 **** extern void DropConversionsCommand(DropStmt *drop);
--- 22,29 ----
  extern void RenameConversion(List *name, const char *newname);
  extern void AlterConversionOwner(List *name, Oid newOwnerId);
  extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId);
+ extern void AlterConversionNamespace(List *name, const char *newschema);
+ extern void AlterConversionNamespace_oid(Oid convOid, Oid newNspOid);
+ 
  
  #endif   /* CONVERSIONCMDS_H */
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
***************
*** 66,71 **** extern void DropCast(DropCastStmt *stmt);
--- 66,72 ----
  extern void DropCastById(Oid castOid);
  extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  					   const char *newschema);
+ extern void AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
  extern void ExecuteDoStmt(DoStmt *stmt);
  extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
  
***************
*** 78,83 **** extern void AlterOperatorOwner(List *name, TypeName *typeName1,
--- 79,86 ----
  extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId);
  extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
+ extern void AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid);
+ extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema);
  
  /* commands/aggregatecmds.c */
  extern void DefineAggregate(List *name, List *args, bool oldstyle,
***************
*** 100,114 **** extern void RenameOpClass(List *name, const char *access_method, const char *new
--- 103,123 ----
  extern void RenameOpFamily(List *name, const char *access_method, const char *newname);
  extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
  extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId);
+ extern void AlterOpClassNamespace(List *name, List *argam, const char *newschema);
+ extern void AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid);
  extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
  extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId);
  extern Oid get_am_oid(const char *amname, bool missing_ok);
+ extern void AlterOpFamilyNamespace(List *name, List *argam, const char *newschema);
+ extern void AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid);
  
  /* commands/tsearchcmds.c */
  extern void DefineTSParser(List *names, List *parameters);
  extern void RenameTSParser(List *oldname, const char *newname);
  extern void RemoveTSParsers(DropStmt *drop);
  extern void RemoveTSParserById(Oid prsId);
+ extern void AlterTSParserNamespace(List *name, const char *newschema);
+ extern void AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid);
  
  extern void DefineTSDictionary(List *names, List *parameters);
  extern void RenameTSDictionary(List *oldname, const char *newname);
***************
*** 116,129 **** extern void RemoveTSDictionaries(DropStmt *drop);
--- 125,144 ----
  extern void RemoveTSDictionaryById(Oid dictId);
  extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt);
  extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId);
+ extern void AlterTSDictionaryNamespace(List *name, const char *newschema);
+ extern void AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid);
  
  extern void DefineTSTemplate(List *names, List *parameters);
  extern void RenameTSTemplate(List *oldname, const char *newname);
+ extern void AlterTSTemplateNamespace(List *name, const char *newschema);
+ extern void AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid);
  extern void RemoveTSTemplates(DropStmt *stmt);
  extern void RemoveTSTemplateById(Oid tmplId);
  
  extern void DefineTSConfiguration(List *names, List *parameters);
  extern void RenameTSConfiguration(List *oldname, const char *newname);
+ extern void AlterTSConfigurationNamespace(List *name, const char *newschema);
+ extern void AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid);
  extern void RemoveTSConfigurations(DropStmt *stmt);
  extern void RemoveTSConfigurationById(Oid cfgId);
  extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
*** a/src/backend/catalog/pg_depend.c
--- b/src/backend/catalog/pg_depend.c
***************
*** 20,25 ****
--- 20,27 ----
  #include "catalog/indexing.h"
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_depend.h"
+ #include "catalog/pg_extension.h"
+ #include "catalog/pg_namespace.h"
  #include "miscadmin.h"
  #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
***************
*** 643,645 **** get_index_constraint(Oid indexId)
--- 645,699 ----
  
  	return constraintId;
  }
+ 
+ /*
+  * get_extension_namespace
+  *		Given the OID of an extension, return the OID of the schema it
+  *		depends on, or InvalidOid when not found
+  */
+ Oid
+ get_extension_namespace(Oid extensionId)
+ {
+ 	Oid			nspId = InvalidOid;
+ 	Relation	depRel;
+ 	ScanKeyData key[3];
+ 	SysScanDesc scan;
+ 	HeapTuple	tup;
+ 
+ 	/* Search the dependency table for the index */
+ 	depRel = heap_open(DependRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&key[0],
+ 				Anum_pg_depend_classid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(ExtensionRelationId));
+ 	ScanKeyInit(&key[1],
+ 				Anum_pg_depend_objid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(extensionId));
+ 	ScanKeyInit(&key[2],
+ 				Anum_pg_depend_objsubid,
+ 				BTEqualStrategyNumber, F_INT4EQ,
+ 				Int32GetDatum(0));
+ 
+ 	scan = systable_beginscan(depRel, DependDependerIndexId, true,
+ 							  SnapshotNow, 3, key);
+ 
+ 	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ 	{
+ 		Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+ 
+ 		if (deprec->refclassid == NamespaceRelationId &&
+ 			deprec->refobjsubid == 0 &&
+ 			deprec->deptype == DEPENDENCY_NORMAL)
+ 		{
+ 			nspId = deprec->refobjid;
+ 			break;
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 	heap_close(depRel, AccessShareLock);
+ 
+ 	return nspId;
+ }
*** a/src/backend/catalog/pg_namespace.c
--- b/src/backend/catalog/pg_namespace.c
***************
*** 76,80 **** NamespaceCreate(const char *nspName, Oid ownerId)
--- 76,92 ----
  	/* Record dependency on owner */
  	recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId);
  
+ 	/* Record dependency on extension, if we're in a CREATE EXTENSION */
+ 	if (create_extension)
+ 	{
+ 		ObjectAddress myself;
+ 
+ 		myself.classId = NamespaceRelationId;
+ 		myself.objectId = nspoid;
+ 		myself.objectSubId = 0;
+ 
+ 		recordDependencyOn(&myself, &extension, DEPENDENCY_INTERNAL);
+ 	}
+ 
  	return nspoid;
  }
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "commands/conversioncmds.h"
  #include "commands/dbcommands.h"
  #include "commands/defrem.h"
+ #include "commands/extension.h"
  #include "commands/proclang.h"
  #include "commands/schemacmds.h"
  #include "commands/tablecmds.h"
***************
*** 186,191 **** ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
--- 187,196 ----
  			AlterConversionNamespace(stmt->object, stmt->newschema);
  			break;
  
+ 		case OBJECT_EXTENSION:
+ 			AlterExtensionNamespace(stmt->object, stmt->newschema);
+ 			break;
+ 
  		case OBJECT_FUNCTION:
  			AlterFunctionNamespace(stmt->object, stmt->objarg, false,
  								   stmt->newschema);
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 38,50 ****
--- 38,55 ----
  #include "access/xact.h"
  #include "catalog/dependency.h"
  #include "catalog/indexing.h"
+ #include "catalog/namespace.h"
  #include "catalog/pg_depend.h"
  #include "catalog/pg_extension.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_type.h"
  #include "commands/comment.h"
+ #include "commands/conversioncmds.h"
+ #include "commands/defrem.h"
  #include "commands/extension.h"
  #include "commands/portalcmds.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
  #include "funcapi.h"
  #include "nodes/parsenodes.h"
  #include "mb/pg_wchar.h"
***************
*** 54,59 ****
--- 59,65 ----
  #include "utils/cfparser.h"
  #include "utils/fmgroids.h"
  #include "utils/guc.h"
+ #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
***************
*** 1276,1278 **** pg_extension_objects(PG_FUNCTION_ARGS)
--- 1282,1403 ----
  	releaseDependentObjects(fctx->targetObjects);
  	SRF_RETURN_DONE(funcctx);
  }
+ 
+ /*
+  * Execute ALTER EXTENSION SET SCHEMA
+  */
+ void
+ AlterExtensionNamespace(List *name, const char *newschema)
+ {
+ 	Oid			     extensionOid, nspOid, oldNspOid;
+ 	ObjectAddress   *object;
+ 	ObjectAddresses *targetObjects;
+ 	int i;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 (errmsg("must be superuser to ALTER EXTENSION"))));
+ 
+ 	Assert(list_length(name) == 1);
+ 	extensionOid = get_extension_oid(strVal(linitial(name)), false);
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	object = (ObjectAddress *)palloc(sizeof(ObjectAddress));
+ 	object->classId = ExtensionRelationId;
+ 	object->objectId = extensionOid;
+ 	object->objectSubId = 0;
+ 
+ 	oldNspOid = get_extension_namespace(extensionOid);
+ 
+ 	targetObjects = listDependentObjects(object);
+ 
+ 	for (i = 0; i < targetObjects->numrefs; i++)
+ 	{
+ 		ObjectAddress *thisobj = targetObjects->refs + i;
+ 
+ 		elog(DEBUG1, "SET SCHEMA on %u: %s",
+ 			 thisobj->objectId, getObjectDescription(thisobj));
+ 
+ 		/*
+ 		 * Do the SET SCHEMA now.
+ 		 *
+ 		 * We only consider objects that have a namespace and that can exist
+ 		 * without depending on another object (like a table) which will
+ 		 * have its dependencies follow the SET SCHEMA operation.
+ 		 */
+ 		switch (getObjectClass(thisobj))
+ 		{
+ 			case OCLASS_CLASS:
+ 			{
+ 				Relation classRel;
+ 				Relation rel = relation_open(thisobj->objectId, RowExclusiveLock);
+ 
+ 				switch (rel->rd_rel->relkind)
+ 				{
+ 					case RELKIND_COMPOSITE_TYPE:
+ 						/*
+ 						 * just skip the pg_class entry, we have a pg_type
+ 						 * entry too
+ 						 */
+ 						break;
+ 
+ 					default:
+ 						classRel = heap_open(RelationRelationId, RowExclusiveLock);
+ 						AlterRelationNamespaceInternal(classRel,
+ 													   RelationGetRelid(rel),
+ 													   RelationGetNamespace(rel),
+ 													   nspOid,
+ 													   true);
+ 						heap_close(classRel, RowExclusiveLock);
+ 						break;
+ 				}
+ 				relation_close(rel, RowExclusiveLock);
+ 				break;
+ 			}
+ 			case OCLASS_PROC:
+ 				AlterFunctionNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_TYPE:
+ 			{
+ 				/* don't allow direct alteration of array types, skip */
+ 				Oid	elemOid = get_element_type(thisobj->objectId);
+ 				if (OidIsValid(elemOid)
+ 					&& get_array_type(elemOid) == thisobj->objectId)
+ 					break;
+ 
+ 				AlterTypeNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			}
+ 			case OCLASS_CONVERSION:
+ 				AlterConversionNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_OPERATOR:
+ 				AlterOperatorNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_OPCLASS:
+ 				AlterOpClassNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_OPFAMILY:
+ 				AlterOpFamilyNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_TSPARSER:
+ 				AlterTSParserNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_TSDICT:
+ 				AlterTSDictionaryNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_TSTEMPLATE:
+ 				AlterTSTemplateNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			case OCLASS_TSCONFIG:
+ 				AlterTSConfigurationNamespace_oid(thisobj->objectId, nspOid);
+ 				break;
+ 			default:
+ 				break;
+ 		}
+ 	}
+ 	/* update dependencies to point to the new schema */
+ 	changeDependencyFor(ExtensionRelationId, extensionOid,
+ 						NamespaceRelationId, oldNspOid, nspOid);
+ }
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 2765,2784 **** AlterTypeNamespace(List *names, const char *newschema)
  	TypeName   *typename;
  	Oid			typeOid;
  	Oid			nspOid;
- 	Oid			elemOid;
  
  	/* Make a TypeName so we can use standard type lookup machinery */
  	typename = makeTypeNameFromNameList(names);
  	typeOid = typenameTypeId(NULL, 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);
- 
  	/* don't allow direct alteration of array types */
  	elemOid = get_element_type(typeOid);
  	if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
--- 2765,2791 ----
  	TypeName   *typename;
  	Oid			typeOid;
  	Oid			nspOid;
  
  	/* Make a TypeName so we can use standard type lookup machinery */
  	typename = makeTypeNameFromNameList(names);
  	typeOid = typenameTypeId(NULL, typename);
  
+ 	/* get schema OID and check its permissions */
+ 	nspOid = LookupCreationNamespace(newschema);
+ 
+ 	AlterTypeNamespace_oid(typeOid, nspOid);
+ }
+ 
+ void
+ AlterTypeNamespace_oid(Oid typeOid, Oid nspOid)
+ {
+ 	Oid			elemOid;
+ 
  	/* check permissions on type */
  	if (!pg_type_ownercheck(typeOid, GetUserId()))
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
  					   format_type_be(typeOid));
  
  	/* don't allow direct alteration of array types */
  	elemOid = get_element_type(typeOid);
  	if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
***************
*** 2790,2796 **** AlterTypeNamespace(List *names, const char *newschema)
  						 format_type_be(elemOid))));
  
  	/* and do the work */
! 	AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
  }
  
  /*
--- 2797,2803 ----
  						 format_type_be(elemOid))));
  
  	/* and do the work */
! 	AlterTypeNamespaceInternal(typeOid, nspOid, false, false);
  }
  
  /*
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 6138,6143 **** AlterObjectSchemaStmt:
--- 6138,6151 ----
  					n->newschema = $6;
  					$$ = (Node *)n;
  				}
+ 			| ALTER EXTENSION any_name SET SCHEMA name
+ 				{
+ 					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+ 					n->objectType = OBJECT_EXTENSION;
+ 					n->object = $3;
+ 					n->newschema = $6;
+ 					$$ = (Node *)n;
+ 				}
  			| ALTER FUNCTION function_with_argtypes SET SCHEMA name
  				{
  					AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 1719,1724 **** CreateCommandTag(Node *parsetree)
--- 1719,1727 ----
  				case OBJECT_DOMAIN:
  					tag = "ALTER DOMAIN";
  					break;
+ 				case OBJECT_EXTENSION:
+ 					tag = "ALTER EXTENSION";
+ 					break;
  				case OBJECT_OPERATOR:
  					tag = "ALTER OPERATOR";
  					break;
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 240,245 **** extern Oid	get_constraint_index(Oid constraintId);
--- 240,248 ----
  
  extern Oid	get_index_constraint(Oid indexId);
  
+ extern Oid get_extension_namespace(Oid extensionId);
+ 
+ 
  /* in pg_shdepend.c */
  
  extern void recordSharedDependencyOn(ObjectAddress *depender,
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 64,69 **** extern void DropExtension(DropExtensionStmt *stmt);
--- 64,70 ----
  extern Oid get_extension_oid(const char *extname, bool missing_ok);
  extern char *get_extension_name(Oid ext_oid);
  extern void RemoveExtensionById(Oid extId);
+ extern void AlterExtensionNamespace(List *name, const char *newschema);
  
  
  #endif   /* EXTENSION_H */
*** a/src/include/commands/typecmds.h
--- b/src/include/commands/typecmds.h
***************
*** 41,46 **** extern void AlterTypeOwner(List *names, Oid newOwnerId);
--- 41,47 ----
  extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
  					   bool hasDependEntry);
  extern void AlterTypeNamespace(List *names, const char *newschema);
+ extern void AlterTypeNamespace_oid(Oid typeOid, Oid nspOid);
  extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
  						   bool isImplicitArray,
  						   bool errorOnTableType);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to