Skip to content

Commit 029fbed

Browse files
authored
fixed the bug that caused incompatible behavior when comparing int64 or int32 column with double literals in query filter conditions. (apache#16917)
1 parent b67f57c commit 029fbed

File tree

7 files changed

+1999
-3
lines changed

7 files changed

+1999
-3
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.db.it.query;
21+
22+
import org.apache.iotdb.it.env.EnvFactory;
23+
import org.apache.iotdb.it.framework.IoTDBTestRunner;
24+
import org.apache.iotdb.itbase.category.ClusterIT;
25+
import org.apache.iotdb.itbase.category.LocalStandaloneIT;
26+
27+
import org.junit.AfterClass;
28+
import org.junit.BeforeClass;
29+
import org.junit.Test;
30+
import org.junit.experimental.categories.Category;
31+
import org.junit.runner.RunWith;
32+
33+
import java.sql.Connection;
34+
import java.sql.Statement;
35+
36+
import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
37+
import static org.junit.Assert.fail;
38+
39+
@RunWith(IoTDBTestRunner.class)
40+
@Category({LocalStandaloneIT.class, ClusterIT.class})
41+
public class IoTDBPredicateConversionTreeIT {
42+
43+
private static final String DATABASE_NAME = "root.test_pred";
44+
private static final String DEVICE_ID = DATABASE_NAME + ".d1";
45+
46+
private static final String[] SQLs =
47+
new String[] {
48+
"CREATE DATABASE " + DATABASE_NAME,
49+
"CREATE TIMESERIES " + DEVICE_ID + ".int32_col WITH DATATYPE=INT32, ENCODING=PLAIN",
50+
"CREATE TIMESERIES " + DEVICE_ID + ".int64_col WITH DATATYPE=INT64, ENCODING=PLAIN",
51+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(1, 20, 20)",
52+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(2, 29, 29)",
53+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(3, 30, 30)",
54+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(4, 31, 31)",
55+
"INSERT INTO " + DEVICE_ID + "(time, int64_col) VALUES(5, 35)",
56+
"INSERT INTO " + DEVICE_ID + "(time, int32_col) VALUES(6, 35)",
57+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(7, null, null)",
58+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(8, 1000, 1000)",
59+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(9, -29, -29)",
60+
"INSERT INTO " + DEVICE_ID + "(time, int32_col, int64_col) VALUES(10, -30, -30)"
61+
};
62+
63+
@BeforeClass
64+
public static void setUp() throws Exception {
65+
EnvFactory.getEnv().initClusterEnvironment();
66+
67+
importData();
68+
}
69+
70+
@AfterClass
71+
public static void tearDown() throws Exception {
72+
EnvFactory.getEnv().cleanClusterEnvironment();
73+
}
74+
75+
private static void importData() {
76+
try (Connection connection = EnvFactory.getEnv().getConnection();
77+
Statement statement = connection.createStatement()) {
78+
79+
for (String sql : SQLs) {
80+
statement.execute(sql);
81+
}
82+
} catch (Exception e) {
83+
e.printStackTrace();
84+
fail(e.getMessage());
85+
}
86+
}
87+
88+
@Test
89+
public void testDoubleLiteralAndInt32Column() {
90+
// Common header definition for Tree Model
91+
String expectedHeader = "Time," + DEVICE_ID + ".int32_col,";
92+
93+
// ----------------------------------------------------------------------
94+
// Group 1: Positive boundary 29.1
95+
// ----------------------------------------------------------------------
96+
97+
// TC-1: int32_col >= 29.1
98+
// Logic: Ceil(29.1) = 30 -> int32_col >= 30
99+
// Expected result: Row 3(30), Row 4(31), Row 6(35), Row 8(1000)
100+
String[] retArrayGtEqPositive = {"3,30,", "4,31,", "6,35,", "8,1000,"};
101+
resultSetEqualTest(
102+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col >= 29.1",
103+
expectedHeader,
104+
retArrayGtEqPositive);
105+
106+
// TC-2: int32_col > 29.1
107+
// Logic: Floor(29.1) = 29 -> int32_col > 29 -> equivalent to int32_col >= 30
108+
// Expected result: Same as above
109+
resultSetEqualTest(
110+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col > 29.1",
111+
expectedHeader,
112+
retArrayGtEqPositive);
113+
114+
// TC-3: int32_col <= 29.1
115+
// Logic: Floor(29.1) = 29 -> int32_col <= 29
116+
// Expected result: Row 1(20), Row 2(29), Row 9(-29), Row 10(-30)
117+
String[] retArrayLtEqPositive = {"1,20,", "2,29,", "9,-29,", "10,-30,"};
118+
resultSetEqualTest(
119+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col <= 29.1",
120+
expectedHeader,
121+
retArrayLtEqPositive);
122+
123+
// TC-4: int32_col < 29.1
124+
// Logic: Ceil(29.1) = 30 -> int32_col < 30 -> equivalent to int32_col <= 29
125+
// Expected result: Same as above
126+
resultSetEqualTest(
127+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col < 29.1",
128+
expectedHeader,
129+
retArrayLtEqPositive);
130+
131+
// TC-5: int32_col = 29.1
132+
// Logic: Integer values can never equal a decimal fraction -> Empty result
133+
String[] retArrayEmpty = {};
134+
resultSetEqualTest(
135+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col = 29.1",
136+
expectedHeader,
137+
retArrayEmpty);
138+
139+
// TC-6: int32_col != 29.1
140+
// Logic: Integer values are never equal to a decimal fraction -> Return all non-null rows
141+
// Expected result: All rows with int32 data (Row 5 and Row 7 are null, excluded)
142+
String[] retArrayNotEq = {
143+
"1,20,", "2,29,", "3,30,", "4,31,", "6,35,", "8,1000,", "9,-29,", "10,-30,"
144+
};
145+
resultSetEqualTest(
146+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col != 29.1",
147+
expectedHeader,
148+
retArrayNotEq);
149+
}
150+
151+
@Test
152+
public void testDoubleLiteralAndInt64Column() {
153+
// Common header definition for Tree Model
154+
String expectedHeader = "Time," + DEVICE_ID + ".int64_col,";
155+
156+
// ----------------------------------------------------------------------
157+
// Group 1: Positive boundary 29.1
158+
// ----------------------------------------------------------------------
159+
160+
// TC-1: int64_col >= 29.1
161+
// Logic: Ceil(29.1) = 30 -> int64_col >= 30
162+
// Expected result: Row 3(30), Row 4(31), Row 5(35), Row 8(1000)
163+
// Note: Row 5 (35) is included here (it is int64). Row 6 is excluded (it is int32).
164+
String[] retArrayGtEqPositive = {"3,30,", "4,31,", "5,35,", "8,1000,"};
165+
resultSetEqualTest(
166+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col >= 29.1",
167+
expectedHeader,
168+
retArrayGtEqPositive);
169+
170+
// TC-2: int64_col > 29.1
171+
// Logic: Floor(29.1) = 29 -> int64_col > 29 -> equivalent to int64_col >= 30
172+
// Expected result: Same as above
173+
resultSetEqualTest(
174+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col > 29.1",
175+
expectedHeader,
176+
retArrayGtEqPositive);
177+
178+
// TC-3: int64_col <= 29.1
179+
// Logic: Floor(29.1) = 29 -> int64_col <= 29
180+
// Expected result: Row 1(20), Row 2(29), Row 9(-29), Row 10(-30)
181+
String[] retArrayLtEqPositive = {"1,20,", "2,29,", "9,-29,", "10,-30,"};
182+
resultSetEqualTest(
183+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col <= 29.1",
184+
expectedHeader,
185+
retArrayLtEqPositive);
186+
187+
// TC-4: int64_col < 29.1
188+
// Logic: Ceil(29.1) = 30 -> int64_col < 30 -> equivalent to int64_col <= 29
189+
// Expected result: Same as above
190+
resultSetEqualTest(
191+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col < 29.1",
192+
expectedHeader,
193+
retArrayLtEqPositive);
194+
195+
// TC-5: int64_col = 29.1
196+
// Logic: Long values can never equal a decimal fraction -> Empty result
197+
String[] retArrayEmpty = {};
198+
resultSetEqualTest(
199+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col = 29.1",
200+
expectedHeader,
201+
retArrayEmpty);
202+
203+
// TC-6: int64_col != 29.1
204+
// Logic: Long values are never equal to a decimal fraction -> Return all non-null rows
205+
// Expected result: All rows with int64 data (Row 6 and Row 7 are null, excluded)
206+
String[] retArrayNotEq = {
207+
"1,20,", "2,29,", "3,30,", "4,31,", "5,35,", "8,1000,", "9,-29,", "10,-30,"
208+
};
209+
resultSetEqualTest(
210+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col != 29.1",
211+
expectedHeader,
212+
retArrayNotEq);
213+
214+
// ----------------------------------------------------------------------
215+
// Group 2: Negative boundary -29.1
216+
// ----------------------------------------------------------------------
217+
218+
// TC-7: int64_col <= -29.1
219+
// Logic: Floor(-29.1) = -30 -> int64_col <= -30
220+
// Expected result: Row 10(-30)
221+
String[] retArrayLtEqNegative = {"10,-30,"};
222+
resultSetEqualTest(
223+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col <= -29.1",
224+
expectedHeader,
225+
retArrayLtEqNegative);
226+
227+
// TC-8: int64_col >= -29.1
228+
// Logic: Ceil(-29.1) = -29 -> int64_col >= -29
229+
// Expected result: Row 9(-29) and all larger positive numbers (20, 29, 30, 31, 35, 1000)
230+
String[] retArrayGtEqNegative = {
231+
"1,20,", "2,29,", "3,30,", "4,31,", "5,35,", "8,1000,", "9,-29,"
232+
};
233+
resultSetEqualTest(
234+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col >= -29.1",
235+
expectedHeader,
236+
retArrayGtEqNegative);
237+
}
238+
239+
@Test
240+
public void testDoubleLiteralOverflow() {
241+
String expectedHeaderInt32 = "Time," + DEVICE_ID + ".int32_col,";
242+
String expectedHeaderInt64 = "Time," + DEVICE_ID + ".int64_col,";
243+
String[] emptyResult = {};
244+
245+
// ----------------------------------------------------------------------
246+
// Part 1: INT32 Column vs Double > Integer.MAX_VALUE (~2.14E9)
247+
// Literal: 3.0E9
248+
// ----------------------------------------------------------------------
249+
250+
// TC-1: int32 >= 3.0E9
251+
// Logic: Impossible for any INT32 value.
252+
// Expected: Empty result.
253+
resultSetEqualTest(
254+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col >= 3.0E9",
255+
expectedHeaderInt32,
256+
emptyResult);
257+
258+
// TC-2: int32 <= 3.0E9
259+
// Logic: Always True for valid values. Must exclude NULLs.
260+
// Expected: All rows where int32_col is not null (Row 5 and 7 are null).
261+
String[] allInt32Rows = {
262+
"1,20,", "2,29,", "3,30,", "4,31,", "6,35,", "8,1000,", "9,-29,", "10,-30,"
263+
};
264+
resultSetEqualTest(
265+
"SELECT int32_col FROM " + DEVICE_ID + " WHERE int32_col <= 3.0E9",
266+
expectedHeaderInt32,
267+
allInt32Rows);
268+
269+
// ----------------------------------------------------------------------
270+
// Part 2: INT64 Column vs Double > Long.MAX_VALUE (~9.22E18)
271+
// Literal: 1.0E19
272+
// ----------------------------------------------------------------------
273+
274+
// TC-3: int64 >= 1.0E19
275+
// Logic: Impossible for any INT64 value.
276+
// Expected: Empty result.
277+
resultSetEqualTest(
278+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col >= 1.0E19",
279+
expectedHeaderInt64,
280+
emptyResult);
281+
282+
// TC-4: int64 <= 1.0E19
283+
// Logic: Always True for valid values. Must exclude NULLs.
284+
// Expected: All rows where int64_col is not null (Row 6 and 7 are null).
285+
String[] allInt64Rows = {
286+
"1,20,", "2,29,", "3,30,", "4,31,", "5,35,", "8,1000,", "9,-29,", "10,-30,"
287+
};
288+
resultSetEqualTest(
289+
"SELECT int64_col FROM " + DEVICE_ID + " WHERE int64_col <= 1.0E19",
290+
expectedHeaderInt64,
291+
allInt64Rows);
292+
}
293+
}

0 commit comments

Comments
 (0)