@@ -228,27 +228,30 @@ public RexNode visitIn(In node, CalcitePlanContext context) {
228228 final RexNode field = analyze (node .getField (), context );
229229 final List <RexNode > valueList =
230230 node .getValueList ().stream ().map (value -> analyze (value , context )).toList ();
231+ // When the field is a temporal type, do NOT use leastRestrictive + rexBuilder.makeIn. For a
232+ // temporal field tested against string/date literals, leastRestrictive collapses the common
233+ // type to VARCHAR (the EXPR_DATE / EXPR_TIMESTAMP UDTs are VARCHAR-backed), so makeIn casts the
234+ // field DOWN to VARCHAR and string-compares mismatched renderings (e.g. '2018-06-23 00:00:00'
235+ // against '2018-06-23') — silently matching nothing. Rewrite the membership test as an OR of
236+ // PPL `=` comparisons, the same temporal-aware comparison path visitCompare takes for `=`, so
237+ // each value is coerced to the field's timestamp domain before comparison.
238+ ExprType fieldExprType = OpenSearchTypeFactory .convertRelDataTypeToExprType (field .getType ());
239+ if (TEMPORAL_TYPES .contains (fieldExprType )) {
240+ List <RexNode > equalities =
241+ valueList .stream ()
242+ .map (value -> PPLFuncImpTable .INSTANCE .resolve (context .rexBuilder , "=" , field , value ))
243+ .toList ();
244+ return context .relBuilder .or (equalities );
245+ }
231246 final List <RelDataType > dataTypes =
232- new java . util . ArrayList <>(valueList .stream ().map (RexNode ::getType ).toList ());
247+ new ArrayList <>(valueList .stream ().map (RexNode ::getType ).toList ());
233248 dataTypes .add (field .getType ());
234249 RelDataType commonType = context .rexBuilder .getTypeFactory ().leastRestrictive (dataTypes );
235250 if (commonType != null ) {
236251 List <RexNode > newValueList =
237252 valueList .stream ().map (value -> context .rexBuilder .makeCast (commonType , value )).toList ();
238253 return context .rexBuilder .makeIn (field , newValueList );
239254 }
240- // leastRestrictive() has no common type for mixed temporal representations — e.g. a standard
241- // Calcite TIMESTAMP field tested against EXPR_DATE UDT values (`ts in (date('...'), ...)`).
242- // Mirror visitBetween: widen all-temporal operands through CoercionUtils so the comparison
243- // runs, while leaving genuinely incompatible mixes to raise SemanticCheckException below.
244- List <RexNode > operands = new ArrayList <>(valueList );
245- operands .add (field );
246- List <RexNode > widened = widenTemporalOperands (context , operands );
247- if (widened != null ) {
248- RexNode widenedField = widened .get (widened .size () - 1 );
249- List <RexNode > widenedValues = widened .subList (0 , widened .size () - 1 );
250- return context .rexBuilder .makeIn (widenedField , widenedValues );
251- }
252255 List <ExprType > exprTypes =
253256 dataTypes .stream ().map (OpenSearchTypeFactory ::convertRelDataTypeToExprType ).toList ();
254257 throw new SemanticCheckException (
@@ -257,6 +260,24 @@ public RexNode visitIn(In node, CalcitePlanContext context) {
257260 exprTypes .getLast (), exprTypes .subList (0 , exprTypes .size () - 1 )));
258261 }
259262
263+ private static final Set <ExprType > TEMPORAL_TYPES =
264+ Set .of (ExprCoreType .DATE , ExprCoreType .TIME , ExprCoreType .TIMESTAMP );
265+
266+ @ Override
267+ public RexNode visitCompare (Compare node , CalcitePlanContext context ) {
268+ RexNode left = analyze (node .getLeft (), context );
269+ RexNode right = analyze (node .getRight (), context );
270+ String op = node .getOperator ();
271+ // Handle boolean_field != literal -> IS_NOT_TRUE/IS_NOT_FALSE
272+ if ("!=" .equals (op ) || "<>" .equals (op )) {
273+ RexNode result = tryMakeBooleanNotEquals (left , right , context );
274+ if (result != null ) {
275+ return result ;
276+ }
277+ }
278+ return PPLFuncImpTable .INSTANCE .resolve (context .rexBuilder , op , left , right );
279+ }
280+
260281 /**
261282 * Widens a set of operands to a common temporal type when, and only when, every operand is a
262283 * temporal type (DATE / TIME / TIMESTAMP), including the EXPR_DATE / EXPR_TIME / EXPR_TIMESTAMP
@@ -276,24 +297,6 @@ public RexNode visitIn(In node, CalcitePlanContext context) {
276297 return CoercionUtils .widenArguments (context .rexBuilder , operands );
277298 }
278299
279- private static final Set <ExprType > TEMPORAL_TYPES =
280- Set .of (ExprCoreType .DATE , ExprCoreType .TIME , ExprCoreType .TIMESTAMP );
281-
282- @ Override
283- public RexNode visitCompare (Compare node , CalcitePlanContext context ) {
284- RexNode left = analyze (node .getLeft (), context );
285- RexNode right = analyze (node .getRight (), context );
286- String op = node .getOperator ();
287- // Handle boolean_field != literal -> IS_NOT_TRUE/IS_NOT_FALSE
288- if ("!=" .equals (op ) || "<>" .equals (op )) {
289- RexNode result = tryMakeBooleanNotEquals (left , right , context );
290- if (result != null ) {
291- return result ;
292- }
293- }
294- return PPLFuncImpTable .INSTANCE .resolve (context .rexBuilder , op , left , right );
295- }
296-
297300 @ Override
298301 public RexNode visitBetween (Between node , CalcitePlanContext context ) {
299302 RexNode value = analyze (node .getValue (), context );
0 commit comments