From ea0f465db370cf05901a37606d69a656c4e0f1e6 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 29 Jun 2026 11:39:41 +0800
Subject: [PATCH v9 2/3] coerceUnknownConst delicated function for coerce const

---
 src/backend/parser/parse_coerce.c | 297 ++++++++++++++++--------------
 1 file changed, 159 insertions(+), 138 deletions(-)

diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 3b92ae2a920..e2edde40fc9 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -52,6 +52,11 @@ static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
 									  int location);
 static bool is_complex_array(Oid typid);
 static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId);
+static Node *coerceUnknownConst(ParseState *pstate, Node *node,
+								Oid inputTypeId,
+								Oid targetTypeId, int32 targetTypeMod,
+								CoercionContext ccontext, CoercionForm cformat,
+								int location);
 
 
 /*
@@ -231,145 +236,15 @@ coerce_type(ParseState *pstate, Node *node,
 		}
 	}
 	if (inputTypeId == UNKNOWNOID && IsA(node, Const))
-	{
-		/*
-		 * Input is a string constant with previously undetermined type. Apply
-		 * the target type's typinput function to it to produce a constant of
-		 * the target type.
-		 *
-		 * NOTE: this case cannot be folded together with the other
-		 * constant-input case, since the typinput function does not
-		 * necessarily behave the same as a type conversion function. For
-		 * example, int4's typinput function will reject "1.2", whereas
-		 * float-to-int type conversion will round to integer.
-		 *
-		 * XXX if the typinput function is not immutable, we really ought to
-		 * postpone evaluation of the function call until runtime. But there
-		 * is no way to represent a typinput function call as an expression
-		 * tree, because C-string values are not Datums. (XXX This *is*
-		 * possible as of 7.3, do we want to do it?)
-		 */
-		Const	   *con = (Const *) node;
-		Const	   *newcon = makeNode(Const);
-		Oid			baseTypeId;
-		int32		baseTypeMod;
-		int32		inputTypeMod;
-		Type		baseType;
-		ParseCallbackState pcbstate;
+		return coerceUnknownConst(pstate,
+								  node,
+								  inputTypeId,
+								  targetTypeId,
+								  targetTypeMod,
+								  ccontext,
+								  cformat,
+								  location);
 
-		/*
-		 * If the target type is a domain, we want to call its base type's
-		 * input routine, not domain_in().  This is to avoid premature failure
-		 * when the domain applies a typmod: existing input routines follow
-		 * implicit-coercion semantics for length checks, which is not always
-		 * what we want here.  The needed check will be applied properly
-		 * inside coerce_to_domain().
-		 */
-		baseTypeMod = targetTypeMod;
-		baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
-
-		/*
-		 * For most types we pass typmod -1 to the input routine, because
-		 * existing input routines follow implicit-coercion semantics for
-		 * length checks, which is not always what we want here.  Any length
-		 * constraint will be applied later by our caller.  An exception
-		 * however is the INTERVAL type, for which we *must* pass the typmod
-		 * or it won't be able to obey the bizarre SQL-spec input rules. (Ugly
-		 * as sin, but so is this part of the spec...)
-		 */
-		if (baseTypeId == INTERVALOID)
-			inputTypeMod = baseTypeMod;
-		else
-			inputTypeMod = -1;
-
-		baseType = typeidType(baseTypeId);
-
-		newcon->consttype = baseTypeId;
-		newcon->consttypmod = inputTypeMod;
-		newcon->constcollid = typeTypeCollation(baseType);
-		newcon->constlen = typeLen(baseType);
-		newcon->constbyval = typeByVal(baseType);
-		newcon->constisnull = con->constisnull;
-
-		/*
-		 * We use the original literal's location regardless of the position
-		 * of the coercion.  This is a change from pre-9.2 behavior, meant to
-		 * simplify life for pg_stat_statements.
-		 */
-		newcon->location = con->location;
-
-		/*
-		 * Set up to point at the constant's text if the input routine throws
-		 * an error.
-		 */
-		setup_parser_errposition_callback(&pcbstate, pstate, con->location);
-
-		/*
-		 * We assume here that UNKNOWN's internal representation is the same
-		 * as CSTRING.
-		 */
-		if (!con->constisnull)
-			newcon->constvalue = stringTypeDatum(baseType,
-												 DatumGetCString(con->constvalue),
-												 inputTypeMod);
-		else
-			newcon->constvalue = stringTypeDatum(baseType,
-												 NULL,
-												 inputTypeMod);
-
-		/*
-		 * If it's a varlena value, force it to be in non-expanded
-		 * (non-toasted) format; this avoids any possible dependency on
-		 * external values and improves consistency of representation.
-		 */
-		if (!con->constisnull && newcon->constlen == -1)
-			newcon->constvalue =
-				PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
-
-#ifdef RANDOMIZE_ALLOCATED_MEMORY
-
-		/*
-		 * For pass-by-reference data types, repeat the conversion to see if
-		 * the input function leaves any uninitialized bytes in the result. We
-		 * can only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is
-		 * enabled, so we don't bother testing otherwise.  The reason we don't
-		 * want any instability in the input function is that comparison of
-		 * Const nodes relies on bytewise comparison of the datums, so if the
-		 * input function leaves garbage then subexpressions that should be
-		 * identical may not get recognized as such.  See pgsql-hackers
-		 * discussion of 2008-04-04.
-		 */
-		if (!con->constisnull && !newcon->constbyval)
-		{
-			Datum		val2;
-
-			val2 = stringTypeDatum(baseType,
-								   DatumGetCString(con->constvalue),
-								   inputTypeMod);
-			if (newcon->constlen == -1)
-				val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
-			if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen))
-				elog(WARNING, "type %s has unstable input conversion for \"%s\"",
-					 typeTypeName(baseType), DatumGetCString(con->constvalue));
-		}
-#endif
-
-		cancel_parser_errposition_callback(&pcbstate);
-
-		result = (Node *) newcon;
-
-		/* If target is a domain, apply constraints. */
-		if (baseTypeId != targetTypeId)
-			result = coerce_to_domain(result,
-									  baseTypeId, baseTypeMod,
-									  targetTypeId,
-									  ccontext, cformat, location,
-									  false);
-
-		ReleaseSysCache(baseType);
-
-		return result;
-	}
 	if (IsA(node, Param) &&
 		pstate != NULL && pstate->p_coerce_param_hook != NULL)
 	{
@@ -546,6 +421,152 @@ coerce_type(ParseState *pstate, Node *node,
 	return NULL;				/* keep compiler quiet */
 }
 
+static Node *
+coerceUnknownConst(ParseState *pstate, Node *node, Oid inputTypeId,
+				   Oid targetTypeId, int32 targetTypeMod,
+				   CoercionContext ccontext, CoercionForm cformat,
+				   int location)
+{
+	Node	   *result;
+
+	/*
+	 * Input is a string constant with previously undetermined type. Apply the
+	 * target type's typinput function to it to produce a constant of the
+	 * target type.
+	 *
+	 * NOTE: this case cannot be folded together with the other constant-input
+	 * case, since the typinput function does not necessarily behave the same
+	 * as a type conversion function. For example, int4's typinput function
+	 * will reject "1.2", whereas float-to-int type conversion will round to
+	 * integer.
+	 *
+	 * XXX if the typinput function is not immutable, we really ought to
+	 * postpone evaluation of the function call until runtime. But there is no
+	 * way to represent a typinput function call as an expression tree,
+	 * because C-string values are not Datums. (XXX This *is* possible as of
+	 * 7.3, do we want to do it?)
+	 */
+	Const	   *con = (Const *) node;
+	Const	   *newcon = makeNode(Const);
+	Oid			baseTypeId;
+	int32		baseTypeMod;
+	int32		inputTypeMod;
+	Type		baseType;
+	ParseCallbackState pcbstate;
+
+	/*
+	 * If the target type is a domain, we want to call its base type's input
+	 * routine, not domain_in().  This is to avoid premature failure when the
+	 * domain applies a typmod: existing input routines follow
+	 * implicit-coercion semantics for length checks, which is not always what
+	 * we want here.  The needed check will be applied properly inside
+	 * coerce_to_domain().
+	 */
+	baseTypeMod = targetTypeMod;
+	baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
+
+	/*
+	 * For most types we pass typmod -1 to the input routine, because existing
+	 * input routines follow implicit-coercion semantics for length checks,
+	 * which is not always what we want here.  Any length constraint will be
+	 * applied later by our caller.  An exception however is the INTERVAL
+	 * type, for which we *must* pass the typmod or it won't be able to obey
+	 * the bizarre SQL-spec input rules. (Ugly as sin, but so is this part of
+	 * the spec...)
+	 */
+	if (baseTypeId == INTERVALOID)
+		inputTypeMod = baseTypeMod;
+	else
+		inputTypeMod = -1;
+
+	baseType = typeidType(baseTypeId);
+
+	newcon->consttype = baseTypeId;
+	newcon->consttypmod = inputTypeMod;
+	newcon->constcollid = typeTypeCollation(baseType);
+	newcon->constlen = typeLen(baseType);
+	newcon->constbyval = typeByVal(baseType);
+	newcon->constisnull = con->constisnull;
+
+	/*
+	 * We use the original literal's location regardless of the position of
+	 * the coercion.  This is a change from pre-9.2 behavior, meant to
+	 * simplify life for pg_stat_statements.
+	 */
+	newcon->location = con->location;
+
+	/*
+	 * Set up to point at the constant's text if the input routine throws an
+	 * error.
+	 */
+	setup_parser_errposition_callback(&pcbstate, pstate, con->location);
+
+	/*
+	 * We assume here that UNKNOWN's internal representation is the same as
+	 * CSTRING.
+	 */
+	if (!con->constisnull)
+		newcon->constvalue = stringTypeDatum(baseType,
+											 DatumGetCString(con->constvalue),
+											 inputTypeMod);
+	else
+		newcon->constvalue = stringTypeDatum(baseType,
+											 NULL,
+											 inputTypeMod);
+
+	/*
+	 * If it's a varlena value, force it to be in non-expanded (non-toasted)
+	 * format; this avoids any possible dependency on external values and
+	 * improves consistency of representation.
+	 */
+	if (!con->constisnull && newcon->constlen == -1)
+		newcon->constvalue =
+			PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+
+	/*
+	 * For pass-by-reference data types, repeat the conversion to see if the
+	 * input function leaves any uninitialized bytes in the result. We can
+	 * only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is enabled, so
+	 * we don't bother testing otherwise.  The reason we don't want any
+	 * instability in the input function is that comparison of Const nodes
+	 * relies on bytewise comparison of the datums, so if the input function
+	 * leaves garbage then subexpressions that should be identical may not get
+	 * recognized as such.  See pgsql-hackers discussion of 2008-04-04.
+	 */
+	if (!con->constisnull && !newcon->constbyval)
+	{
+		Datum		val2;
+
+		val2 = stringTypeDatum(baseType,
+							   DatumGetCString(con->constvalue),
+							   inputTypeMod);
+		if (newcon->constlen == -1)
+			val2 = PointerGetDatum(PG_DETOAST_DATUM(val2));
+		if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen))
+			elog(WARNING, "type %s has unstable input conversion for \"%s\"",
+				 typeTypeName(baseType), DatumGetCString(con->constvalue));
+	}
+#endif
+
+	cancel_parser_errposition_callback(&pcbstate);
+
+	result = (Node *) newcon;
+
+	/* If target is a domain, apply constraints. */
+	if (baseTypeId != targetTypeId)
+		result = coerce_to_domain(result,
+								  baseTypeId, baseTypeMod,
+								  targetTypeId,
+								  ccontext, cformat, location,
+								  false);
+
+	ReleaseSysCache(baseType);
+
+	return result;
+}
+
 
 /*
  * can_coerce_type()
-- 
2.34.1

