1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +03:00

Rework order of checks in ALTER / SET SCHEMA

When attempting to move an object into the schema in which it already
was, for most objects classes we were correctly complaining about
exactly that ("object is already in schema"); but for some other object
classes, such as functions, we were instead complaining of a name
collision ("object already exists in schema").  The latter is wrong and
misleading, per complaint from Robert Haas in
CA+TgmoZ0+gNf7RDKRc3u5rHXffP=QjqPZKGxb4BsPz65k7qnHQ@mail.gmail.com

To fix, refactor the way these checks are done.  As a bonus, the
resulting code is smaller and can also share some code with Rename
cases.

While at it, remove use of getObjectDescriptionOids() in error messages.
These are normally disallowed because of translatability considerations,
but this one had slipped through since 9.1.  (Not sure that this is
worth backpatching, though, as it would create some untranslated
messages in back branches.)

This is loosely based on a patch by KaiGai Kohei, heavily reworked by
me.
This commit is contained in:
Alvaro Herrera
2013-01-15 13:23:43 -03:00
parent ffda05977a
commit 7ac5760fa2
7 changed files with 125 additions and 187 deletions

View File

@ -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.