…ensearch-project#5175)
When unresolved field names (including the literal `null`) fall back to
null literals inside COALESCE, QualifiedNameResolver was creating them
with SqlTypeName.VARCHAR instead of SqlTypeName.NULL. This caused
leastRestrictive to widen [VARCHAR, INTEGER] to VARCHAR, producing
string output for what should be an integer result.
Changes:
- QualifiedNameResolver: use SqlTypeName.NULL for unresolved-field null
literals in COALESCE, so leastRestrictive correctly picks the
non-null operand's type.
- EnhancedCoalesceFunction: add fallback to VARCHAR when
leastRestrictive returns NULL type (all-null-operands edge case).
- Unit tests in OpenSearchTypeFactoryTest for leastRestrictive with
NULL type.
- Unit tests in CalcitePPLEnhancedCoalesceTest asserting INTEGER
return type for COALESCE(null, 42) and COALESCE(42, null).
- Integration tests in CalcitePPLEnhancedCoalesceIT for null-literal
and nonexistent-field variants.
- YAML REST test (issues/5175.yml) covering the reported scenarios.
Signed-off-by: Heng Qian <qianheng@amazon.com>
Summary
COALESCE(null, 42)returningstringtype instead ofintby usingSqlTypeName.NULLinstead ofSqlTypeName.VARCHARfor unresolved-field null literals inQualifiedNameResolver.replaceWithNullLiteralInCoalesce()EnhancedCoalesceFunction.getReturnTypeInference()when all operands are NULL (leastRestrictive returns NULL type)Resolves #5175
Root Cause
In PPL,
nullis parsed as aQualifiedName(field name), not a literal. When COALESCE encounters an unresolvable field,QualifiedNameResolver.replaceWithNullLiteralInCoalesce()creates a null literal typed asVARCHARinstead ofNULL. This causesleastRestrictive([VARCHAR, INTEGER])to returnVARCHAR, producing string output for what should be an integer result.Test plan
CalcitePPLEnhancedCoalesceTest- new tests forCOALESCE(null, 42)andCOALESCE(42, null)asserting INTEGER typeOpenSearchTypeFactoryTest- new tests forleastRestrictivewith NULL typeCalcitePPLEnhancedCoalesceTestplans fromnull:VARCHARtonull:NULLCalcitePPLEnhancedCoalesceIT- new tests for null literal + integer, integer + null literal, nonexistent field + integerissues/5175.yml- coversCOALESCE(null, 42),COALESCE(42, null),COALESCE(nonexistent_field, 42), andCOALESCE(null, 3.14)