From 31d17076f77c045c2645ab91c0dce6e9a52710ac Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 29 Nov 2019 23:03:13 -0300
Subject: [PATCH v7 13/13] rewrite makeUniqueTypeName

---
 src/backend/catalog/pg_type.c | 113 ++++++++++++++++------------------
 1 file changed, 53 insertions(+), 60 deletions(-)

diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 73b8ace4c5..9e3b866d1c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
 #include "utils/acl.h"
@@ -36,8 +37,8 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-static int	makeUniqueTypeName(char *dest, const char *typeName, size_t namelen,
-							   Oid typeNamespace, bool tryOriginalName);
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+								bool tryOriginal);
 
 /* Potentially set by pg_upgrade_support functions */
 Oid			binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -786,12 +787,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
 char *
 makeArrayTypeName(const char *typeName, Oid typeNamespace)
 {
-	char	   *arr = (char *) palloc(NAMEDATALEN);
-	int			namelen = strlen(typeName);
-	int			underscores;
+	char	   *arr;
 
-	underscores = makeUniqueTypeName(arr, typeName, namelen, typeNamespace, false);
-	if (underscores >= NAMEDATALEN - 1)
+	arr = makeUniqueTypeName(typeName, typeNamespace, false);
+	if (arr == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_OBJECT),
 				 errmsg("could not form array type name for type \"%s\"",
@@ -868,91 +867,85 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
  * makeMultirangeTypeName
  *	  - given a range type name, make a multirange type name for it
  *
- * the caller is responsible for pfreeing the result
+ * caller is responsible for pfreeing the result
  */
 char *
 makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
 {
-	char		mltrng[NAMEDATALEN];
-	char	   *mltrngunique = (char *) palloc(NAMEDATALEN);
-	int			namelen = strlen(rangeTypeName);
+	char	   *buf;
+	char	   *mrname;
 	char	   *rangestr;
-	int			rangeoffset;
-	int			underscores;
 
 	/*
 	 * If the range type name contains "range" then change that to
-	 * "multirange". Otherwise add "multirange" to the end. After that,
+	 * "multirange". Otherwise add "_multirange" to the end. After that,
 	 * prepend underscores as needed until we make a name that doesn't collide
 	 * with anything...
 	 */
-	strlcpy(mltrng, rangeTypeName, NAMEDATALEN);
 	rangestr = strstr(rangeTypeName, "range");
 	if (rangestr)
 	{
-		rangeoffset = rangestr - rangeTypeName;
-		strlcpy(mltrng + rangeoffset, "multi", NAMEDATALEN - rangeoffset);
-		strlcpy(mltrng + rangeoffset + 5, rangestr, NAMEDATALEN - rangeoffset - 5);
-		namelen += 5;
+		char	*prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+		buf = psprintf("%s%s%s", prefix, "multi", rangestr);
 	}
 	else
-	{
-		strlcpy(mltrng + namelen, "multirange", NAMEDATALEN - namelen);
-		namelen += 10;
-	}
+		buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
 
-	underscores = makeUniqueTypeName(mltrngunique, mltrng, namelen, typeNamespace, true);
-	if (underscores >= NAMEDATALEN - 1)
+	/* clip it at NAMEDATALEN-1 bytes */
+	buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+	mrname = makeUniqueTypeName(buf, typeNamespace, true);
+	if (mrname == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_OBJECT),
 				 errmsg("could not form multirange type name for type \"%s\"",
 						rangeTypeName)));
 
-	return mltrngunique;
+	return mrname;
 }
 
 /*
- * makeUniqueTypeName: Prepend underscores as needed until we make a name that
- * doesn't collide with anything. Tries the original typeName if requested.
+ * makeUniqueTypeName
+ *		Generate a unique name for a prospective new type
  *
- * Returns the number of underscores added.
+ * Given a typeName, return a new palloc'ed name by prepending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
  */
-int
-makeUniqueTypeName(char *dest, const char *typeName, size_t namelen, Oid typeNamespace,
-				   bool tryOriginalName)
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
 {
 	int			i;
+	int			namelen;
+	char		dest[NAMEDATALEN];
 
-	for (i = 0; i < NAMEDATALEN - 1; i++)
+	Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+	if (tryOriginal &&
+		!SearchSysCacheExists2(TYPENAMENSP,
+							   CStringGetDatum(typeName),
+							   ObjectIdGetDatum(typeNamespace)))
+		return pstrdup(typeName);
+
+	/*
+	 * The idea is to prepend underscores as needed until we make a name that
+	 * doesn't collide with anything ...
+	 */
+	namelen = strlen(typeName);
+	for (i = 1; i < NAMEDATALEN - 1; i++)
 	{
-		if (i == 0)
-		{
-			if (tryOriginalName &&
-				!SearchSysCacheExists2(TYPENAMENSP,
-									   CStringGetDatum(typeName),
-									   ObjectIdGetDatum(typeNamespace)))
-			{
-				strcpy(dest, typeName);
-				break;
-			}
+		dest[i - 1] = '_';
+		strlcpy(dest + i, typeName, NAMEDATALEN - i);
+		if (namelen + i >= NAMEDATALEN)
+			truncate_identifier(dest, NAMEDATALEN, false);
 
-		}
-		else
-		{
-			dest[i - 1] = '_';
-			if (i + namelen < NAMEDATALEN)
-				strcpy(dest + i, typeName);
-			else
-			{
-				strlcpy(dest + i, typeName, NAMEDATALEN);
-				truncate_identifier(dest, NAMEDATALEN, false);
-			}
-			if (!SearchSysCacheExists2(TYPENAMENSP,
-									   CStringGetDatum(dest),
-									   ObjectIdGetDatum(typeNamespace)))
-				break;
-		}
+		if (!SearchSysCacheExists2(TYPENAMENSP,
+								   CStringGetDatum(dest),
+								   ObjectIdGetDatum(typeNamespace)))
+			return pstrdup(dest);
 	}
 
-	return i;
+	return NULL;
 }
-- 
2.20.1

