src/backend/catalog/objectaddress.c | 577 ++++++++++++++++++++++++---- src/backend/commands/comment.c | 3 +- src/backend/commands/dropcmds.c | 3 +- src/backend/commands/extension.c | 3 +- src/backend/commands/seclabel.c | 3 +- src/include/catalog/objectaddress.h | 11 +- src/test/regress/expected/alter_rename.out | 282 ++++++++++++++ src/test/regress/expected/privileges.out | 2 +- src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/alter_rename.sql | 240 ++++++++++++ 11 files changed, 1031 insertions(+), 96 deletions(-) diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index ec4c987..bcf5259 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -53,6 +53,7 @@ #include "foreign/foreign.h" #include "libpq/be-fsstubs.h" #include "miscadmin.h" +#include "mb/pg_wchar.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" @@ -76,8 +77,11 @@ typedef struct { Oid class_oid; /* oid of catalog */ Oid oid_index_oid; /* oid of index on system oid column */ + Oid name_index_oid; /* oid of index on name/schema column */ int oid_catcache_id; /* id of catcache on system oid column */ + int name_catcache_id; /* id of catcache on name/schema column */ AttrNumber attnum_namespace; /* attnum of namespace field */ + AttrNumber attnum_name; /* attnum of name field */ } ObjectPropertyType; static ObjectPropertyType ObjectProperty[] = @@ -85,152 +89,227 @@ static ObjectPropertyType ObjectProperty[] = { CastRelationId, CastOidIndexId, + InvalidOid, -1, - InvalidAttrNumber + -1, + InvalidAttrNumber, + InvalidAttrNumber, }, { CollationRelationId, CollationOidIndexId, + InvalidOid, /* No suitable index on pg_collation */ COLLOID, - Anum_pg_collation_collnamespace + -1, /* COLLNAMEENCNSP also takes oid of encoding */ + Anum_pg_collation_collnamespace, + Anum_pg_collation_collname, }, { ConstraintRelationId, ConstraintOidIndexId, + ConstraintNameNspIndexId, CONSTROID, - Anum_pg_constraint_connamespace + -1, + Anum_pg_constraint_connamespace, + Anum_pg_constraint_conname, }, { ConversionRelationId, ConversionOidIndexId, + ConversionNameNspIndexId, CONVOID, - Anum_pg_conversion_connamespace + -1, + Anum_pg_conversion_connamespace, + Anum_pg_conversion_conname, }, { DatabaseRelationId, DatabaseOidIndexId, + DatabaseNameIndexId, DATABASEOID, - InvalidAttrNumber + -1, + InvalidAttrNumber, + Anum_pg_database_datname, }, { ExtensionRelationId, ExtensionOidIndexId, + InvalidOid, + -1, -1, - InvalidAttrNumber /* extension doesn't belong to extnamespace */ + InvalidAttrNumber, /* extension doesn't belong to extnamespace */ + InvalidAttrNumber, /* extension name is unavailable to change */ }, { ForeignDataWrapperRelationId, ForeignDataWrapperOidIndexId, + ForeignDataWrapperNameIndexId, FOREIGNDATAWRAPPEROID, - InvalidAttrNumber + FOREIGNDATAWRAPPERNAME, + InvalidAttrNumber, + Anum_pg_foreign_data_wrapper_fdwname, }, { ForeignServerRelationId, ForeignServerOidIndexId, + ForeignServerNameIndexId, FOREIGNSERVEROID, - InvalidAttrNumber + FOREIGNSERVERNAME, + InvalidAttrNumber, + Anum_pg_foreign_server_srvname, }, { ProcedureRelationId, ProcedureOidIndexId, + InvalidOid, /* No suitable index on pg_proc */ PROCOID, - Anum_pg_proc_pronamespace + -1, /* PROCNAMEARGSNSP takes argument types too */ + Anum_pg_proc_pronamespace, + Anum_pg_proc_proname, }, { LanguageRelationId, LanguageOidIndexId, + LanguageNameIndexId, LANGOID, + LANGNAME, InvalidAttrNumber, + Anum_pg_language_lanname, }, { LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId, + InvalidOid, + -1, -1, - InvalidAttrNumber + InvalidAttrNumber, + InvalidAttrNumber, }, { OperatorClassRelationId, OpclassOidIndexId, + InvalidOid, /* No suitable index on pg_opclass */ CLAOID, + -1, /* CLANAME takes oid of access method too */ Anum_pg_opclass_opcnamespace, + Anum_pg_opclass_opcname, }, { OperatorRelationId, OperatorOidIndexId, + OperatorNameNspIndexId, OPEROID, - Anum_pg_operator_oprnamespace + -1, /* OPERNAMENSP takes oid of left/right types too */ + Anum_pg_operator_oprnamespace, + Anum_pg_operator_oprname, }, { OperatorFamilyRelationId, OpfamilyOidIndexId, + InvalidOid, /* No suitable index on op_opfamily */ OPFAMILYOID, - Anum_pg_opfamily_opfnamespace + -1, /* OPFAMILYAMNAMENSP takes oid of access method too */ + Anum_pg_opfamily_opfnamespace, + Anum_pg_opfamily_opfname, }, { AuthIdRelationId, AuthIdOidIndexId, + AuthIdRolnameIndexId, AUTHOID, - InvalidAttrNumber + AUTHNAME, + InvalidAttrNumber, + AuthIdRelationId, }, { RewriteRelationId, RewriteOidIndexId, + InvalidOid, /* No suitable index on pg_rewrite */ -1, - InvalidAttrNumber + -1, /* RULERELNAME takes oid of relation too */ + InvalidAttrNumber, + Anum_pg_rewrite_rulename, }, { NamespaceRelationId, NamespaceOidIndexId, + NamespaceNameIndexId, NAMESPACEOID, - InvalidAttrNumber + NAMESPACENAME, + InvalidAttrNumber, + Anum_pg_namespace_nspname, }, { RelationRelationId, ClassOidIndexId, + ClassNameNspIndexId, RELOID, - Anum_pg_class_relnamespace + RELNAMENSP, + Anum_pg_class_relnamespace, + Anum_pg_class_relname, }, { TableSpaceRelationId, TablespaceOidIndexId, + TablespaceNameIndexId, TABLESPACEOID, - InvalidAttrNumber + -1, + InvalidAttrNumber, + Anum_pg_tablespace_spcname, }, { TriggerRelationId, TriggerOidIndexId, + InvalidOid, /* No suitable index on pg_trigegr */ + -1, -1, - InvalidAttrNumber + InvalidAttrNumber, + Anum_pg_trigger_tgname, }, { TSConfigRelationId, TSConfigOidIndexId, + TSConfigNameNspIndexId, TSCONFIGOID, - Anum_pg_ts_config_cfgnamespace + TSCONFIGNAMENSP, + Anum_pg_ts_config_cfgnamespace, + Anum_pg_ts_config_cfgname, }, { TSDictionaryRelationId, TSDictionaryOidIndexId, + TSDictionaryNameNspIndexId, TSDICTOID, - Anum_pg_ts_dict_dictnamespace + TSDICTNAMENSP, + Anum_pg_ts_dict_dictnamespace, + Anum_pg_ts_dict_dictname, }, { TSParserRelationId, TSParserOidIndexId, + TSParserNameNspIndexId, TSPARSEROID, - Anum_pg_ts_parser_prsnamespace + TSPARSERNAMENSP, + Anum_pg_ts_parser_prsnamespace, + Anum_pg_ts_parser_prsname, }, { TSTemplateRelationId, TSTemplateOidIndexId, + TSTemplateNameNspIndexId, TSTEMPLATEOID, + TSTEMPLATENAMENSP, Anum_pg_ts_template_tmplnamespace, + Anum_pg_ts_template_tmplname, }, { TypeRelationId, TypeOidIndexId, + TypeNameNspIndexId, TYPEOID, - Anum_pg_type_typnamespace + TYPENAMENSP, + Anum_pg_type_typnamespace, + Anum_pg_type_typname, } }; @@ -914,93 +993,129 @@ object_exists(ObjectAddress address) * Check ownership of an object previously identified by get_object_address. */ void -check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, - List *objname, List *objargs, Relation relation) +check_object_ownership(Oid roleid, ObjectAddress address, Relation relation) { - switch (objtype) + switch (address.classId) { - case OBJECT_INDEX: - case OBJECT_SEQUENCE: - case OBJECT_TABLE: - case OBJECT_VIEW: - case OBJECT_FOREIGN_TABLE: - case OBJECT_COLUMN: - case OBJECT_RULE: - case OBJECT_TRIGGER: - case OBJECT_CONSTRAINT: + case RelationRelationId: + case RewriteRelationId: + case TriggerRelationId: + case ConstraintRelationId: if (!pg_class_ownercheck(RelationGetRelid(relation), roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); break; - case OBJECT_DATABASE: + case DatabaseRelationId: if (!pg_database_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - NameListToString(objname)); + get_database_name(address.objectId)); break; - case OBJECT_TYPE: - case OBJECT_DOMAIN: - case OBJECT_ATTRIBUTE: + case TypeRelationId: if (!pg_type_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(address.objectId)); break; - case OBJECT_AGGREGATE: - case OBJECT_FUNCTION: + case ProcedureRelationId: if (!pg_proc_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(objname)); + format_procedure(address.objectId)); break; - case OBJECT_OPERATOR: + case OperatorRelationId: if (!pg_oper_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, - NameListToString(objname)); + format_operator(address.objectId)); break; - case OBJECT_SCHEMA: + case NamespaceRelationId: if (!pg_namespace_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, - NameListToString(objname)); + get_namespace_name(address.objectId)); break; - case OBJECT_COLLATION: + case CollationRelationId: if (!pg_collation_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, - NameListToString(objname)); + get_collation_name(address.objectId)); break; - case OBJECT_CONVERSION: + case ConversionRelationId: if (!pg_conversion_ownercheck(address.objectId, roleid)) + { + char *convname = NULL; + HeapTuple tup = SearchSysCache1(CONVOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_conversion conversion + = (Form_pg_conversion) GETSTRUCT(tup); + convname = pstrdup(NameStr(conversion->conname)); + ReleaseSysCache(tup); + } aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, - NameListToString(objname)); + convname); + } break; - case OBJECT_EXTENSION: + case ExtensionRelationId: if (!pg_extension_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EXTENSION, - NameListToString(objname)); + get_extension_name(address.objectId)); break; - case OBJECT_FDW: + case ForeignDataWrapperRelationId: if (!pg_foreign_data_wrapper_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FDW, - NameListToString(objname)); + GetForeignDataWrapper(address.objectId)->fdwname); break; - case OBJECT_FOREIGN_SERVER: + case ForeignServerRelationId: if (!pg_foreign_server_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - NameListToString(objname)); + GetForeignServer(address.objectId)->servername); break; - case OBJECT_LANGUAGE: + case LanguageRelationId: if (!pg_language_ownercheck(address.objectId, roleid)) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, - NameListToString(objname)); + { + char *lanname = NULL; + HeapTuple tup = SearchSysCache1(LANGOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_language lang + = (Form_pg_language) GETSTRUCT(tup); + lanname = pstrdup(NameStr(lang->lanname)); + ReleaseSysCache(tup); + } + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, lanname); + } break; - case OBJECT_OPCLASS: + case OperatorClassRelationId: if (!pg_opclass_ownercheck(address.objectId, roleid)) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, - NameListToString(objname)); + { + char *opcname = NULL; + HeapTuple tup = SearchSysCache1(CLAOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_opclass opclass + = (Form_pg_opclass) GETSTRUCT(tup); + opcname = pstrdup(NameStr(opclass->opcname)); + ReleaseSysCache(tup); + } + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, opcname); + } break; - case OBJECT_OPFAMILY: + case OperatorFamilyRelationId: if (!pg_opfamily_ownercheck(address.objectId, roleid)) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, - NameListToString(objname)); + { + char *opfname = NULL; + HeapTuple tup = SearchSysCache1(OPFAMILYOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_opfamily opfamily + = (Form_pg_opfamily) GETSTRUCT(tup); + opfname = pstrdup(NameStr(opfamily->opfname)); + ReleaseSysCache(tup); + } + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, opfname); + } break; - case OBJECT_LARGEOBJECT: + case LargeObjectRelationId: if (!lo_compat_privileges && !pg_largeobject_ownercheck(address.objectId, roleid)) ereport(ERROR, @@ -1008,14 +1123,36 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, errmsg("must be owner of large object %u", address.objectId))); break; - case OBJECT_CAST: + case CastRelationId: { - /* We can only check permissions on the source/target types */ - TypeName *sourcetype = (TypeName *) linitial(objname); - TypeName *targettype = (TypeName *) linitial(objargs); - Oid sourcetypeid = typenameTypeId(NULL, sourcetype); - Oid targettypeid = typenameTypeId(NULL, targettype); + Relation catalog; + ScanKeyData skey[1]; + SysScanDesc scan; + HeapTuple tup; + Oid sourcetypeid; + Oid targettypeid; + + catalog = heap_open(CastRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(address.objectId)); + + scan = systable_beginscan(catalog, CastOidIndexId, true, + SnapshotNow, 1, skey); + tup = systable_getnext(scan); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "could not find tuple for cast %u", + address.objectId); + + sourcetypeid = ((Form_pg_cast) GETSTRUCT(tup))->castsource; + targettypeid = ((Form_pg_cast) GETSTRUCT(tup))->casttarget; + + systable_endscan(scan); + heap_close(catalog, AccessShareLock); + /* We can only check permissions on the source/target types */ if (!pg_type_ownercheck(sourcetypeid, roleid) && !pg_type_ownercheck(targettypeid, roleid)) ereport(ERROR, @@ -1025,23 +1162,46 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, format_type_be(targettypeid)))); } break; - case OBJECT_TABLESPACE: + case TableSpaceRelationId: if (!pg_tablespace_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, - NameListToString(objname)); + get_tablespace_name(address.objectId)); break; - case OBJECT_TSDICTIONARY: + case TSDictionaryRelationId: if (!pg_ts_dict_ownercheck(address.objectId, roleid)) + { + char *dictname = NULL; + HeapTuple tup = SearchSysCache1(TSDICTOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_ts_dict tsdict + = (Form_pg_ts_dict) GETSTRUCT(tup); + dictname = pstrdup(NameStr(tsdict->dictname)); + ReleaseSysCache(tup); + } aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(objname)); + dictname); + } break; - case OBJECT_TSCONFIGURATION: + case TSConfigRelationId: if (!pg_ts_config_ownercheck(address.objectId, roleid)) + { + char *confname = NULL; + HeapTuple tup = SearchSysCache1(TSCONFIGOID, + ObjectIdGetDatum(address.objectId)); + if (HeapTupleIsValid(tup)) + { + Form_pg_ts_config tsconf + = (Form_pg_ts_config) GETSTRUCT(tup); + confname = pstrdup(NameStr(tsconf->cfgname)); + ReleaseSysCache(tup); + } aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(objname)); + confname); + } break; - case OBJECT_ROLE: - + case AuthIdRelationId: /* * We treat roles as being "owned" by those with CREATEROLE priv, * except that superusers are only owned by superusers. @@ -1061,8 +1221,8 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, errmsg("must have CREATEROLE privilege"))); } break; - case OBJECT_TSPARSER: - case OBJECT_TSTEMPLATE: + case TSParserRelationId: + case TSTemplateRelationId: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) ereport(ERROR, @@ -1070,9 +1230,258 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, errmsg("must be superuser"))); break; default: - elog(ERROR, "unrecognized object type: %d", - (int) objtype); + elog(ERROR, "unrecognized class-id of the object: %u", + address.classId); + } +} + +/* + * check_exists_namespace + * + * check name or/and namespace conflict of the supplied object, if and + * when its name or namespace would be switched to. + */ +bool +object_exists_namespace(ObjectAddress address, + Oid new_namespace, const char *new_name) +{ + ObjectPropertyType *property; + HeapTuple tuple; + Datum datum; + bool isnull; + bool found = false; + + /* + * Right now, we have no code path to invoke this routine toward + * pg_attribute, pg_trigger or pg_rewrite; that takes special + * treatments. + */ + Assert(address.objectSubId == 0); + Assert(address.classId != TriggerRelationId); + Assert(address.classId != RewriteRelationId); + + /* + * Weird backward compatibility hack: ObjectAddress notation uses + * LargeObjectRelationId for large objects, but since PostgreSQL + * 9.0, the relevant catalog is actually LargeObjectMetadataRelationId. + */ + if (address.classId == LargeObjectRelationId) + address.classId = LargeObjectMetadataRelationId; + + property = get_object_property_data(address.classId); + + /* This routine must be called towards object classes with its name. */ + Assert(property->attnum_name != InvalidAttrNumber); + + /* We have catcache by oid for named object types */ + Assert(property->oid_catcache_id != -1); + + tuple = SearchSysCache1(property->oid_catcache_id, + ObjectIdGetDatum(address.objectId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for class %u object %u subobj %d", + address.classId, address.objectId, address.objectSubId); + + /* + * if namespace is preserved, the current one shall be used as + * a key to look up system catalog. + */ + if (!OidIsValid(new_namespace) && + property->attnum_namespace != InvalidAttrNumber) + { + + datum = SysCacheGetAttr(property->oid_catcache_id, + tuple, + property->attnum_namespace, + &isnull); + Assert(!isnull); + new_namespace = DatumGetObjectId(datum); + } + + /* + * if name is preserved, the current one shall be used as + * a key to look up system catalog. + */ + if (!new_name) + { + datum = SysCacheGetAttr(property->oid_catcache_id, + tuple, + property->attnum_name, + &isnull); + Assert(!isnull); + new_name = pstrdup(NameStr(*(DatumGetName(datum)))); + } + Assert(new_name != NULL); + + /* COLLNAMEENCNSP cache takes encoding type, so special treatment too */ + if (address.classId == CollationRelationId) + { + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(new_name), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(new_namespace))) + found = true; + /* mustn't match an any-encoding entry, either */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(new_name), + Int32GetDatum(-1), + ObjectIdGetDatum(new_namespace))) + found = true; } + /* PROCNAMEARGSNSP cache takes argument types too */ + else if (address.classId == ProcedureRelationId) + { + Form_pg_proc procForm = (Form_pg_proc) GETSTRUCT(tuple); + + if (SearchSysCacheExists3(PROCNAMEARGSNSP, + CStringGetDatum(new_name), + PointerGetDatum(&procForm->proargtypes), + ObjectIdGetDatum(new_namespace))) + found = true; + } + /* OPERNAMENSP cache takes oid of left/right types too */ + else if (address.classId == OperatorRelationId) + { + Oid oprleft = ((Form_pg_operator) GETSTRUCT(tuple))->oprleft; + Oid oprright = ((Form_pg_operator) GETSTRUCT(tuple))->oprright; + + if (SearchSysCacheExists4(OPERNAMENSP, + CStringGetDatum(new_name), + ObjectIdGetDatum(oprleft), + ObjectIdGetDatum(oprright), + ObjectIdGetDatum(new_namespace))) + found = true; + } + /* CLAAMNAMENSP cache takes oid of access method too */ + else if (address.classId == OperatorClassRelationId) + { + Oid amoid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcmethod; + + if (SearchSysCacheExists3(CLAAMNAMENSP, + ObjectIdGetDatum(amoid), + CStringGetDatum(new_name), + ObjectIdGetDatum(new_namespace))) + found = true; + } + /* OPFAMILYAMNAMENSP cache takes oid of access method too */ + else if (address.classId == OperatorFamilyRelationId) + { + Oid amoid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfmethod; + + if (SearchSysCacheExists3(OPFAMILYAMNAMENSP, + ObjectIdGetDatum(amoid), + CStringGetDatum(new_name), + ObjectIdGetDatum(new_namespace))) + found = true; + } + else if (property->name_catcache_id != -1) + { + found = SearchSysCacheExists2(property->name_catcache_id, + CStringGetDatum(new_name), + ObjectIdGetDatum(new_namespace)); + } + else + { + Relation rel; + ScanKeyData skey[2]; + SysScanDesc scan; + int nkeys; + + /* No syscache, so examine the table directly. */ + Assert(OidIsValid(property->name_index_oid)); + + ScanKeyInit(&skey[0], + property->attnum_name, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(new_name)); + if (property->attnum_namespace == InvalidAttrNumber) + nkeys = 1; + else + { + ScanKeyInit(&skey[1], + property->attnum_namespace, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(new_namespace)); + nkeys = 2; + } + + rel = heap_open(address.classId, AccessShareLock); + scan = systable_beginscan(rel, property->name_index_oid, true, + SnapshotNow, nkeys, skey); + found = HeapTupleIsValid(systable_getnext(scan)); + systable_endscan(scan); + heap_close(rel, AccessShareLock); + } + ReleaseSysCache(tuple); + + return found; +} + +/* + * get_object_tuple + * + * It returns a copy of heaptuple data within system catalog. + */ +HeapTuple +get_object_tuple(ObjectAddress address) +{ + int cache = -1; + Oid indexoid = InvalidOid; + Relation rel; + ScanKeyData skey[1]; + SysScanDesc sd; + HeapTuple tuple = NULL; + ObjectPropertyType *property; + + /* Sub-objects require special treatment. */ + if (address.objectSubId != 0) + { + /* Currently, attributes are the only sub-objects. */ + Assert(address.classId == RelationRelationId); + tuple = SearchSysCacheCopy2(ATTNUM, + ObjectIdGetDatum(address.objectId), + Int16GetDatum(address.objectSubId)); + if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped) + { + heap_freetuple(tuple); + return NULL; + } + return tuple; + } + + /* + * Weird backward compatibility hack: ObjectAddress notation uses + * LargeObjectRelationId for large objects, but since PostgreSQL + * 9.0, the relevant catalog is actually LargeObjectMetadataRelationId. + */ + if (address.classId == LargeObjectRelationId) + address.classId = LargeObjectMetadataRelationId; + + /* + * For object types that have a relevant syscache, we use it; for + * everything else, we'll have to do an index-scan. + */ + property = get_object_property_data(address.classId); + cache = property->oid_catcache_id; + indexoid = property->oid_index_oid; + + /* Found a syscache? */ + if (cache != -1) + return SearchSysCacheCopy1(cache, ObjectIdGetDatum(address.objectId)); + + /* No syscache, so examine the table directly. */ + Assert(OidIsValid(indexoid)); + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(address.objectId)); + rel = heap_open(address.classId, AccessShareLock); + sd = systable_beginscan(rel, indexoid, true, SnapshotNow, 1, skey); + tuple = heap_copytuple(systable_getnext(sd)); + systable_endscan(sd); + heap_close(rel, AccessShareLock); + + return tuple; } /* diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 200b6a2..34705e5 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -73,8 +73,7 @@ CommentObject(CommentStmt *stmt) &relation, ShareUpdateExclusiveLock, false); /* Require ownership of the target object. */ - check_object_ownership(GetUserId(), stmt->objtype, address, - stmt->objname, stmt->objargs, relation); + check_object_ownership(GetUserId(), address, relation); /* Perform other integrity checks as needed. */ switch (stmt->objtype) diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index ef70168..2cd8232 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -98,8 +98,7 @@ RemoveObjects(DropStmt *stmt) namespaceId = get_object_namespace(&address); if (!OidIsValid(namespaceId) || !pg_namespace_ownercheck(namespaceId, GetUserId())) - check_object_ownership(GetUserId(), stmt->removeType, address, - objname, objargs, relation); + check_object_ownership(GetUserId(), address, relation); } /* Release any relcache reference count, but keep lock until commit. */ diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index c334ca9..5a2cc09 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -2652,8 +2652,7 @@ ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) &relation, ShareUpdateExclusiveLock, false); /* Permission check: must own target object, too */ - check_object_ownership(GetUserId(), stmt->objtype, object, - stmt->objname, stmt->objargs, relation); + check_object_ownership(GetUserId(), object, relation); /* * Check existing extension membership. diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index e0fd7ac..118f467 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -90,8 +90,7 @@ ExecSecLabelStmt(SecLabelStmt *stmt) &relation, ShareUpdateExclusiveLock, false); /* Require ownership of the target object. */ - check_object_ownership(GetUserId(), stmt->objtype, address, - stmt->objname, stmt->objargs, relation); + check_object_ownership(GetUserId(), address, relation); /* Perform other integrity checks as needed. */ switch (stmt->objtype) diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index eddccb8..ff7b957 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -13,6 +13,7 @@ #ifndef OBJECTADDRESS_H #define OBJECTADDRESS_H +#include "access/htup.h" #include "nodes/parsenodes.h" #include "storage/lock.h" #include "utils/relcache.h" @@ -32,8 +33,14 @@ extern ObjectAddress get_object_address(ObjectType objtype, List *objname, LOCKMODE lockmode, bool missing_ok); extern void check_object_ownership(Oid roleid, - ObjectType objtype, ObjectAddress address, - List *objname, List *objargs, Relation relation); + ObjectAddress address, + Relation relation); + +extern bool object_exists_namespace(ObjectAddress address, + Oid new_namespace, + const char *new_name); + +extern HeapTuple get_object_tuple(ObjectAddress address); extern Oid get_object_namespace(const ObjectAddress *address); diff --git a/src/test/regress/expected/alter_rename.out b/src/test/regress/expected/alter_rename.out new file mode 100644 index 0000000..6688d50 --- /dev/null +++ b/src/test/regress/expected/alter_rename.out @@ -0,0 +1,282 @@ +-- +-- ALTER xxx RENAME TO/SET SCHEMA statement +-- +CREATE SCHEMA testschema_1; +CREATE SCHEMA testschema_2; +SET search_path = testschema_1, public; +-- +-- ROLE +-- +CREATE ROLE testrole_1; +CREATE ROLE testrole_2; +ALTER ROLE testrole_1 RENAME TO testrole_2; -- fail +ERROR: role "testrole_2" already exists +ALTER ROLE testrole_1 RENAME TO testrole_3; -- OK +DROP ROLE testrole_2, testrole_3; +-- +-- LANGUAGE +-- +CREATE LANGUAGE testlang_1 HANDLER plpgsql_call_handler; +CREATE LANGUAGE testlang_2 HANDLER plpgsql_call_handler; +ALTER LANGUAGE testlang_1 RENAME TO testlang_2; -- fail +ERROR: language "testlang_2" already exists +ALTER LANGUAGE testlang_1 RENAME TO testlang_3; -- OK +DROP LANGUAGE testlang_2; +DROP LANGUAGE testlang_3; +-- +-- TABLE +-- +CREATE TABLE testtable_a(a int, b int); +CREATE TABLE testtable_b(x int, y int); +ALTER TABLE testtable_a RENAME TO testtable_b; -- fail +ERROR: relation "testtable_b" already exists +ALTER TABLE testtable_b SET SCHEMA testschema_2; -- OK +ALTER TABLE testtable_a RENAME TO testtable_b; -- OK +ALTER TABLE testtable_b SET SCHEMA testschema_2; -- fail +ERROR: relation "testtable_b" already exists in schema "testschema_2" +-- +-- VIEW +-- +CREATE VIEW testview_a AS SELECT * FROM pg_class WHERE relname like 'pg_%'; +CREATE VIEW testview_b AS SELECT * FROM pg_class WHERE relname like 'pg_%'; +ALTER VIEW testview_a RENAME TO testview_b; -- fail +ERROR: relation "testview_b" already exists +ALTER VIEW testview_b SET SCHEMA testschema_2; -- OK +ALTER VIEW testview_a RENAME TO testview_b; -- OK +ALTER VIEW testview_b SET SCHEMA testschema_2; -- fail +ERROR: relation "testview_b" already exists in schema "testschema_2" +ALTER VIEW testtable_b SET SCHEMA public; -- fail +ERROR: "testtable_b" is not a view +ALTER VIEW testtable_b RENAME TO testtable_c; -- fail +ERROR: "testtable_b" is not a view +-- +-- SEQUENCE +-- +CREATE SEQUENCE testseq_a; +CREATE SEQUENCE testseq_b; +ALTER SEQUENCE testseq_a RENAME TO testseq_b; -- fail +ERROR: relation "testseq_b" already exists +ALTER SEQUENCE testseq_b SET SCHEMA testschema_2; -- OK +ALTER SEQUENCE testseq_a RENAME TO testseq_b; -- OK +ALTER SEQUENCE testseq_b SET SCHEMA testschema_2; -- fail +ERROR: relation "testseq_b" already exists in schema "testschema_2" +ALTER SEQUENCE testview_b SET SCHEMA public; -- fail +ERROR: "testview_b" is not a sequence +ALTER SEQUENCE testview_b RENAME TO testview_c; -- fail +ERROR: "testview_b" is not a sequence +-- +-- COMPOSITE TYPE +-- +CREATE TYPE testcomptype_a AS (a int, b int); +CREATE TYPE testcomptype_b AS (a int, b int); +ALTER TYPE testcomptype_a RENAME TO testcomptype_b; -- fail +ERROR: relation "testcomptype_b" already exists +ALTER TYPE testcomptype_b SET SCHEMA testschema_2; -- OK +ALTER TYPE testcomptype_a RENAME TO testcomptype_b; -- OK +ALTER TYPE testcomptype_b SET SCHEMA testschema_2; -- fail +ERROR: type "testcomptype_b" already exists in schema "testschema_2" +ALTER TYPE testseq_b SET SCHEMA public; -- fail +ERROR: testseq_b is a table's row type +HINT: Use ALTER TABLE instead. +ALTER TYPE testseq_b RENAME TO testseq_c; -- fail +ERROR: testseq_b is a table's row type +HINT: Use ALTER TABLE instead. +-- +-- INDEX +-- +CREATE INDEX testindex_a ON testtable_b (a); +CREATE INDEX testindex_b ON testtable_b (b); +ALTER INDEX testindex_a RENAME TO testindex_b; -- fail +ERROR: relation "testindex_b" already exists +ALTER INDEX testindex_a RENAME TO testindex_c; -- OK +-- +-- COLUMN +-- +ALTER TABLE testtable_b RENAME a TO b; -- fail +ERROR: column "b" of relation "testtable_b" already exists +ALTER TABLE testtable_b RENAME a TO x; -- OK +CREATE TABLE testtable_c (z int) INHERITS (testtable_b); +ALTER TABLE ONLY testtable_b RENAME b TO y; -- fail +ERROR: inherited column "b" must be renamed in child tables too +ALTER TABLE testtable_b RENAME b TO y; -- OK +ALTER TABLE testtable_c RENAME z TO zz; -- OK +ALTER TABLE testtable_c RENAME x TO xx; -- fail +ERROR: cannot rename inherited column "x" +-- +-- TRIGGER +-- +CREATE TRIGGER testtrigger_a + BEFORE UPDATE ON testtable_b + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +CREATE TRIGGER testtrigger_b + BEFORE UPDATE ON testtable_b + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +ALTER TRIGGER testtrigger_a ON testtable_b RENAME TO testtrigger_b; -- fail +ERROR: trigger "testtrigger_b" for relation "testtable_b" already exists +ALTER TRIGGER testtrigger_a ON testtable_b RENAME TO testtrigger_c; -- OK +-- +-- FUNCTION, AGGREGATE +-- +CREATE FUNCTION testfunc_a(int) + RETURNS int LANGUAGE sql + AS 'SELECT $1 + $1'; +CREATE FUNCTION testfunc_b(int) + RETURNS int LANGUAGE sql + AS 'SELECT $1 + $1'; +ALTER FUNCTION testfunc_a(int) RENAME TO testfunc_b; -- fail +ERROR: function testfunc_b(integer) already exists in schema "testschema_1" +ALTER FUNCTION testfunc_b(int) SET SCHEMA testschema_2; -- OK +ALTER FUNCTION testfunc_a(int) RENAME TO testfunc_b; -- OK +ALTER FUNCTION testfunc_b(int) SET SCHEMA testschema_2; -- fail +ERROR: function "testfunc_b" already exists in schema "testschema_2" +CREATE AGGREGATE testagg_a( + sfunc1 = int4pl, basetype = int4, stype1 = int4, + initcond1 = '0' +); +CREATE AGGREGATE testagg_b( + sfunc1 = int4pl, basetype = int4, stype1 = int4, + initcond1 = '0' +); +ALTER AGGREGATE testagg_a(int) RENAME TO testagg_b; -- fail +ERROR: function testagg_b(integer) already exists in schema "testschema_1" +ALTER AGGREGATE testagg_b(int) SET SCHEMA testschema_2; -- OK +ALTER AGGREGATE testagg_a(int) RENAME TO testagg_b; -- OK +ALTER AGGREGATE testagg_b(int) SET SCHEMA testschema_2; -- fail +ERROR: function "testagg_b" already exists in schema "testschema_2" +-- +-- TYPE +-- +CREATE TYPE testtype_a; +CREATE TYPE testtype_b; +CREATE FUNCTION testtype_a_in (cstring) RETURNS testtype_a LANGUAGE 'internal' AS 'int4in'; +NOTICE: return type testtype_a is only a shell +CREATE FUNCTION testtype_b_in (cstring) RETURNS testtype_b LANGUAGE 'internal' AS 'int4in'; +NOTICE: return type testtype_b is only a shell +CREATE FUNCTION testtype_a_out (testtype_a) RETURNS cstring LANGUAGE 'internal' AS 'int4out'; +NOTICE: argument type testtype_a is only a shell +CREATE FUNCTION testtype_b_out (testtype_b) RETURNS cstring LANGUAGE 'internal' AS 'int4out'; +NOTICE: argument type testtype_b is only a shell +CREATE TYPE testtype_a (internallength = 4, input = testtype_a_in, output = testtype_a_out); +CREATE TYPE testtype_b (internallength = 4, input = testtype_b_in, output = testtype_b_out); +ALTER TYPE testtype_a RENAME TO testtype_b; -- fail +ERROR: type "testtype_b" already exists +ALTER TYPE testtype_b SET SCHEMA testschema_2; -- OK +ALTER TYPE testtype_a RENAME TO testtype_b; -- OK +ALTER TYPE testtype_b SET SCHEMA testschema_2; -- fail +ERROR: type "testtype_b" already exists in schema "testschema_2" +-- +-- OPERATOR, OPERATOR FAMILY and OPERATOR CLASS +-- +CREATE OPERATOR @#@ + (leftarg = int8, rightarg = int8, procedure = int8xor); +ALTER OPERATOR @#@(int8,int8) SET SCHEMA testschema_2; -- OK +CREATE OPERATOR @#@ + (leftarg = int8, rightarg = int8, procedure = int8xor); +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR @#@(int8,int8) SET SCHEMA testschema_2; -- fail +CREATE OPERATOR FAMILY testopfamily_a USING btree; +CREATE OPERATOR FAMILY testopfamily_b USING btree; +ALTER OPERATOR FAMILY testopfamily_a USING btree RENAME TO testopfamily_b; -- fail +ERROR: operator family "testopfamily_b" for access method "btree" already exists in schema "testschema_1" +ALTER OPERATOR FAMILY testopfamily_b USING btree SET SCHEMA testschema_2; -- OK +ALTER OPERATOR FAMILY testopfamily_a USING btree RENAME TO testopfamily_b; -- OK +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR FAMILY testopfamily_b USING btree SET SCHEMA testschema_2; -- fail +CREATE OPERATOR CLASS testopclass_a + FOR TYPE box USING btree FAMILY testopfamily_b AS OPERATOR 1 =; +CREATE OPERATOR CLASS testopclass_b + FOR TYPE circle USING btree FAMILY testopfamily_b AS OPERATOR 1 =; +ALTER OPERATOR CLASS testopclass_a USING btree RENAME TO testopclass_b; -- fail +ERROR: operator class "testopclass_b" for access method "btree" already exists in schema "testschema_1" +ALTER OPERATOR CLASS testopclass_b USING btree SET SCHEMA testschema_2; -- OK +ALTER OPERATOR CLASS testopclass_a USING btree RENAME TO testopclass_b; -- OK +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR CLASS testopclass_b USING btree SET SCHEMA testschema_2; -- fail +-- +-- TEXT SEARCH +-- +CREATE TEXT SEARCH DICTIONARY testtsdict_a ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +CREATE TEXT SEARCH DICTIONARY testtsdict_b ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +ALTER TEXT SEARCH DICTIONARY testtsdict_a RENAME TO testtsdict_b; -- fail +ERROR: text search dictionary "testtsdict_b" already exists +ALTER TEXT SEARCH DICTIONARY testtsdict_b SET SCHEMA testschema_2; -- OK +ALTER TEXT SEARCH DICTIONARY testtsdict_a RENAME TO testtsdict_b; -- OK +ALTER TEXT SEARCH DICTIONARY testtsdict_b SET SCHEMA testschema_2; -- fail +ERROR: text search dictionary testtsdict_b already exists in schema "testschema_2" +CREATE TEXT SEARCH CONFIGURATION testtsconf_a (COPY=english); +CREATE TEXT SEARCH CONFIGURATION testtsconf_b (COPY=english); +ALTER TEXT SEARCH CONFIGURATION testtsconf_a RENAME TO testtsconf_b; -- fail +ERROR: text search configuration "testtsconf_b" already exists +ALTER TEXT SEARCH CONFIGURATION testtsconf_b SET SCHEMA testschema_2; -- OK +ALTER TEXT SEARCH CONFIGURATION testtsconf_a RENAME TO testtsconf_b; -- OK +ALTER TEXT SEARCH CONFIGURATION testtsconf_b SET SCHEMA testschema_2; -- fail +ERROR: text search configuration testtsconf_b already exists in schema "testschema_2" +-- +-- COLLATION +-- +CREATE COLLATION testcollation_a FROM "C"; +CREATE COLLATION testcollation_b FROM "C"; +ALTER COLLATION testcollation_a RENAME TO testcollation_b; -- fail +ERROR: collation "testcollation_b" for encoding "UTF8" already exists in schema "testschema_1" +ALTER COLLATION testcollation_b SET SCHEMA testschema_2; -- OK +ALTER COLLATION testcollation_a RENAME TO testcollation_b; -- OK +ALTER COLLATION testcollation_b SET SCHEMA testschema_2; -- fail +ERROR: collation "testcollation_b" for encoding "UTF8" already exists in schema "testschema_2" +-- +-- CONVERSION +-- +CREATE CONVERSION testconv_a FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; +CREATE CONVERSION testconv_b FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; +ALTER CONVERSION testconv_a RENAME TO testconv_b; -- fail +ERROR: conversion "testconv_b" already exists in schema "testschema_1" +ALTER CONVERSION testconv_b SET SCHEMA testschema_2; -- OK +ALTER CONVERSION testconv_a RENAME TO testconv_b; -- OK +ALTER CONVERSION testconv_b SET SCHEMA testschema_2; -- fail +ERROR: conversion testconv_b already exists in schema "testschema_2" +-- +-- SCHEMA +-- +ALTER SCHEMA testschema_1 RENAME TO testschema_2; -- fail +ERROR: schema "testschema_2" already exists +ALTER SCHEMA testschema_1 RENAME TO testschema_3; -- OK +DROP SCHEMA testschema_2, testschema_3 CASCADE; +NOTICE: drop cascades to 31 other objects +DETAIL: drop cascades to table testschema_3.testtable_b +drop cascades to view testschema_3.testview_b +drop cascades to sequence testschema_3.testseq_b +drop cascades to type testschema_3.testcomptype_b +drop cascades to table testschema_3.testtable_c +drop cascades to function testschema_3.testfunc_b(integer) +drop cascades to function testschema_3.testagg_b(integer) +drop cascades to type testschema_3.testtype_b +drop cascades to function testschema_3.testtype_a_in(cstring) +drop cascades to function testschema_3.testtype_a_out(testschema_3.testtype_b) +drop cascades to operator testschema_3.@#@(bigint,bigint) +drop cascades to operator family testschema_3.testopfamily_b for access method btree +drop cascades to text search dictionary testtsdict_b +drop cascades to text search configuration testtsconf_b +drop cascades to collation testcollation_b +drop cascades to conversion testconv_b +drop cascades to table testschema_2.testtable_b +drop cascades to view testschema_2.testview_b +drop cascades to sequence testschema_2.testseq_b +drop cascades to type testschema_2.testcomptype_b +drop cascades to function testschema_2.testfunc_b(integer) +drop cascades to function testschema_2.testagg_b(integer) +drop cascades to type testschema_2.testtype_b +drop cascades to function testschema_3.testtype_b_in(cstring) +drop cascades to function testschema_3.testtype_b_out(testschema_2.testtype_b) +drop cascades to operator testschema_2.@#@(bigint,bigint) +drop cascades to operator family testschema_2.testopfamily_b for access method btree +drop cascades to text search dictionary testtsdict_b +drop cascades to text search configuration testtsconf_b +drop cascades to collation testcollation_b +drop cascades to conversion testconv_b diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index 5cda230..8aec0a4 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -504,7 +504,7 @@ SELECT testfunc1(5); -- ok (1 row) DROP FUNCTION testfunc1(int); -- fail -ERROR: must be owner of function testfunc1 +ERROR: must be owner of function testfunc1(integer) \c - DROP FUNCTION testfunc1(int); -- ok -- restore to sanity diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index b47b08b..7f590fc 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -99,7 +99,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo # NB: temp.sql does a reconnect which transiently uses 2 connections, # so keep this parallel group to at most 19 tests # ---------- -test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml +test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_rename alter_table sequence polymorphism rowtypes returning largeobject with xml # run stats by itself because its delay may be insufficient under heavy load test: stats diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 57806b5..78f63e3 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -119,6 +119,7 @@ test: prepare test: without_oid test: conversion test: truncate +test: alter_rename test: alter_table test: sequence test: polymorphism diff --git a/src/test/regress/sql/alter_rename.sql b/src/test/regress/sql/alter_rename.sql new file mode 100644 index 0000000..9b2bdf1 --- /dev/null +++ b/src/test/regress/sql/alter_rename.sql @@ -0,0 +1,240 @@ +-- +-- ALTER xxx RENAME TO/SET SCHEMA statement +-- + +CREATE SCHEMA testschema_1; +CREATE SCHEMA testschema_2; + +SET search_path = testschema_1, public; + +-- +-- ROLE +-- +CREATE ROLE testrole_1; +CREATE ROLE testrole_2; + +ALTER ROLE testrole_1 RENAME TO testrole_2; -- fail +ALTER ROLE testrole_1 RENAME TO testrole_3; -- OK + +DROP ROLE testrole_2, testrole_3; + +-- +-- LANGUAGE +-- +CREATE LANGUAGE testlang_1 HANDLER plpgsql_call_handler; +CREATE LANGUAGE testlang_2 HANDLER plpgsql_call_handler; + +ALTER LANGUAGE testlang_1 RENAME TO testlang_2; -- fail +ALTER LANGUAGE testlang_1 RENAME TO testlang_3; -- OK + +DROP LANGUAGE testlang_2; +DROP LANGUAGE testlang_3; + +-- +-- TABLE +-- +CREATE TABLE testtable_a(a int, b int); +CREATE TABLE testtable_b(x int, y int); +ALTER TABLE testtable_a RENAME TO testtable_b; -- fail +ALTER TABLE testtable_b SET SCHEMA testschema_2; -- OK +ALTER TABLE testtable_a RENAME TO testtable_b; -- OK +ALTER TABLE testtable_b SET SCHEMA testschema_2; -- fail + +-- +-- VIEW +-- +CREATE VIEW testview_a AS SELECT * FROM pg_class WHERE relname like 'pg_%'; +CREATE VIEW testview_b AS SELECT * FROM pg_class WHERE relname like 'pg_%'; +ALTER VIEW testview_a RENAME TO testview_b; -- fail +ALTER VIEW testview_b SET SCHEMA testschema_2; -- OK +ALTER VIEW testview_a RENAME TO testview_b; -- OK +ALTER VIEW testview_b SET SCHEMA testschema_2; -- fail +ALTER VIEW testtable_b SET SCHEMA public; -- fail +ALTER VIEW testtable_b RENAME TO testtable_c; -- fail + +-- +-- SEQUENCE +-- +CREATE SEQUENCE testseq_a; +CREATE SEQUENCE testseq_b; +ALTER SEQUENCE testseq_a RENAME TO testseq_b; -- fail +ALTER SEQUENCE testseq_b SET SCHEMA testschema_2; -- OK +ALTER SEQUENCE testseq_a RENAME TO testseq_b; -- OK +ALTER SEQUENCE testseq_b SET SCHEMA testschema_2; -- fail +ALTER SEQUENCE testview_b SET SCHEMA public; -- fail +ALTER SEQUENCE testview_b RENAME TO testview_c; -- fail + +-- +-- COMPOSITE TYPE +-- +CREATE TYPE testcomptype_a AS (a int, b int); +CREATE TYPE testcomptype_b AS (a int, b int); +ALTER TYPE testcomptype_a RENAME TO testcomptype_b; -- fail +ALTER TYPE testcomptype_b SET SCHEMA testschema_2; -- OK +ALTER TYPE testcomptype_a RENAME TO testcomptype_b; -- OK +ALTER TYPE testcomptype_b SET SCHEMA testschema_2; -- fail +ALTER TYPE testseq_b SET SCHEMA public; -- fail +ALTER TYPE testseq_b RENAME TO testseq_c; -- fail + +-- +-- INDEX +-- +CREATE INDEX testindex_a ON testtable_b (a); +CREATE INDEX testindex_b ON testtable_b (b); +ALTER INDEX testindex_a RENAME TO testindex_b; -- fail +ALTER INDEX testindex_a RENAME TO testindex_c; -- OK + +-- +-- COLUMN +-- +ALTER TABLE testtable_b RENAME a TO b; -- fail +ALTER TABLE testtable_b RENAME a TO x; -- OK +CREATE TABLE testtable_c (z int) INHERITS (testtable_b); +ALTER TABLE ONLY testtable_b RENAME b TO y; -- fail +ALTER TABLE testtable_b RENAME b TO y; -- OK +ALTER TABLE testtable_c RENAME z TO zz; -- OK +ALTER TABLE testtable_c RENAME x TO xx; -- fail + +-- +-- TRIGGER +-- +CREATE TRIGGER testtrigger_a + BEFORE UPDATE ON testtable_b + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +CREATE TRIGGER testtrigger_b + BEFORE UPDATE ON testtable_b + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +ALTER TRIGGER testtrigger_a ON testtable_b RENAME TO testtrigger_b; -- fail +ALTER TRIGGER testtrigger_a ON testtable_b RENAME TO testtrigger_c; -- OK + + +-- +-- FUNCTION, AGGREGATE +-- +CREATE FUNCTION testfunc_a(int) + RETURNS int LANGUAGE sql + AS 'SELECT $1 + $1'; +CREATE FUNCTION testfunc_b(int) + RETURNS int LANGUAGE sql + AS 'SELECT $1 + $1'; +ALTER FUNCTION testfunc_a(int) RENAME TO testfunc_b; -- fail +ALTER FUNCTION testfunc_b(int) SET SCHEMA testschema_2; -- OK +ALTER FUNCTION testfunc_a(int) RENAME TO testfunc_b; -- OK +ALTER FUNCTION testfunc_b(int) SET SCHEMA testschema_2; -- fail + + +CREATE AGGREGATE testagg_a( + sfunc1 = int4pl, basetype = int4, stype1 = int4, + initcond1 = '0' +); +CREATE AGGREGATE testagg_b( + sfunc1 = int4pl, basetype = int4, stype1 = int4, + initcond1 = '0' +); +ALTER AGGREGATE testagg_a(int) RENAME TO testagg_b; -- fail +ALTER AGGREGATE testagg_b(int) SET SCHEMA testschema_2; -- OK +ALTER AGGREGATE testagg_a(int) RENAME TO testagg_b; -- OK +ALTER AGGREGATE testagg_b(int) SET SCHEMA testschema_2; -- fail + +-- +-- TYPE +-- +CREATE TYPE testtype_a; +CREATE TYPE testtype_b; +CREATE FUNCTION testtype_a_in (cstring) RETURNS testtype_a LANGUAGE 'internal' AS 'int4in'; +CREATE FUNCTION testtype_b_in (cstring) RETURNS testtype_b LANGUAGE 'internal' AS 'int4in'; +CREATE FUNCTION testtype_a_out (testtype_a) RETURNS cstring LANGUAGE 'internal' AS 'int4out'; +CREATE FUNCTION testtype_b_out (testtype_b) RETURNS cstring LANGUAGE 'internal' AS 'int4out'; +CREATE TYPE testtype_a (internallength = 4, input = testtype_a_in, output = testtype_a_out); +CREATE TYPE testtype_b (internallength = 4, input = testtype_b_in, output = testtype_b_out); + +ALTER TYPE testtype_a RENAME TO testtype_b; -- fail +ALTER TYPE testtype_b SET SCHEMA testschema_2; -- OK +ALTER TYPE testtype_a RENAME TO testtype_b; -- OK +ALTER TYPE testtype_b SET SCHEMA testschema_2; -- fail + +-- +-- OPERATOR, OPERATOR FAMILY and OPERATOR CLASS +-- + +CREATE OPERATOR @#@ + (leftarg = int8, rightarg = int8, procedure = int8xor); +ALTER OPERATOR @#@(int8,int8) SET SCHEMA testschema_2; -- OK +CREATE OPERATOR @#@ + (leftarg = int8, rightarg = int8, procedure = int8xor); +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR @#@(int8,int8) SET SCHEMA testschema_2; -- fail + +CREATE OPERATOR FAMILY testopfamily_a USING btree; +CREATE OPERATOR FAMILY testopfamily_b USING btree; +ALTER OPERATOR FAMILY testopfamily_a USING btree RENAME TO testopfamily_b; -- fail +ALTER OPERATOR FAMILY testopfamily_b USING btree SET SCHEMA testschema_2; -- OK +ALTER OPERATOR FAMILY testopfamily_a USING btree RENAME TO testopfamily_b; -- OK +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR FAMILY testopfamily_b USING btree SET SCHEMA testschema_2; -- fail + + +CREATE OPERATOR CLASS testopclass_a + FOR TYPE box USING btree FAMILY testopfamily_b AS OPERATOR 1 =; +CREATE OPERATOR CLASS testopclass_b + FOR TYPE circle USING btree FAMILY testopfamily_b AS OPERATOR 1 =; +ALTER OPERATOR CLASS testopclass_a USING btree RENAME TO testopclass_b; -- fail +ALTER OPERATOR CLASS testopclass_b USING btree SET SCHEMA testschema_2; -- OK +ALTER OPERATOR CLASS testopclass_a USING btree RENAME TO testopclass_b; -- OK +-- XXX - comment out due to error message with oid determinded at runtime +-- ALTER OPERATOR CLASS testopclass_b USING btree SET SCHEMA testschema_2; -- fail + +-- +-- TEXT SEARCH +-- +CREATE TEXT SEARCH DICTIONARY testtsdict_a ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +CREATE TEXT SEARCH DICTIONARY testtsdict_b ( + Template=ispell, + DictFile=ispell_sample, + AffFile=ispell_sample +); +ALTER TEXT SEARCH DICTIONARY testtsdict_a RENAME TO testtsdict_b; -- fail +ALTER TEXT SEARCH DICTIONARY testtsdict_b SET SCHEMA testschema_2; -- OK +ALTER TEXT SEARCH DICTIONARY testtsdict_a RENAME TO testtsdict_b; -- OK +ALTER TEXT SEARCH DICTIONARY testtsdict_b SET SCHEMA testschema_2; -- fail + +CREATE TEXT SEARCH CONFIGURATION testtsconf_a (COPY=english); +CREATE TEXT SEARCH CONFIGURATION testtsconf_b (COPY=english); +ALTER TEXT SEARCH CONFIGURATION testtsconf_a RENAME TO testtsconf_b; -- fail +ALTER TEXT SEARCH CONFIGURATION testtsconf_b SET SCHEMA testschema_2; -- OK +ALTER TEXT SEARCH CONFIGURATION testtsconf_a RENAME TO testtsconf_b; -- OK +ALTER TEXT SEARCH CONFIGURATION testtsconf_b SET SCHEMA testschema_2; -- fail + +-- +-- COLLATION +-- +CREATE COLLATION testcollation_a FROM "C"; +CREATE COLLATION testcollation_b FROM "C"; + +ALTER COLLATION testcollation_a RENAME TO testcollation_b; -- fail +ALTER COLLATION testcollation_b SET SCHEMA testschema_2; -- OK +ALTER COLLATION testcollation_a RENAME TO testcollation_b; -- OK +ALTER COLLATION testcollation_b SET SCHEMA testschema_2; -- fail + +-- +-- CONVERSION +-- +CREATE CONVERSION testconv_a FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; +CREATE CONVERSION testconv_b FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; + +ALTER CONVERSION testconv_a RENAME TO testconv_b; -- fail +ALTER CONVERSION testconv_b SET SCHEMA testschema_2; -- OK +ALTER CONVERSION testconv_a RENAME TO testconv_b; -- OK +ALTER CONVERSION testconv_b SET SCHEMA testschema_2; -- fail + +-- +-- SCHEMA +-- +ALTER SCHEMA testschema_1 RENAME TO testschema_2; -- fail +ALTER SCHEMA testschema_1 RENAME TO testschema_3; -- OK + +DROP SCHEMA testschema_2, testschema_3 CASCADE;