Copilot commented on code in PR #16917:
URL: https://github.com/apache/iotdb/pull/16917#discussion_r2634077513


##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java:
##########
@@ -77,6 +81,10 @@
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.predicate.PredicatePushIntoScanChecker.isSymbolReference;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isExtractTimeColumn;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.GlobalTimePredicateExtractVisitor.isTimeColumn;
+import static org.apache.tsfile.enums.TSDataType.FLOAT;

Review Comment:
   The FLOAT import is unused in this file. While FLOAT is referenced in the 
getValue method at line 350, it doesn't need to be statically imported since 
it's accessed via dataType.getTypeEnum(). Consider removing this unused static 
import to keep the imports clean.
   ```suggestion
   
   ```



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/expression/visitor/predicate/ConvertPredicateToFilterVisitor.java:
##########
@@ -265,7 +273,131 @@ private <T extends Comparable<T>> Filter 
constructCompareFilter(
       Context context) {
     PartialPath path = ((TimeSeriesOperand) timeseriesOperand).getPath();
     int measurementIndex = context.getMeasurementIndex(path.getMeasurement());
-    TSDataType dataType = context.getType(path);
+    TSDataType columnDataType = context.getType(path);
+
+    ConstantOperand constantLiteral = (ConstantOperand) constantOperand;
+    TSDataType constantDataType = constantLiteral.getDataType();
+    String constantString = constantLiteral.getValueString();
+    if (constantDataType == DOUBLE && NumberUtils.isCreatable(constantString)) 
{
+      BigDecimal constantDecimal = new BigDecimal(constantString);
+
+      if (columnDataType == INT64) {
+        if (constantDecimal.compareTo(new BigDecimal(Long.MAX_VALUE)) > 0) {
+          return constructFilterForGreaterThanMax(expressionType, 
measurementIndex);
+        }
+        if (constantDecimal.compareTo(new BigDecimal(Long.MIN_VALUE)) < 0) {
+          return constructFilterForLessThanMin(expressionType, 
measurementIndex);
+        }
+        return constructFilterFromDouble(
+            expressionType, constantOperand, measurementIndex, columnDataType);
+
+      } else if (columnDataType == INT32) {
+        if (constantDecimal.compareTo(new BigDecimal(Integer.MAX_VALUE)) > 0) {
+          return constructFilterForGreaterThanMax(expressionType, 
measurementIndex);
+        }
+        if (constantDecimal.compareTo(new BigDecimal(Integer.MIN_VALUE)) < 0) {
+          return constructFilterForLessThanMin(expressionType, 
measurementIndex);
+        }
+        return constructFilterFromDouble(
+            expressionType, constantOperand, measurementIndex, columnDataType);
+      }
+    }

Review Comment:
   The code uses NumberUtils.isCreatable() to validate the constant string, but 
this check is insufficient for handling special double values. 
NumberUtils.isCreatable() returns true for strings like "Infinity", 
"-Infinity", and "NaN", which would then be parsed into BigDecimal and cause 
issues. BigDecimal constructor throws NumberFormatException for these special 
values, but they should be handled explicitly. Consider adding specific checks 
for these cases or handling the NumberFormatException properly to avoid 
unexpected behavior.



##########
iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/expression/predicate/TreePredicateConversionTest.java:
##########
@@ -0,0 +1,572 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.queryengine.expression.predicate;
+
+import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.queryengine.plan.analyze.PredicateUtils;
+import org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
+import org.apache.iotdb.db.queryengine.plan.expression.Expression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.EqualToExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.GreaterEqualExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.GreaterThanExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LessEqualExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.LessThanExpression;
+import 
org.apache.iotdb.db.queryengine.plan.expression.binary.NonEqualExpression;
+import org.apache.iotdb.db.queryengine.plan.expression.leaf.ConstantOperand;
+import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.filter.basic.Filter;
+import org.apache.tsfile.read.filter.operator.FalseLiteralFilter;
+import org.apache.tsfile.read.filter.operator.LongFilterOperators;
+import org.apache.tsfile.read.filter.operator.ValueIsNotNullOperator;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+
+public class TreePredicateConversionTest {
+
+  Expression int64Column = new TimeSeriesOperand(new 
PartialPath("root.sg.d1.Int64Column"));
+  Expression int32Column = new TimeSeriesOperand(new 
PartialPath("root.sg.d1.Int32Column"));
+
+  public TreePredicateConversionTest() throws IllegalPathException {}
+
+  private TypeProvider getMockTypeProvider(TSDataType dataType) {
+    TypeProvider mockTypeProvider = Mockito.mock(TypeProvider.class);
+    
Mockito.when(mockTypeProvider.getTreeModelType(Mockito.anyString())).thenReturn(dataType);
+    return mockTypeProvider;
+  }
+
+  @Test
+  public void testDoubleLiteralAndInt64Column() {
+    Expression doubleConstant = new ConstantOperand(TSDataType.DOUBLE, 
"123.3");
+
+    // 1. GreaterEqual (>=): int64 >= 123.3 -> int64 >= 124 (ceil)
+    // Result: ValueGtEq(124)
+    Expression greaterEqualExpression = new 
GreaterEqualExpression(int64Column, doubleConstant);
+    Filter actualFilter1 =
+        PredicateUtils.convertPredicateToFilter(
+            greaterEqualExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueGtEq(0, 124), 
actualFilter1);
+
+    // 2. LessEqual (<=): int64 <= 123.3 -> int64 <= 123 (floor)
+    // Result: ValueLtEq(123)
+    LessEqualExpression lessEqualExpression = new 
LessEqualExpression(int64Column, doubleConstant);
+    Filter actualFilter2 =
+        PredicateUtils.convertPredicateToFilter(
+            lessEqualExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueLtEq(0, 123), 
actualFilter2);
+
+    // 3. GreaterThan (>): int64 > 123.3 -> int64 > 123 (floor)
+    // Result: ValueGt(123)
+    GreaterThanExpression greaterThanExpression =
+        new GreaterThanExpression(int64Column, doubleConstant);
+    Filter actualFilter3 =
+        PredicateUtils.convertPredicateToFilter(
+            greaterThanExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueGt(0, 123), 
actualFilter3);
+
+    // 4. LessThan (<): int64 < 123.3 -> int64 < 124 (ceil)
+    // Result: ValueLt(124)
+    LessThanExpression lessThanExpression = new 
LessThanExpression(int64Column, doubleConstant);
+    Filter actualFilter4 =
+        PredicateUtils.convertPredicateToFilter(
+            lessThanExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueLt(0, 124), 
actualFilter4);
+
+    // 5. EqualTo (=): int64 = 123.3 -> Impossible for int64
+    // Result: FalseLiteralFilter (Always False)
+    EqualToExpression equalToExpression = new EqualToExpression(int64Column, 
doubleConstant);
+    Filter actualFilter5 =
+        PredicateUtils.convertPredicateToFilter(
+            equalToExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new FalseLiteralFilter(), actualFilter5);
+
+    // 6. NonEqual (!=): int64 != 123.3
+    // Result: ValueIsNotNullOperator (Always True, as long as value exists)
+    NonEqualExpression nonEqualExpression = new 
NonEqualExpression(int64Column, doubleConstant);
+    Filter actualFilter6 =
+        PredicateUtils.convertPredicateToFilter(
+            nonEqualExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new ValueIsNotNullOperator(0), actualFilter6);
+  }
+
+  @Test
+  public void testNegativeDoubleLiteralAndInt64Column() {
+    Expression doubleConstant = new ConstantOperand(TSDataType.DOUBLE, 
"-123.3");
+
+    // 1. GreaterEqual (>=): int64 >= -123.3 -> int64 >= -123 (ceil)
+    // Result: ValueGtEq(-123)
+    Expression greaterEqualExpression = new 
GreaterEqualExpression(int64Column, doubleConstant);
+    Filter actualFilter1 =
+        PredicateUtils.convertPredicateToFilter(
+            greaterEqualExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueGtEq(0, -123), 
actualFilter1);
+
+    // 2. LessEqual (<=): int64 <= -123.3 -> int64 <= -124 (floor)
+    // Result: ValueLtEq(-124)
+    LessEqualExpression lessEqualExpression = new 
LessEqualExpression(int64Column, doubleConstant);
+    Filter actualFilter2 =
+        PredicateUtils.convertPredicateToFilter(
+            lessEqualExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueLtEq(0, -124), 
actualFilter2);
+
+    // 3. GreaterThan (>): int64 > -123.3 -> int64 > -124 (floor)
+    // Result: ValueGt(-124)
+    GreaterThanExpression greaterThanExpression =
+        new GreaterThanExpression(int64Column, doubleConstant);
+    Filter actualFilter3 =
+        PredicateUtils.convertPredicateToFilter(
+            greaterThanExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueGt(0, -124), 
actualFilter3);
+
+    // 4. LessThan (<): int64 < -123.3 -> int64 < -123 (ceil)
+    // Result: ValueLt(-123)
+    LessThanExpression lessThanExpression = new 
LessThanExpression(int64Column, doubleConstant);
+    Filter actualFilter4 =
+        PredicateUtils.convertPredicateToFilter(
+            lessThanExpression,
+            Collections.singletonList("Int64Column"),
+            false,
+            getMockTypeProvider(TSDataType.INT64),
+            null);
+    Assert.assertEquals(new LongFilterOperators.ValueLt(0, -123), 
actualFilter4);
+
+    // 5. EqualTo (=): int64 = -123.3 -> Impossible for int64
+    // Result:FalseLiteralFilter(Always False)

Review Comment:
   There's a spacing issue in the comment. "Result:FalseLiteralFilter(Always 
False)" should have a space after the colon for consistency with other comments 
in the file (e.g., line 180).
   ```suggestion
       // Result: FalseLiteralFilter(Always False)
   ```



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java:
##########
@@ -155,9 +163,117 @@ public static <T extends Comparable<T>> Filter 
constructCompareFilter(
 
     int measurementIndex = 
context.getMeasurementIndex(symbolReference.getName());
     Type type = context.getType(Symbol.from(symbolReference));
+    TSDataType columnDataType = InternalTypeManager.getTSDataType(type);
+
+    // when literal is the doubleLiteral type and the columnDataType is INT64 
or INT32,
+    // the doubleLiteral has to be converted.
+    if (literal instanceof DoubleLiteral) {
+      DoubleLiteral doubleLiteral = (DoubleLiteral) literal;
+      double doubleLiteralValue = doubleLiteral.getValue();
+
+      if (columnDataType == INT64) {
+        if (doubleLiteralValue > Long.MAX_VALUE) {
+          return constructFilterForGreaterThanMax(operator, measurementIndex);
+        }
+        if (doubleLiteralValue < Long.MIN_VALUE) {
+          return constructFilterForLessThanMin(operator, measurementIndex);
+        }
+        return constructFilterFromDouble(operator, doubleLiteralValue, 
measurementIndex, type);
+
+      } else if (columnDataType == INT32) {
+        if (doubleLiteralValue > Integer.MAX_VALUE) {
+          return constructFilterForGreaterThanMax(operator, measurementIndex);
+        }
+
+        if (doubleLiteralValue < Integer.MIN_VALUE) {
+          return constructFilterForLessThanMin(operator, measurementIndex);
+        }
+        return constructFilterFromDouble(operator, doubleLiteralValue, 
measurementIndex, type);
+      }
+    }

Review Comment:
   The code doesn't handle special double values like NaN and Infinity. When 
doubleLiteralValue is NaN, comparisons with Long.MAX_VALUE/Long.MIN_VALUE at 
lines 175, 178, 184, 188 will always return false, and NaN will be passed to 
Math.ceil/floor which returns NaN, leading to incorrect filter construction. 
Similarly, positive/negative infinity should be explicitly checked. Consider 
adding checks at the beginning: if (Double.isNaN(doubleLiteralValue)) throw new 
IllegalArgumentException() or return appropriate filter, and if 
(Double.isInfinite(doubleLiteralValue)) handle overflow cases directly.



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/predicate/ConvertPredicateToFilterVisitor.java:
##########
@@ -155,9 +163,117 @@ public static <T extends Comparable<T>> Filter 
constructCompareFilter(
 
     int measurementIndex = 
context.getMeasurementIndex(symbolReference.getName());
     Type type = context.getType(Symbol.from(symbolReference));
+    TSDataType columnDataType = InternalTypeManager.getTSDataType(type);
+
+    // when literal is the doubleLiteral type and the columnDataType is INT64 
or INT32,
+    // the doubleLiteral has to be converted.
+    if (literal instanceof DoubleLiteral) {
+      DoubleLiteral doubleLiteral = (DoubleLiteral) literal;
+      double doubleLiteralValue = doubleLiteral.getValue();
+
+      if (columnDataType == INT64) {
+        if (doubleLiteralValue > Long.MAX_VALUE) {
+          return constructFilterForGreaterThanMax(operator, measurementIndex);
+        }
+        if (doubleLiteralValue < Long.MIN_VALUE) {
+          return constructFilterForLessThanMin(operator, measurementIndex);
+        }
+        return constructFilterFromDouble(operator, doubleLiteralValue, 
measurementIndex, type);
+
+      } else if (columnDataType == INT32) {
+        if (doubleLiteralValue > Integer.MAX_VALUE) {
+          return constructFilterForGreaterThanMax(operator, measurementIndex);
+        }
+
+        if (doubleLiteralValue < Integer.MIN_VALUE) {
+          return constructFilterForLessThanMin(operator, measurementIndex);
+        }
+        return constructFilterFromDouble(operator, doubleLiteralValue, 
measurementIndex, type);
+      }
+    }

Review Comment:
   The implementation uses DoubleMath.isMathematicalInteger() to check if a 
double is an integer (line 221), while the tree model implementation 
(ConvertPredicateToFilterVisitor in the expression/visitor/predicate package) 
uses BigDecimal.remainder(BigDecimal.ONE).signum() == 0 for the same purpose 
(line 342). This inconsistency makes the codebase harder to maintain. Consider 
using the same approach in both places, preferably 
DoubleMath.isMathematicalInteger() as it's more concise and handles edge cases 
like NaN correctly.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to