Fix: Prevent integer overflow in ConstantOperator distance calculation
Co-authored-by: huanmingwong <huanmingwong@gmail.com>
This commit is contained in:
parent
d597f92555
commit
6ce2895b46
|
|
@ -677,11 +677,11 @@ public final class ConstantOperator extends ScalarOperator implements Comparable
|
|||
|
||||
public long distance(ConstantOperator other) {
|
||||
if (type.isTinyint()) {
|
||||
return other.getTinyInt() - getTinyInt();
|
||||
return (long) other.getTinyInt() - (long) getTinyInt();
|
||||
} else if (type.isSmallint()) {
|
||||
return other.getSmallint() - getSmallint();
|
||||
return (long) other.getSmallint() - (long) getSmallint();
|
||||
} else if (type.isInt()) {
|
||||
return other.getInt() - getInt();
|
||||
return (long) other.getInt() - (long) getInt();
|
||||
} else if (type.isBigint()) {
|
||||
return other.getBigint() - getBigint();
|
||||
} else if (type.isLargeint()) {
|
||||
|
|
|
|||
|
|
@ -158,6 +158,12 @@ public class ConstantOperatorTest {
|
|||
ConstantOperator var2 = ConstantOperator.createTinyInt((byte) 20);
|
||||
Assertions.assertEquals(10, var1.distance(var2));
|
||||
Assertions.assertEquals(-10, var2.distance(var1));
|
||||
|
||||
// tinyint edge cases
|
||||
ConstantOperator tinyMax = ConstantOperator.createTinyInt(Byte.MAX_VALUE);
|
||||
ConstantOperator tinyMin = ConstantOperator.createTinyInt(Byte.MIN_VALUE);
|
||||
Assertions.assertEquals(255L, tinyMax.distance(tinyMin));
|
||||
Assertions.assertEquals(-255L, tinyMin.distance(tinyMax));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -166,6 +172,12 @@ public class ConstantOperatorTest {
|
|||
ConstantOperator var2 = ConstantOperator.createSmallInt((short) 20);
|
||||
Assertions.assertEquals(10, var1.distance(var2));
|
||||
Assertions.assertEquals(-10, var2.distance(var1));
|
||||
|
||||
// smallint edge cases
|
||||
ConstantOperator smallMax = ConstantOperator.createSmallInt(Short.MAX_VALUE);
|
||||
ConstantOperator smallMin = ConstantOperator.createSmallInt(Short.MIN_VALUE);
|
||||
Assertions.assertEquals(65535L, smallMax.distance(smallMin));
|
||||
Assertions.assertEquals(-65535L, smallMin.distance(smallMax));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -176,6 +188,27 @@ public class ConstantOperatorTest {
|
|||
Assertions.assertEquals(-10, var2.distance(var1));
|
||||
}
|
||||
|
||||
{
|
||||
// int edge cases - test for integer overflow fix
|
||||
ConstantOperator intMax = ConstantOperator.createInt(Integer.MAX_VALUE);
|
||||
ConstantOperator intMin = ConstantOperator.createInt(Integer.MIN_VALUE);
|
||||
ConstantOperator testValue = ConstantOperator.createInt(1234567890);
|
||||
ConstantOperator zero = ConstantOperator.createInt(0);
|
||||
|
||||
// Test case that previously caused overflow: INT_MAX - INT_MIN should be 4294967295
|
||||
Assertions.assertEquals(4294967295L, intMax.distance(intMin));
|
||||
Assertions.assertEquals(-4294967295L, intMin.distance(intMax));
|
||||
|
||||
// Test case from issue #63669: test_value - INT_MIN should be 3382051538
|
||||
Assertions.assertEquals(3382051538L, testValue.distance(intMin));
|
||||
|
||||
// Test case: INT_MAX - test_value should be 912915757
|
||||
Assertions.assertEquals(912915757L, intMax.distance(testValue));
|
||||
|
||||
// Test case: zero - INT_MIN should be 2147483648
|
||||
Assertions.assertEquals(2147483648L, zero.distance(intMin));
|
||||
}
|
||||
|
||||
{
|
||||
// long
|
||||
ConstantOperator var1 = ConstantOperator.createBigint(10);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed 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
|
||||
//
|
||||
// https://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 com.starrocks.sql.optimizer.rule.transformation.materialization;
|
||||
|
||||
import com.starrocks.catalog.Type;
|
||||
import com.starrocks.sql.ast.expression.BinaryType;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.BinaryPredicateOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ConstantOperator;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Test case for issue #63669: FE Assertion Failure when querying on int32 column in WHERE clause
|
||||
*/
|
||||
public class Int32PredicateTest {
|
||||
|
||||
@Test
|
||||
public void testInt32PredicateWithLargeValue() {
|
||||
// Reproduce the issue scenario: WHERE collect_api_receive_time = 1234567890
|
||||
ColumnRefOperator columnRef = new ColumnRefOperator(1, Type.INT, "collect_api_receive_time", true);
|
||||
ConstantOperator value = ConstantOperator.createInt(1234567890);
|
||||
|
||||
// Create a binary predicate: collect_api_receive_time = 1234567890
|
||||
BinaryPredicateOperator pred = new BinaryPredicateOperator(BinaryType.EQ, columnRef, value);
|
||||
|
||||
// This should not throw an assertion failure anymore
|
||||
Assertions.assertNotNull(pred);
|
||||
Assertions.assertEquals(BinaryType.EQ, pred.getBinaryType());
|
||||
Assertions.assertEquals(columnRef, pred.getChild(0));
|
||||
Assertions.assertEquals(value, pred.getChild(1));
|
||||
|
||||
// Test the distance calculation that was causing the issue
|
||||
ConstantOperator intMin = ConstantOperator.createInt(Integer.MIN_VALUE);
|
||||
long distance = value.distance(intMin);
|
||||
Assertions.assertEquals(3382051538L, distance);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt32PredicateExtraction() {
|
||||
// Test predicate extraction which involves range canonicalization
|
||||
ColumnRefOperator columnRef = new ColumnRefOperator(1, Type.INT, "test_column", true);
|
||||
ConstantOperator value = ConstantOperator.createInt(1234567890);
|
||||
|
||||
// Create equality predicate
|
||||
BinaryPredicateOperator eqPred = new BinaryPredicateOperator(BinaryType.EQ, columnRef, value);
|
||||
|
||||
// Create greater than predicate
|
||||
BinaryPredicateOperator gtPred = new BinaryPredicateOperator(BinaryType.GT, columnRef, value);
|
||||
|
||||
// Create less than predicate
|
||||
BinaryPredicateOperator ltPred = new BinaryPredicateOperator(BinaryType.LT, columnRef, value);
|
||||
|
||||
// These should not cause assertion failures
|
||||
PredicateExtractor extractor = new PredicateExtractor();
|
||||
PredicateExtractor.PredicateExtractorContext context = new PredicateExtractor.PredicateExtractorContext();
|
||||
|
||||
try {
|
||||
RangePredicate eqRange = extractor.visitBinaryPredicate(eqPred, context);
|
||||
RangePredicate gtRange = extractor.visitBinaryPredicate(gtPred, context);
|
||||
RangePredicate ltRange = extractor.visitBinaryPredicate(ltPred, context);
|
||||
|
||||
Assertions.assertNotNull(eqRange);
|
||||
Assertions.assertNotNull(gtRange);
|
||||
Assertions.assertNotNull(ltRange);
|
||||
} catch (Exception e) {
|
||||
Assertions.fail("Predicate extraction should not throw exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt32EdgeCasesInPredicates() {
|
||||
// Test edge cases that could cause overflow in distance calculation
|
||||
ColumnRefOperator columnRef = new ColumnRefOperator(1, Type.INT, "test_column", true);
|
||||
|
||||
// Test with INT_MAX
|
||||
ConstantOperator intMax = ConstantOperator.createInt(Integer.MAX_VALUE);
|
||||
BinaryPredicateOperator maxPred = new BinaryPredicateOperator(BinaryType.EQ, columnRef, intMax);
|
||||
|
||||
// Test with INT_MIN
|
||||
ConstantOperator intMin = ConstantOperator.createInt(Integer.MIN_VALUE);
|
||||
BinaryPredicateOperator minPred = new BinaryPredicateOperator(BinaryType.EQ, columnRef, intMin);
|
||||
|
||||
// Test with the specific value from the issue
|
||||
ConstantOperator issueValue = ConstantOperator.createInt(1234567890);
|
||||
BinaryPredicateOperator issuePred = new BinaryPredicateOperator(BinaryType.EQ, columnRef, issueValue);
|
||||
|
||||
PredicateExtractor extractor = new PredicateExtractor();
|
||||
PredicateExtractor.PredicateExtractorContext context = new PredicateExtractor.PredicateExtractorContext();
|
||||
|
||||
try {
|
||||
RangePredicate maxRange = extractor.visitBinaryPredicate(maxPred, context);
|
||||
RangePredicate minRange = extractor.visitBinaryPredicate(minPred, context);
|
||||
RangePredicate issueRange = extractor.visitBinaryPredicate(issuePred, context);
|
||||
|
||||
Assertions.assertNotNull(maxRange);
|
||||
Assertions.assertNotNull(minRange);
|
||||
Assertions.assertNotNull(issueRange);
|
||||
} catch (Exception e) {
|
||||
Assertions.fail("Edge case predicate extraction should not throw exception: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue