diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 73138f940ae..b565e9b2c33 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -19,9 +19,16 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_conversion.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_ts_config.h" +#include "catalog/pg_ts_dict.h" +#include "catalog/pg_ts_parser.h" +#include "catalog/pg_ts_template.h" #include "commands/alter.h" #include "commands/collationcmds.h" #include "commands/conversioncmds.h" @@ -46,6 +53,8 @@ #include "utils/tqual.h" +static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid); + /* * Executes an ALTER OBJECT / RENAME TO statement. Based on the object * type, the function appropriate to that type is executed. @@ -146,40 +155,32 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) { switch (stmt->objectType) { - case OBJECT_AGGREGATE: - return AlterFunctionNamespace(stmt->object, stmt->objarg, true, - stmt->newschema); - - case OBJECT_COLLATION: - return AlterCollationNamespace(stmt->object, stmt->newschema); - case OBJECT_EXTENSION: return AlterExtensionNamespace(stmt->object, stmt->newschema); - case OBJECT_FUNCTION: - return AlterFunctionNamespace(stmt->object, stmt->objarg, false, - stmt->newschema); - + case OBJECT_FOREIGN_TABLE: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: - case OBJECT_FOREIGN_TABLE: return AlterTableNamespace(stmt); - case OBJECT_TYPE: case OBJECT_DOMAIN: + case OBJECT_TYPE: return AlterTypeNamespace(stmt->object, stmt->newschema, stmt->objectType); /* generic code path */ + case OBJECT_AGGREGATE: + case OBJECT_COLLATION: case OBJECT_CONVERSION: + case OBJECT_FUNCTION: case OBJECT_OPERATOR: case OBJECT_OPCLASS: case OBJECT_OPFAMILY: - case OBJECT_TSPARSER: - case OBJECT_TSDICTIONARY: - case OBJECT_TSTEMPLATE: case OBJECT_TSCONFIGURATION: + case OBJECT_TSDICTIONARY: + case OBJECT_TSPARSER: + case OBJECT_TSTEMPLATE: { Relation catalog; Relation relation; @@ -253,22 +254,16 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, break; } - case OCLASS_PROC: - oldNspOid = AlterFunctionNamespace_oid(objid, nspOid); - break; - case OCLASS_TYPE: oldNspOid = AlterTypeNamespace_oid(objid, nspOid, objsMoved); break; case OCLASS_COLLATION: - oldNspOid = AlterCollationNamespace_oid(objid, nspOid); - break; - case OCLASS_CONVERSION: case OCLASS_OPERATOR: case OCLASS_OPCLASS: case OCLASS_OPFAMILY: + case OCLASS_PROC: case OCLASS_TSPARSER: case OCLASS_TSDICT: case OCLASS_TSTEMPLATE: @@ -292,6 +287,43 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, return oldNspOid; } +/* + * Raise an error to the effect that an object of the given name is already + * present in the given namespace. + */ +static void +report_namespace_conflict(Oid classId, const char *name, Oid nspOid) +{ + char *msgfmt; + + Assert(OidIsValid(nspOid)); + switch (classId) + { + case ConversionRelationId: + msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\""); + break; + case TSParserRelationId: + msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\""); + break; + case TSDictionaryRelationId: + msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\""); + break; + case TSTemplateRelationId: + msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\""); + break; + case TSConfigRelationId: + msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\""); + break; + default: + elog(ERROR, "unsupported object class %u", classId); + break; + } + + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg(msgfmt, name, get_namespace_name(nspOid)))); +} + /* * Generic function to change the namespace of a given object, for simple * cases (won't work for tables, nor other cases where we need to do more @@ -303,7 +335,7 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, * * Returns the OID of the object's previous namespace. */ -Oid +static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) { Oid classId = RelationGetRelid(rel); @@ -373,13 +405,36 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) * Since this is just a friendliness check, we can just skip it in cases * where there isn't a suitable syscache available. */ - if (nameCacheId >= 0 && - SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("%s already exists in schema \"%s\"", - getObjectDescriptionOids(classId, objid), - get_namespace_name(nspOid)))); + if (classId == ProcedureRelationId) + { + HeapTuple tup; + Form_pg_proc proc; + + tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(objid)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for function %u", objid); + proc = (Form_pg_proc) GETSTRUCT(tup); + + IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs, + proc->proargtypes, nspOid); + heap_freetuple(tup); + } + else if (classId == CollationRelationId) + { + char *collname; + + collname = get_collation_name(objid); + if (!collname) + elog(ERROR, "cache lookup failed for collation %u", objid); + IsThereCollationInNamespace(collname, nspOid); + pfree(collname); + } + else if (nameCacheId >= 0 && + SearchSysCacheExists2(nameCacheId, name, + ObjectIdGetDatum(nspOid))) + report_namespace_conflict(classId, + NameStr(*(DatumGetName(name))), + nspOid); /* Build modified tuple */ values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum)); @@ -406,7 +461,6 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid) return oldNspOid; } - /* * Executes an ALTER OBJECT / OWNER TO statement. Based on the object * type, the function appropriate to that type is executed. diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c index b763f15b30d..67200f87ba5 100644 --- a/src/backend/commands/collationcmds.c +++ b/src/backend/commands/collationcmds.c @@ -167,27 +167,7 @@ RenameCollation(List *name, const char *newname) namespaceOid = ((Form_pg_collation) GETSTRUCT(tup))->collnamespace; /* make sure the new name doesn't exist */ - if (SearchSysCacheExists3(COLLNAMEENCNSP, - CStringGetDatum(newname), - Int32GetDatum(GetDatabaseEncoding()), - ObjectIdGetDatum(namespaceOid))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", - newname, - GetDatabaseEncodingName(), - get_namespace_name(namespaceOid)))); - - /* mustn't match an any-encoding entry, either */ - if (SearchSysCacheExists3(COLLNAMEENCNSP, - CStringGetDatum(newname), - Int32GetDatum(-1), - ObjectIdGetDatum(namespaceOid))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("collation \"%s\" already exists in schema \"%s\"", - newname, - get_namespace_name(namespaceOid)))); + IsThereCollationInNamespace(newname, namespaceOid); /* must be owner */ if (!pg_collation_ownercheck(collationOid, GetUserId())) @@ -213,71 +193,32 @@ RenameCollation(List *name, const char *newname) } /* - * Execute ALTER COLLATION SET SCHEMA + * Subroutine for ALTER COLLATION SET SCHEMA and RENAME + * + * Is there a collation with the same name of the given collation already in + * the given namespace? If so, raise an appropriate error message. */ -Oid -AlterCollationNamespace(List *name, const char *newschema) +void +IsThereCollationInNamespace(const char *collname, Oid nspOid) { - Oid collOid, - nspOid; - - collOid = get_collation_oid(name, false); - - nspOid = LookupCreationNamespace(newschema); - - AlterCollationNamespace_oid(collOid, nspOid); - - return collOid; -} - -/* - * Change collation schema, by oid - */ -Oid -AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) -{ - Oid oldNspOid; - Relation rel; - char *collation_name; - - rel = heap_open(CollationRelationId, RowExclusiveLock); - - /* - * We have to check for name collision ourselves, because - * AlterObjectNamespace_internal doesn't know how to deal with the encoding - * considerations. - */ - collation_name = get_collation_name(collOid); - if (!collation_name) - elog(ERROR, "cache lookup failed for collation %u", collOid); - /* make sure the name doesn't already exist in new schema */ if (SearchSysCacheExists3(COLLNAMEENCNSP, - CStringGetDatum(collation_name), + CStringGetDatum(collname), Int32GetDatum(GetDatabaseEncoding()), - ObjectIdGetDatum(newNspOid))) + ObjectIdGetDatum(nspOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"", - collation_name, - GetDatabaseEncodingName(), - get_namespace_name(newNspOid)))); + collname, GetDatabaseEncodingName(), + get_namespace_name(nspOid)))); /* mustn't match an any-encoding entry, either */ if (SearchSysCacheExists3(COLLNAMEENCNSP, - CStringGetDatum(collation_name), + CStringGetDatum(collname), Int32GetDatum(-1), - ObjectIdGetDatum(newNspOid))) + ObjectIdGetDatum(nspOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("collation \"%s\" already exists in schema \"%s\"", - collation_name, - get_namespace_name(newNspOid)))); - - /* OK, do the work */ - oldNspOid = AlterObjectNamespace_internal(rel, collOid, newNspOid); - - heap_close(rel, RowExclusiveLock); - - return oldNspOid; + collname, get_namespace_name(nspOid)))); } diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index c858511c1d9..4f8e82a8e69 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -1069,20 +1069,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) namespaceOid = procForm->pronamespace; /* make sure the new name doesn't exist */ - if (SearchSysCacheExists3(PROCNAMEARGSNSP, - CStringGetDatum(newname), - PointerGetDatum(&procForm->proargtypes), - ObjectIdGetDatum(namespaceOid))) - { - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_FUNCTION), - errmsg("function %s already exists in schema \"%s\"", - funcname_signature_string(newname, - procForm->pronargs, - NIL, - procForm->proargtypes.values), - get_namespace_name(namespaceOid)))); - } + IsThereFunctionInNamespace(newname, procForm->pronargs, + procForm->proargtypes, + namespaceOid); /* must be owner */ if (!pg_proc_ownercheck(procOid, GetUserId())) @@ -1688,71 +1677,28 @@ DropCastById(Oid castOid) } /* - * Execute ALTER FUNCTION/AGGREGATE SET SCHEMA + * Subroutine for ALTER FUNCTION/AGGREGATE SET SCHEMA * - * These commands are identical except for the lookup procedure, so share code. + * Is there a function with the given name and signature already in the given + * namespace? If so, raise an appropriate error message. */ -Oid -AlterFunctionNamespace(List *name, List *argtypes, bool isagg, - const char *newschema) +void +IsThereFunctionInNamespace(const char *proname, int pronargs, + oidvector proargtypes, Oid nspOid) { - Oid procOid; - Oid nspOid; - - /* get function OID */ - if (isagg) - procOid = LookupAggNameTypeNames(name, argtypes, false); - else - procOid = LookupFuncNameTypeNames(name, argtypes, false); - - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); - - AlterFunctionNamespace_oid(procOid, nspOid); - - return procOid; -} - -Oid -AlterFunctionNamespace_oid(Oid procOid, Oid nspOid) -{ - Oid oldNspOid; - HeapTuple tup; - Relation procRel; - Form_pg_proc proc; - - procRel = heap_open(ProcedureRelationId, RowExclusiveLock); - - /* - * We have to check for name collisions ourselves, because - * AlterObjectNamespace_internal doesn't know how to deal with the - * argument types. - */ - tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "cache lookup failed for function %u", procOid); - proc = (Form_pg_proc) GETSTRUCT(tup); - /* check for duplicate name (more friendly than unique-index failure) */ if (SearchSysCacheExists3(PROCNAMEARGSNSP, - CStringGetDatum(NameStr(proc->proname)), - PointerGetDatum(&proc->proargtypes), + CStringGetDatum(proname), + PointerGetDatum(&proargtypes), ObjectIdGetDatum(nspOid))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), - errmsg("function \"%s\" already exists in schema \"%s\"", - NameStr(proc->proname), + errmsg("function %s already exists in schema \"%s\"", + funcname_signature_string(proname, pronargs, + NIL, proargtypes.values), get_namespace_name(nspOid)))); - - /* OK, do the work */ - oldNspOid = AlterObjectNamespace_internal(procRel, procOid, nspOid); - - heap_close(procRel, RowExclusiveLock); - - return oldNspOid; } - /* * ExecuteDoStmt * Execute inline procedural-language code diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h index 37e095efa56..b9400a98128 100644 --- a/src/include/commands/alter.h +++ b/src/include/commands/alter.h @@ -21,9 +21,8 @@ extern Oid ExecRenameStmt(RenameStmt *stmt); extern Oid ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); -extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, +extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid, ObjectAddresses *objsMoved); -extern Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid); extern Oid ExecAlterOwnerStmt(AlterOwnerStmt *stmt); extern void AlterObjectOwner_internal(Relation catalog, Oid objectId, diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h index c7b536a6358..5aaa1781726 100644 --- a/src/include/commands/collationcmds.h +++ b/src/include/commands/collationcmds.h @@ -19,7 +19,6 @@ extern Oid DefineCollation(List *names, List *parameters); extern Oid RenameCollation(List *name, const char *newname); -extern Oid AlterCollationNamespace(List *name, const char *newschema); -extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid); +extern void IsThereCollationInNamespace(const char *collname, Oid newNspOid); #endif /* COLLATIONCMDS_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index c327136a36d..7de6d5de979 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -50,9 +50,8 @@ extern Oid RenameFunction(List *name, List *argtypes, const char *newname); extern Oid AlterFunction(AlterFunctionStmt *stmt); extern Oid CreateCast(CreateCastStmt *stmt); extern void DropCastById(Oid castOid); -extern Oid AlterFunctionNamespace(List *name, List *argtypes, bool isagg, - const char *newschema); -extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid); +extern void IsThereFunctionInNamespace(const char *proname, int pronargs, + oidvector proargtypes, Oid nspOid); extern void ExecuteDoStmt(DoStmt *stmt); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out index 3beddd783e3..f27d21787e3 100644 --- a/src/test/regress/expected/alter_generic.out +++ b/src/test/regress/expected/alter_generic.out @@ -69,7 +69,7 @@ ERROR: must be member of role "regtest_alter_user3" ALTER FUNCTION alt_func3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of function alt_func3 ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- failed (name conflicts) -ERROR: function "alt_func2" already exists in schema "alt_nsp2" +ERROR: function alt_func2(integer) already exists in schema "alt_nsp2" ALTER AGGREGATE alt_agg3(int) RENAME TO alt_agg4; -- failed (not owner) ERROR: must be owner of function alt_agg3 ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg4; -- OK @@ -80,7 +80,7 @@ ERROR: must be member of role "regtest_alter_user3" ALTER AGGREGATE alt_agg3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of function alt_agg3 ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: function "alt_agg2" already exists in schema "alt_nsp2" +ERROR: function alt_agg2(integer) already exists in schema "alt_nsp2" RESET SESSION AUTHORIZATION; SELECT n.nspname, proname, prorettype::regtype, proisagg, a.rolname FROM pg_proc p, pg_namespace n, pg_authid a @@ -129,7 +129,7 @@ ERROR: must be member of role "regtest_alter_user3" ALTER CONVERSION alt_conv3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of conversion alt_conv3 ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: conversion alt_conv2 already exists in schema "alt_nsp2" +ERROR: conversion "alt_conv2" already exists in schema "alt_nsp2" RESET SESSION AUTHORIZATION; SELECT n.nspname, c.conname, a.rolname FROM pg_conversion c, pg_namespace n, pg_authid a @@ -346,7 +346,7 @@ ERROR: must be member of role "regtest_alter_user3" ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of text search dictionary alt_ts_dict3 ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search dictionary alt_ts_dict2 already exists in schema "alt_nsp2" +ERROR: text search dictionary "alt_ts_dict2" already exists in schema "alt_nsp2" RESET SESSION AUTHORIZATION; SELECT nspname, dictname, rolname FROM pg_ts_dict t, pg_namespace n, pg_authid a @@ -387,7 +387,7 @@ ERROR: must be member of role "regtest_alter_user3" ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of text search configuration alt_ts_conf3 ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search configuration alt_ts_conf2 already exists in schema "alt_nsp2" +ERROR: text search configuration "alt_ts_conf2" already exists in schema "alt_nsp2" RESET SESSION AUTHORIZATION; SELECT nspname, cfgname, rolname FROM pg_ts_config t, pg_namespace n, pg_authid a @@ -413,7 +413,7 @@ ALTER TEXT SEARCH TEMPLATE alt_ts_temp1 RENAME TO alt_ts_temp3; -- OK ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH TEMPLATE alt_ts_temp2 (lexize=dsimple_lexize); ALTER TEXT SEARCH TEMPLATE alt_ts_temp2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search template alt_ts_temp2 already exists in schema "alt_nsp2" +ERROR: text search template "alt_ts_temp2" already exists in schema "alt_nsp2" SELECT nspname, tmplname FROM pg_ts_template t, pg_namespace n WHERE t.tmplnamespace = n.oid AND nspname like 'alt_nsp%' @@ -439,7 +439,7 @@ ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- OK CREATE TEXT SEARCH PARSER alt_ts_prs2 (start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype); ALTER TEXT SEARCH PARSER alt_ts_prs2 SET SCHEMA alt_nsp2; -- failed (name conflict) -ERROR: text search parser alt_ts_prs2 already exists in schema "alt_nsp2" +ERROR: text search parser "alt_ts_prs2" already exists in schema "alt_nsp2" SELECT nspname, prsname FROM pg_ts_parser t, pg_namespace n WHERE t.prsnamespace = n.oid AND nspname like 'alt_nsp%'