Andrew Dunstan <[EMAIL PROTECTED]> writes:
> Tom Lane wrote:
>> Auto-rename. I'm working on a patch now, and it doesn't look like it'll
>> be too awful. Will post it for comments when it's working.
> Ok, cool. I look forward to it.
Here's a bare-bones patch (no doc or regression tests). Seems to work.
Anyone think this is too ugly a way to proceed?
regards, tom lane
Index: src/backend/catalog/heap.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/heap.c,v
retrieving revision 1.319
diff -c -r1.319 heap.c
*** src/backend/catalog/heap.c 11 May 2007 17:57:11 -0000 1.319
--- src/backend/catalog/heap.c 11 May 2007 23:18:51 -0000
***************
*** 797,802 ****
--- 797,803 ----
{
Relation pg_class_desc;
Relation new_rel_desc;
+ Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
***************
*** 815,820 ****
--- 816,842 ----
errmsg("relation \"%s\" already exists",
relname)));
/*
+ * Since we are going to create a rowtype as well, also check for
+ * collision with an existing type name. If there is one and it's
+ * an autogenerated array, we can rename it out of the way; otherwise
+ * we can at least give a good error message.
+ */
+ old_type_oid = GetSysCacheOid(TYPENAMENSP,
+
CStringGetDatum(relname),
+
ObjectIdGetDatum(relnamespace),
+ 0, 0);
+ if (OidIsValid(old_type_oid))
+ {
+ if (!moveArrayTypeName(old_type_oid, relname, relnamespace))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists",
relname),
+ errhint("A relation has an associated
type of the same name, "
+ "so you must use a
name that doesn't conflict "
+ "with any existing
type.")));
+ }
+
+ /*
* Allocate an OID for the relation, unless we were told what to use.
*
* The OID will be the relfilenode as well, so make sure it doesn't
***************
*** 861,868 ****
* Since defining a relation also defines a complex type, we add a new
* system type corresponding to the new relation.
*
! * NOTE: we could get a unique-index failure here, in case the same name
! * has already been used for a type.
*/
new_type_oid = AddNewRelationType(relname,
relnamespace,
--- 883,891 ----
* Since defining a relation also defines a complex type, we add a new
* system type corresponding to the new relation.
*
! * NOTE: we could get a unique-index failure here, in case someone else
is
! * creating the same type name in parallel but hadn't committed yet
! * when we checked for a duplicate name above.
*/
new_type_oid = AddNewRelationType(relname,
relnamespace,
Index: src/backend/catalog/pg_type.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v
retrieving revision 1.112
diff -c -r1.112 pg_type.c
*** src/backend/catalog/pg_type.c 11 May 2007 17:57:12 -0000 1.112
--- src/backend/catalog/pg_type.c 11 May 2007 23:18:51 -0000
***************
*** 15,20 ****
--- 15,21 ----
#include "postgres.h"
#include "access/heapam.h"
+ #include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
***************
*** 26,31 ****
--- 27,33 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
#include "utils/syscache.h"
***************
*** 551,580 ****
/*
* TypeRename
! * This renames a type
*
! * Note: any associated array type is *not* renamed; caller must make
! * another call to handle that case. Currently this is only used for
! * renaming types associated with tables, for which there are no arrays.
*/
void
! TypeRename(const char *oldTypeName, Oid typeNamespace,
! const char *newTypeName)
{
Relation pg_type_desc;
HeapTuple tuple;
pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
! tuple = SearchSysCacheCopy(TYPENAMENSP,
!
CStringGetDatum(oldTypeName),
!
ObjectIdGetDatum(typeNamespace),
! 0, 0);
if (!HeapTupleIsValid(tuple))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_OBJECT),
! errmsg("type \"%s\" does not exist",
oldTypeName)));
if (SearchSysCacheExists(TYPENAMENSP,
CStringGetDatum(newTypeName),
ObjectIdGetDatum(typeNamespace),
--- 553,587 ----
/*
* TypeRename
! * This renames a type, as well as any associated array type.
*
! * Note: this isn't intended to be a user-exposed function; it doesn't check
! * permissions etc. (Perhaps TypeRenameInternal would be a better name.)
! * Currently this is only used for renaming table rowtypes.
*/
void
! TypeRename(Oid typeOid, const char *newTypeName, Oid typeNamespace)
{
Relation pg_type_desc;
HeapTuple tuple;
+ Form_pg_type typ;
+ Oid arrayOid;
pg_type_desc = heap_open(TypeRelationId, RowExclusiveLock);
! tuple = SearchSysCacheCopy(TYPEOID,
!
ObjectIdGetDatum(typeOid),
! 0, 0, 0);
if (!HeapTupleIsValid(tuple))
! elog(ERROR, "cache lookup failed for type %u", typeOid);
! typ = (Form_pg_type) GETSTRUCT(tuple);
!
! /* We are not supposed to be changing schemas here */
! Assert(typeNamespace == typ->typnamespace);
+ arrayOid = typ->typarray;
+
+ /* Just to give a more friendly error than unique-index violation */
if (SearchSysCacheExists(TYPENAMENSP,
CStringGetDatum(newTypeName),
ObjectIdGetDatum(typeNamespace),
***************
*** 583,589 ****
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("type \"%s\" already exists",
newTypeName)));
! namestrcpy(&(((Form_pg_type) GETSTRUCT(tuple))->typname), newTypeName);
simple_heap_update(pg_type_desc, &tuple->t_self, tuple);
--- 590,597 ----
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("type \"%s\" already exists",
newTypeName)));
! /* OK, do the rename --- tuple is a copy, so OK to scribble on it */
! namestrcpy(&(typ->typname), newTypeName);
simple_heap_update(pg_type_desc, &tuple->t_self, tuple);
***************
*** 592,602 ****
heap_freetuple(tuple);
heap_close(pg_type_desc, RowExclusiveLock);
}
/*
! * makeArrayTypeName(typeName)
* - given a base type name, make an array type name for it
*
* the caller is responsible for pfreeing the result
--- 600,619 ----
heap_freetuple(tuple);
heap_close(pg_type_desc, RowExclusiveLock);
+
+ /* If the type has an array type, recurse to handle that */
+ if (OidIsValid(arrayOid))
+ {
+ char *arrname = makeArrayTypeName(newTypeName, typeNamespace);
+
+ TypeRename(arrayOid, arrname, typeNamespace);
+ pfree(arrname);
+ }
}
/*
! * makeArrayTypeName
* - given a base type name, make an array type name for it
*
* the caller is responsible for pfreeing the result
***************
*** 638,640 ****
--- 655,712 ----
return arr;
}
+
+
+ /*
+ * moveArrayTypeName
+ * - try to reassign an array type name that the user wants to use.
+ *
+ * The given type name has been discovered to already exist (with the given
+ * OID). If it is an autogenerated array type, change the array type's name
+ * to not conflict. This allows the user to create type "foo" followed by
+ * type "_foo" without problems. (Of course, there are race conditions if
+ * two backends try to create similarly-named types concurrently, but the
+ * worst that can happen is an unnecessary failure --- anything we do here
+ * will be rolled back if the type creation fails due to conflicting names.)
+ *
+ * Note that this must be called *before* calling makeArrayTypeName to
+ * determine the new type's own array type name; else the latter will
+ * certainly pick the same name.
+ *
+ * Returns TRUE if successfully moved the type, FALSE if not.
+ */
+ bool
+ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
+ {
+ Oid elemOid;
+ char *newname;
+
+ /*
+ * Can't change it if it's not an autogenerated array type.
+ */
+ elemOid = get_element_type(typeOid);
+ if (!OidIsValid(elemOid) ||
+ get_array_type(elemOid) != typeOid)
+ return false;
+
+ /*
+ * OK, use makeArrayTypeName to pick an unused modification of the
+ * name. Note that since makeArrayTypeName is an iterative process,
+ * this will produce a name that it might have produced the first time,
+ * had the conflicting type we are about to create already existed.
+ */
+ newname = makeArrayTypeName(typeName, typeNamespace);
+
+ /* Apply the rename */
+ TypeRename(typeOid, newname, typeNamespace);
+
+ /*
+ * We must bump the command counter so that any subsequent use of
+ * makeArrayTypeName sees what we just did and doesn't pick the same
name.
+ */
+ CommandCounterIncrement();
+
+ pfree(newname);
+
+ return true;
+ }
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.221
diff -c -r1.221 tablecmds.c
*** src/backend/commands/tablecmds.c 11 May 2007 20:16:36 -0000 1.221
--- src/backend/commands/tablecmds.c 11 May 2007 23:18:52 -0000
***************
*** 1626,1631 ****
--- 1626,1632 ----
Relation targetrelation;
Relation relrelation; /* for RELATION relation */
HeapTuple reltup;
+ Form_pg_class relform;
Oid namespaceId;
char *oldrelname;
char relkind;
***************
*** 1655,1664 ****
relrelation = heap_open(RelationRelationId, RowExclusiveLock);
reltup = SearchSysCacheCopy(RELOID,
!
PointerGetDatum(myrelid),
0, 0, 0);
if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for relation %u", myrelid);
if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
ereport(ERROR,
--- 1656,1666 ----
relrelation = heap_open(RelationRelationId, RowExclusiveLock);
reltup = SearchSysCacheCopy(RELOID,
!
ObjectIdGetDatum(myrelid),
0, 0, 0);
if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for relation %u", myrelid);
+ relform = (Form_pg_class) GETSTRUCT(reltup);
if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
ereport(ERROR,
***************
*** 1670,1676 ****
* Update pg_class tuple with new relname. (Scribbling on reltup
is OK
* because it's a copy...)
*/
! namestrcpy(&(((Form_pg_class) GETSTRUCT(reltup))->relname), newrelname);
simple_heap_update(relrelation, &reltup->t_self, reltup);
--- 1672,1678 ----
* Update pg_class tuple with new relname. (Scribbling on reltup
is OK
* because it's a copy...)
*/
! namestrcpy(&(relform->relname), newrelname);
simple_heap_update(relrelation, &reltup->t_self, reltup);
***************
*** 1683,1690 ****
/*
* Also rename the associated type, if any.
*/
! if (relkind != RELKIND_INDEX)
! TypeRename(oldrelname, namespaceId, newrelname);
/*
* Close rel, but keep exclusive lock!
--- 1685,1692 ----
/*
* Also rename the associated type, if any.
*/
! if (OidIsValid(targetrelation->rd_rel->reltype))
! TypeRename(targetrelation->rd_rel->reltype, newrelname,
namespaceId);
/*
* Close rel, but keep exclusive lock!
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/typecmds.c,v
retrieving revision 1.103
diff -c -r1.103 typecmds.c
*** src/backend/commands/typecmds.c 11 May 2007 20:16:54 -0000 1.103
--- src/backend/commands/typecmds.c 11 May 2007 23:18:52 -0000
***************
*** 137,149 ****
/*
* Look to see if type already exists (presumably as a shell; if not,
! * TypeCreate will complain). If it doesn't, create it as a shell, so
! * that the OID is known for use in the I/O function definitions.
*/
typoid = GetSysCacheOid(TYPENAMENSP,
CStringGetDatum(typeName),
ObjectIdGetDatum(typeNamespace),
0, 0);
if (!OidIsValid(typoid))
{
typoid = TypeShellMake(typeName, typeNamespace);
--- 137,163 ----
/*
* Look to see if type already exists (presumably as a shell; if not,
! * TypeCreate will complain).
*/
typoid = GetSysCacheOid(TYPENAMENSP,
CStringGetDatum(typeName),
ObjectIdGetDatum(typeNamespace),
0, 0);
+
+ /*
+ * If it's not a shell, see if it's an autogenerated array type,
+ * and if so rename it out of the way.
+ */
+ if (OidIsValid(typoid) && get_typisdefined(typoid))
+ {
+ if (moveArrayTypeName(typoid, typeName, typeNamespace))
+ typoid = InvalidOid;
+ }
+
+ /*
+ * If it doesn't exist, create it as a shell, so that the OID is known
+ * for use in the I/O function definitions.
+ */
if (!OidIsValid(typoid))
{
typoid = TypeShellMake(typeName, typeNamespace);
***************
*** 602,607 ****
--- 616,622 ----
ListCell *listptr;
Oid basetypeoid;
Oid domainoid;
+ Oid old_type_oid;
Form_pg_type baseType;
int32 basetypeMod;
***************
*** 617,622 ****
--- 632,653 ----
get_namespace_name(domainNamespace));
/*
+ * Check for collision with an existing type name. If there is one and
+ * it's an autogenerated array, we can rename it out of the way.
+ */
+ old_type_oid = GetSysCacheOid(TYPENAMENSP,
+
CStringGetDatum(domainName),
+
ObjectIdGetDatum(domainNamespace),
+ 0, 0);
+ if (OidIsValid(old_type_oid))
+ {
+ if (!moveArrayTypeName(old_type_oid, domainName,
domainNamespace))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists",
domainName)));
+ }
+
+ /*
* Look up the base type.
*/
typeTup = typenameType(NULL, stmt->typename);
***************
*** 948,953 ****
--- 979,985 ----
Oid enumNamespace;
Oid enumTypeOid;
AclResult aclresult;
+ Oid old_type_oid;
Oid enumArrayOid;
Relation pg_type;
***************
*** 961,966 ****
--- 993,1014 ----
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(enumNamespace));
+ /*
+ * Check for collision with an existing type name. If there is one and
+ * it's an autogenerated array, we can rename it out of the way.
+ */
+ old_type_oid = GetSysCacheOid(TYPENAMENSP,
+
CStringGetDatum(enumName),
+
ObjectIdGetDatum(enumNamespace),
+ 0, 0);
+ if (OidIsValid(old_type_oid))
+ {
+ if (!moveArrayTypeName(old_type_oid, enumName, enumNamespace))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists",
enumName)));
+ }
+
/* Preassign array type OID so we can insert it in pg_type.typarray */
pg_type = heap_open(TypeRelationId, AccessShareLock);
enumArrayOid = GetNewOid(pg_type);
Index: src/include/catalog/pg_type.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_type.h,v
retrieving revision 1.183
diff -c -r1.183 pg_type.h
*** src/include/catalog/pg_type.h 11 May 2007 17:57:13 -0000 1.183
--- src/include/catalog/pg_type.h 11 May 2007 23:18:52 -0000
***************
*** 645,653 ****
Node *defaultExpr,
bool rebuild);
! extern void TypeRename(const char *oldTypeName, Oid typeNamespace,
! const char *newTypeName);
extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
#endif /* PG_TYPE_H */
--- 645,656 ----
Node *defaultExpr,
bool rebuild);
! extern void TypeRename(Oid typeOid, const char *newTypeName,
! Oid typeNamespace);
extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
+ extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
+ Oid typeNamespace);
+
#endif /* PG_TYPE_H */
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?
http://archives.postgresql.org