From 1c7bef32b46ac602d5478314dcfe1154858e970e Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 8 Oct 2001 19:55:07 +0000
Subject: [PATCH] Fix ruleutils to depend on format_type, rather than having a
 private copy of code that knows about displaying types with typmod info.
 Needed so that it does the right thing with timestamp datatypes now.

---
 src/backend/utils/adt/format_type.c |  15 +++-
 src/backend/utils/adt/ruleutils.c   | 124 ++++++++++++++++++++--------
 src/include/utils/builtins.h        |   3 +-
 3 files changed, 104 insertions(+), 38 deletions(-)

diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index faa0c9f6bb6..51795aa3b35 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.18 2001/10/04 17:52:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.19 2001/10/08 19:55:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,11 +87,11 @@ format_type(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(_textin(result));
 }
 
-
-
 /*
  * This version is for use within the backend in error messages, etc.
  * One difference is that it will fail for an invalid type.
+ *
+ * The result is always a palloc'd string.
  */
 char *
 format_type_be(Oid type_oid)
@@ -99,6 +99,15 @@ format_type_be(Oid type_oid)
 	return format_type_internal(type_oid, -1, false);
 }
 
+/*
+ * This version allows a nondefault typemod to be specified.
+ */
+char *
+format_type_with_typemod(Oid type_oid, int32 typemod)
+{
+	return format_type_internal(type_oid, typemod, false);
+}
+
 
 
 static char *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c931d0a6ef3..1d1969e68d5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.84 2001/10/04 22:00:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.85 2001/10/08 19:55:07 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -130,6 +130,7 @@ static bool find_alias_in_namespace(Node *nsnode, Node *expr,
 static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
 static void get_rule_expr(Node *node, deparse_context *context);
 static void get_func_expr(Expr *expr, deparse_context *context);
+static Node *strip_type_coercion(Node *expr, Oid resultType);
 static void get_tle_expr(TargetEntry *tle, deparse_context *context);
 static void get_const_expr(Const *constval, deparse_context *context);
 static void get_sublink_expr(Node *node, deparse_context *context);
@@ -2038,49 +2039,31 @@ get_func_expr(Expr *expr, deparse_context *context)
 	if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
 	{
 		Node	   *arg = lfirst(expr->args);
+		char	   *typdesc;
 
 		/*
-		 * Strip off any RelabelType on the input, so we don't print
-		 * redundancies like x::bpchar::char(8).
+		 * Strip off any type coercions on the input, so we don't print
+		 * redundancies like x::bpchar::character(8).
 		 *
 		 * XXX Are there any cases where this is a bad idea?
 		 */
-		if (IsA(arg, RelabelType))
-			arg = ((RelabelType *) arg)->arg;
+		arg = strip_type_coercion(arg, procStruct->prorettype);
+
 		appendStringInfoChar(buf, '(');
 		get_rule_expr(arg, context);
-		appendStringInfo(buf, ")::");
-
 		/*
 		 * Show typename with appropriate length decoration. Note that
-		 * since exprIsLengthCoercion succeeded, the function name is the
-		 * same as its output type name.
+		 * since exprIsLengthCoercion succeeded, the function's output
+		 * type is the right thing to use.
+		 *
+		 * XXX In general it is incorrect to quote the result of
+		 * format_type_with_typemod, but are there any special cases
+		 * where we should do so?
 		 */
-		if (strcmp(proname, "bpchar") == 0)
-		{
-			if (coercedTypmod > (int32) VARHDRSZ)
-				appendStringInfo(buf, "char(%d)", coercedTypmod - VARHDRSZ);
-			else
-				appendStringInfo(buf, "char");
-		}
-		else if (strcmp(proname, "varchar") == 0)
-		{
-			if (coercedTypmod > (int32) VARHDRSZ)
-				appendStringInfo(buf, "varchar(%d)", coercedTypmod - VARHDRSZ);
-			else
-				appendStringInfo(buf, "varchar");
-		}
-		else if (strcmp(proname, "numeric") == 0)
-		{
-			if (coercedTypmod >= (int32) VARHDRSZ)
-				appendStringInfo(buf, "numeric(%d,%d)",
-							 ((coercedTypmod - VARHDRSZ) >> 16) & 0xffff,
-								 (coercedTypmod - VARHDRSZ) & 0xffff);
-			else
-				appendStringInfo(buf, "numeric");
-		}
-		else
-			appendStringInfo(buf, "%s", quote_identifier(proname));
+		typdesc = format_type_with_typemod(procStruct->prorettype,
+										   coercedTypmod);
+		appendStringInfo(buf, ")::%s", typdesc);
+		pfree(typdesc);
 
 		ReleaseSysCache(proctup);
 		return;
@@ -2103,6 +2086,79 @@ get_func_expr(Expr *expr, deparse_context *context)
 }
 
 
+/*
+ * strip_type_coercion
+ *		Strip any type coercions at the top of the given expression tree,
+ *		as long as they are coercions to the given datatype.
+ *
+ * A RelabelType node is always a type coercion.  A function call is also
+ * considered a type coercion if it has one argument and the function name
+ * is the same as the (internal) name of its result type.
+ *
+ * XXX It'd be better if the parsetree retained some explicit indication
+ * of the coercion, so we didn't need these heuristics.
+ */
+static Node *
+strip_type_coercion(Node *expr, Oid resultType)
+{
+	if (expr == NULL || exprType(expr) != resultType)
+		return expr;
+
+	if (IsA(expr, RelabelType))
+		return strip_type_coercion(((RelabelType *) expr)->arg, resultType);
+
+	if (IsA(expr, Expr) && ((Expr *) expr)->opType == FUNC_EXPR)
+	{
+		Func	   *func;
+		HeapTuple	procTuple;
+		HeapTuple	typeTuple;
+		Form_pg_proc procStruct;
+		Form_pg_type typeStruct;
+
+		func = (Func *) (((Expr *) expr)->oper);
+		Assert(IsA(func, Func));
+		if (length(((Expr *) expr)->args) != 1)
+			return expr;
+		/* Lookup the function in pg_proc */
+		procTuple = SearchSysCache(PROCOID,
+								   ObjectIdGetDatum(func->funcid),
+								   0, 0, 0);
+		if (!HeapTupleIsValid(procTuple))
+			elog(ERROR, "cache lookup for proc %u failed", func->funcid);
+		procStruct = (Form_pg_proc) GETSTRUCT(procTuple);
+		/* Double-check func has one arg and correct result type */
+		if (procStruct->pronargs != 1 ||
+			procStruct->prorettype != resultType)
+		{
+			ReleaseSysCache(procTuple);
+			return expr;
+		}
+		/* See if function has same name as its result type */
+		typeTuple = SearchSysCache(TYPEOID,
+								   ObjectIdGetDatum(procStruct->prorettype),
+								   0, 0, 0);
+		if (!HeapTupleIsValid(typeTuple))
+			elog(ERROR, "cache lookup for type %u failed",
+				 procStruct->prorettype);
+		typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+		if (strncmp(NameStr(procStruct->proname),
+					NameStr(typeStruct->typname),
+					NAMEDATALEN) != 0)
+		{
+			ReleaseSysCache(procTuple);
+			ReleaseSysCache(typeTuple);
+			return expr;
+		}
+		/* Okay, it is indeed a type-coercion function */
+		ReleaseSysCache(procTuple);
+		ReleaseSysCache(typeTuple);
+		return strip_type_coercion(lfirst(((Expr *) expr)->args), resultType);
+	}
+
+	return expr;
+}
+
+
 /* ----------
  * get_tle_expr
  *
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 0f0c9e15a8a..e9b52e7cb47 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.165 2001/09/28 08:09:14 thomas Exp $
+ * $Id: builtins.h,v 1.166 2001/10/08 19:55:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -598,6 +598,7 @@ extern Datum pg_convert2(PG_FUNCTION_ARGS);
 /* format_type.c */
 extern Datum format_type(PG_FUNCTION_ARGS);
 extern char * format_type_be(Oid type_oid);
+extern char * format_type_with_typemod(Oid type_oid, int32 typemod);
 extern Datum oidvectortypes(PG_FUNCTION_ARGS);
 extern int32 type_maximum_size(Oid type_oid, int32 typemod);
 
-- 
GitLab