From ea0f465db370cf05901a37606d69a656c4e0f1e6 Mon Sep 17 00:00:00 2001 From: jian he 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