Hi, In the road to the extension patch, we already found some parts that have to be separated into their own patch. Here's another one. It occurred to me while implementing the pg_extension_objects() SRF that if we can list all objects that belong to an extension, certainly we also are able to move them to another schema.
As soon as we have that ability, we are able to provide for relocatable extensions with the following command: ALTER EXTENSION ext SET SCHEMA name; ALTER EXTENSION ext SET SCHEMA foo TO bar; I think that would end the open debate about search_path vs extension, because each user would be able to relocate his local extensions easily, wherever the main script has installed them (often enough, public). Please find attached a work-in-progress patch (it's missing documentation) implementing support for setting a new schema to SQL objects of types conversion, operator, operator class, operator family, text search parser, dictionary, template and configuration. If there's will to apply such a patch, I'll finish it by writing the necessary documentation for the 8 new SQL commands. Note: CreateCommandTag() already has support for tags for ALTER TEXT SEARCH <OBJECT> … SET SCHEMA …, but the implementation I've not found, in the grammar nor in tsearchcmds.c. It's in the patch. As usual, you can also get to the development version by using git: http://git.postgresql.org/gitweb?p=postgresql-extension.git;a=shortlog;h=refs/heads/set_schema git --no-pager diff master..|diffstat backend/catalog/pg_namespace.c | 38 ++++ backend/commands/alter.c | 32 ++++ backend/commands/conversioncmds.c | 84 ++++++++++ backend/commands/opclasscmds.c | 215 +++++++++++++++++++++++++++ backend/commands/operatorcmds.c | 90 +++++++++++ backend/commands/tsearchcmds.c | 295 ++++++++++++++++++++++++++++++++++++++ backend/parser/gram.y | 67 ++++++++ backend/tcop/utility.c | 12 + include/catalog/pg_namespace.h | 2 include/commands/conversioncmds.h | 5 include/commands/defrem.h | 23 ++ 11 files changed, 863 insertions(+) Regards, -- Dimitri Fontaine http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
*** a/src/backend/catalog/pg_namespace.c --- b/src/backend/catalog/pg_namespace.c *************** *** 17,24 **** --- 17,26 ---- #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" + #include "catalog/namespace.h" #include "catalog/pg_namespace.h" #include "utils/builtins.h" + #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" *************** *** 77,79 **** NamespaceCreate(const char *nspName, Oid ownerId) --- 79,117 ---- return nspoid; } + + /* + * Check new namespace validity in ALTER OBJECT ... SET SCHEMA ... and + * ereport(ERROR, ...) in case of any problem. + */ + void + CheckSetNamespace(Oid oldNspOid, Oid nspOid, + const char *name, const char *objtype) + { + if (oldNspOid == nspOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("%s \"%s\" already exists in schema \"%s\"", + objtype, name, 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 \"%s\" already exists in schema \"%s\"", + objtype, name, get_namespace_name(nspOid)))); + } *** 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" *************** *** 326,328 **** AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) --- 327,412 ---- heap_freetuple(tup); } + + /* + * Execute ALTER CONVERSION SET SCHEMA + */ + void + AlterConversionNamespace(List *name, const char *newschema) + { + Oid conversionOid, nspOid; + Relation rel; + + rel = heap_open(ConversionRelationId, RowExclusiveLock); + + conversionOid = get_conversion_oid(name, false); + + /* get schema OID */ + nspOid = LookupCreationNamespace(newschema); + + AlterConversionNamespace_internal(rel, conversionOid, nspOid); + + heap_close(rel, NoLock); + } + + /* + * Change conversion owner, by oid + */ + void + AlterConversionNamespace_oid(Oid conversionOid, Oid newNspOid) + { + Relation rel; + + rel = heap_open(ConversionRelationId, RowExclusiveLock); + + AlterConversionNamespace_internal(rel, conversionOid, newNspOid); + + heap_close(rel, NoLock); + } + + void + AlterConversionNamespace_internal(Relation rel, Oid conversionOid, Oid nspOid) + { + Oid oldNspOid; + HeapTuple tup; + Form_pg_conversion convForm; + + Assert(RelationGetRelid(rel) == ConversionRelationId); + + tup = SearchSysCacheCopy1(CONVOID, ObjectIdGetDatum(conversionOid)); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for conversion %u", conversionOid); + + convForm = (Form_pg_conversion) GETSTRUCT(tup); + oldNspOid = convForm->connamespace; + + CheckSetNamespace(oldNspOid, nspOid, + NameStr(convForm->conname), "conversion"); + + /* 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, conversionOid, + NamespaceRelationId, oldNspOid, nspOid); + } *** a/src/backend/commands/opclasscmds.c --- b/src/backend/commands/opclasscmds.c *************** *** 1912,1917 **** AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) --- 1912,2007 ---- } /* + * 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); + + AlterOpClassOwner_internal(rel, tup, newNspOid); + + heap_freetuple(tup); + heap_close(rel, NoLock); + } + + 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, + NameStr(opcForm->opcname), "operator class"); + + /* 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) --- 2157,2284 ---- 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); + + AlterOpFamilyOwner_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, + NameStr(opfForm->opfname), "operator family"); + + /* 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" *************** *** 452,454 **** AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) --- 453,544 ---- heap_freetuple(tup); } + + /* + * Execute ALTER OPERATOR SET SCHEMA + */ + void + AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid) + { + Relation rel; + + rel = heap_open(OperatorRelationId, RowExclusiveLock); + + AlterOperatorOwner_internal(rel, operOid, newNspOid); + + heap_close(rel, NoLock); + } + + 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_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, NameStr(oprForm->oprname), "operator"); + + /* 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 *************** *** 393,398 **** RenameTSParser(List *oldname, const char *newname) --- 393,465 ---- 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); + } + + 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, + NameStr(prs->prsname), "text search parser"); + + 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) --- 687,772 ---- } /* + * 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, + NameStr(dict->dictname), "text search dictionary"); + + /* 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) --- 1247,1321 ---- } /* + * 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, + NameStr(tmpl->tmplname), "text search template"); + + 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) --- 1714,1798 ---- } /* + * 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, + NameStr(cfg->cfgname), "text search configuration"); + + /* 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/pg_namespace.h --- b/src/include/catalog/pg_namespace.h *************** *** 78,82 **** DESCR("standard public schema"); --- 78,84 ---- * prototypes for functions in pg_namespace.c */ extern Oid NamespaceCreate(const char *nspName, Oid ownerId); + extern void CheckSetNamespace(Oid oldNspOid, Oid nspOid, + const char *name, const char *objtype); #endif /* PG_NAMESPACE_H */ *** a/src/include/commands/conversioncmds.h --- b/src/include/commands/conversioncmds.h *************** *** 16,26 **** --- 16,31 ---- #define CONVERSIONCMDS_H #include "nodes/parsenodes.h" + #include "utils/relcache.h" extern void CreateConversionCommand(CreateConversionStmt *parsetree); extern void DropConversionsCommand(DropStmt *drop); 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 conversionOid, Oid newNspOid); + extern void AlterConversionNamespace_internal(Relation rel, Oid conversionOid, Oid nspOid); + #endif /* CONVERSIONCMDS_H */ *** a/src/include/commands/defrem.h --- b/src/include/commands/defrem.h *************** *** 14,20 **** --- 14,22 ---- #ifndef DEFREM_H #define DEFREM_H + #include "access/htup.h" #include "nodes/parsenodes.h" + #include "utils/relcache.h" /* commands/indexcmds.c */ *************** *** 78,83 **** extern void AlterOperatorOwner(List *name, TypeName *typeName1, --- 80,88 ---- 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); + extern void AlterOperatorNamespace_internal(Relation rel, Oid operoid, Oid nspOid); /* 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 --- 105,128 ---- 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 AlterOpClassNamespace_internal(Relation rel, HeapTuple tup, Oid nspOid); 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); + extern void AlterOpFamilyNamespace_internal(Relation rel, HeapTuple tup, Oid nspOid); /* 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 AlterTSParserNamespace_internal(Relation rel, Oid prsId, Oid nspOid); extern void DefineTSDictionary(List *names, List *parameters); extern void RenameTSDictionary(List *oldname, const char *newname); *************** *** 116,129 **** extern void RemoveTSDictionaries(DropStmt *drop); --- 130,152 ---- 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 AlterTSDictionaryNamespace_internal(Relation rel, Oid dictId, Oid nspOid); 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 AlterTSTemplateNamespace_internal(Relation rel, Oid tmplId, Oid nspOid); 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 AlterTSConfigurationNamespace_internal(Relation rel, Oid cfgId, Oid nspOid); extern void RemoveTSConfigurations(DropStmt *stmt); extern void RemoveTSConfigurationById(Oid cfgId); extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers