Skip to content

Commit 1c1e3c7

Browse files
committed
add explain analyze test cases
1 parent e168d58 commit 1c1e3c7

File tree

6 files changed

+305
-2
lines changed

6 files changed

+305
-2
lines changed

integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppCommonConfig.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,18 @@ public CommonConfig setAuditableOperationResult(String auditableOperationResult)
651651
return this;
652652
}
653653

654+
@Override
655+
public CommonConfig setCteBufferSize(long cteBufferSize) {
656+
setProperty("cte_buffer_size_in_bytes", String.valueOf(cteBufferSize));
657+
return this;
658+
}
659+
660+
@Override
661+
public CommonConfig setMaxRowsInCteBuffer(int maxRows) {
662+
setProperty("max_rows_in_cte_buffer", String.valueOf(maxRows));
663+
return this;
664+
}
665+
654666
// For part of the log directory
655667
public String getClusterConfigStr() {
656668
return fromConsensusFullNameToAbbr(properties.getProperty(CONFIG_NODE_CONSENSUS_PROTOCOL_CLASS))

integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppSharedCommonConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,4 +684,18 @@ public CommonConfig setAuditableOperationResult(String auditableOperationResult)
684684
cnConfig.setAuditableOperationResult(auditableOperationResult);
685685
return this;
686686
}
687+
688+
@Override
689+
public CommonConfig setCteBufferSize(long cteBufferSize) {
690+
dnConfig.setCteBufferSize(cteBufferSize);
691+
cnConfig.setCteBufferSize(cteBufferSize);
692+
return this;
693+
}
694+
695+
@Override
696+
public CommonConfig setMaxRowsInCteBuffer(int maxRows) {
697+
dnConfig.setMaxRowsInCteBuffer(maxRows);
698+
cnConfig.setMaxRowsInCteBuffer(maxRows);
699+
return this;
700+
}
687701
}

integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteCommonConfig.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,14 @@ public CommonConfig setAuditableOperationLevel(String auditableOperationLevel) {
477477
public CommonConfig setAuditableOperationResult(String auditableOperationResult) {
478478
return this;
479479
}
480+
481+
@Override
482+
public CommonConfig setCteBufferSize(long cteBufferSize) {
483+
return this;
484+
}
485+
486+
@Override
487+
public CommonConfig setMaxRowsInCteBuffer(int maxRows) {
488+
return this;
489+
}
480490
}

integration-test/src/main/java/org/apache/iotdb/itbase/env/CommonConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,8 @@ default CommonConfig setDefaultStorageGroupLevel(int defaultStorageGroupLevel) {
211211
CommonConfig setAuditableOperationLevel(String auditableOperationLevel);
212212

213213
CommonConfig setAuditableOperationResult(String auditableOperationResult);
214+
215+
CommonConfig setCteBufferSize(long cteBufferSize);
216+
217+
CommonConfig setMaxRowsInCteBuffer(int maxRows);
214218
}
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
*
3+
* * Licensed to the Apache Software Foundation (ASF) under one
4+
* * or more contributor license agreements. See the NOTICE file
5+
* * distributed with this work for additional information
6+
* * regarding copyright ownership. The ASF licenses this file
7+
* * to you under the Apache License, Version 2.0 (the
8+
* * "License"); you may not use this file except in compliance
9+
* * with the License. You may obtain a copy of the License at
10+
* *
11+
* * http://www.apache.org/licenses/LICENSE-2.0
12+
* *
13+
* * Unless required by applicable law or agreed to in writing,
14+
* * software distributed under the License is distributed on an
15+
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* * KIND, either express or implied. See the License for the
17+
* * specific language governing permissions and limitations
18+
* * under the License.
19+
*
20+
*/
21+
22+
package org.apache.iotdb.relational.it.query.recent;
23+
24+
import org.apache.iotdb.it.env.EnvFactory;
25+
import org.apache.iotdb.it.framework.IoTDBTestRunner;
26+
import org.apache.iotdb.itbase.category.TableClusterIT;
27+
import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
28+
import org.apache.iotdb.itbase.env.BaseEnv;
29+
30+
import org.junit.After;
31+
import org.junit.AfterClass;
32+
import org.junit.Assert;
33+
import org.junit.Before;
34+
import org.junit.BeforeClass;
35+
import org.junit.Test;
36+
import org.junit.experimental.categories.Category;
37+
import org.junit.runner.RunWith;
38+
39+
import java.sql.Connection;
40+
import java.sql.ResultSet;
41+
import java.sql.SQLException;
42+
import java.sql.Statement;
43+
import java.util.Arrays;
44+
import java.util.Locale;
45+
import java.util.function.ToLongFunction;
46+
import java.util.regex.Matcher;
47+
import java.util.regex.Pattern;
48+
49+
import static org.junit.Assert.assertEquals;
50+
import static org.junit.Assert.fail;
51+
52+
@RunWith(IoTDBTestRunner.class)
53+
@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
54+
public class IoTExplainAnalyzeIT {
55+
private static final String DATABASE_NAME = "testdb";
56+
57+
private static final String[] creationSqls =
58+
new String[] {
59+
"CREATE DATABASE IF NOT EXISTS testdb",
60+
"USE testdb",
61+
"CREATE TABLE IF NOT EXISTS testtb(deviceid STRING TAG, voltage FLOAT FIELD)",
62+
"INSERT INTO testtb VALUES(1000, 'd1', 100.0)",
63+
"INSERT INTO testtb VALUES(2000, 'd1', 200.0)",
64+
"INSERT INTO testtb VALUES(1000, 'd2', 300.0)",
65+
};
66+
67+
private static final String dropDbSqls = "DROP DATABASE IF EXISTS testdb";
68+
69+
@BeforeClass
70+
public static void setUpClass() {
71+
Locale.setDefault(Locale.ENGLISH);
72+
73+
EnvFactory.getEnv()
74+
.getConfig()
75+
.getCommonConfig()
76+
.setPartitionInterval(1000)
77+
.setMemtableSizeThreshold(10000)
78+
.setMaxRowsInCteBuffer(100);
79+
EnvFactory.getEnv().initClusterEnvironment();
80+
}
81+
82+
@AfterClass
83+
public static void tearDownClass() {
84+
EnvFactory.getEnv().cleanClusterEnvironment();
85+
}
86+
87+
@Before
88+
public void setUp() throws SQLException {
89+
prepareData();
90+
}
91+
92+
@After
93+
public void tearDown() {
94+
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
95+
Statement statement = connection.createStatement()) {
96+
statement.execute(dropDbSqls);
97+
} catch (Exception e) {
98+
fail(e.getMessage());
99+
}
100+
}
101+
102+
@Test
103+
public void testEmptyCteQuery() throws SQLException {
104+
ResultSet resultSet = null;
105+
String sql =
106+
"explain analyze with cte1 as materialized (select * from testtb1) select * from testtb, cte1 where testtb.deviceid = cte1.deviceid";
107+
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
108+
Statement statement = conn.createStatement()) {
109+
statement.execute("Use testdb");
110+
statement.execute(
111+
"CREATE TABLE IF NOT EXISTS testtb1(deviceid STRING TAG, voltage FLOAT FIELD)");
112+
resultSet = statement.executeQuery(sql);
113+
StringBuilder sb = new StringBuilder();
114+
while (resultSet.next()) {
115+
sb.append(resultSet.getString(1)).append(System.lineSeparator());
116+
}
117+
String result = sb.toString();
118+
Assert.assertFalse(
119+
"Explain Analyze should not contain ExplainAnalyze node.",
120+
result.contains("ExplainAnalyzeNode"));
121+
122+
String[] lines = result.split(System.lineSeparator());
123+
Assert.assertTrue(lines.length > 3);
124+
Assert.assertEquals("CTE Query : 'cte1'", lines[0]);
125+
Assert.assertEquals("", lines[1]);
126+
Assert.assertEquals("Main Query", lines[2]);
127+
statement.execute("DROP TABLE testtb1");
128+
} finally {
129+
if (resultSet != null) {
130+
resultSet.close();
131+
}
132+
}
133+
}
134+
135+
@Test
136+
public void testCteQueryExceedsThreshold() throws SQLException {
137+
ResultSet resultSet = null;
138+
String sql =
139+
"explain analyze with cte1 as materialized (select * from testtb2) select * from testtb where testtb.deviceid in (select deviceid from cte1)";
140+
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
141+
Statement statement = conn.createStatement()) {
142+
statement.execute("Use testdb");
143+
statement.execute(
144+
"CREATE TABLE IF NOT EXISTS testtb2(deviceid STRING TAG, voltage FLOAT FIELD)");
145+
for (int i = 0; i < 100; i++) {
146+
statement.addBatch(
147+
String.format("insert into testtb2(deviceid, voltage) values('d%d', %d)", i, i));
148+
}
149+
statement.executeBatch();
150+
resultSet = statement.executeQuery(sql);
151+
StringBuilder sb = new StringBuilder();
152+
while (resultSet.next()) {
153+
sb.append(resultSet.getString(1)).append(System.lineSeparator());
154+
}
155+
156+
String result = sb.toString();
157+
Assert.assertFalse(
158+
"Main Query should not contain CteScan node when the CTE query's result set exceeds threshold.",
159+
result.contains("CteScanNode(CteScanOperator)"));
160+
Assert.assertTrue(
161+
"CTE Query should contain warning message when CTE query's result set exceeds threshold.",
162+
result.contains("Failed to materialize CTE"));
163+
Assert.assertFalse(
164+
"Explain Analyze should not contain ExplainAnalyze node.",
165+
result.contains("ExplainAnalyzeNode"));
166+
167+
String[] plans = result.split("Main Query");
168+
for (String plan : plans) {
169+
String[] lines = plan.split(System.lineSeparator());
170+
long[] instanceCount =
171+
Arrays.stream(lines)
172+
.filter(line -> line.contains("Fragment Instances Count:"))
173+
.mapToLong(extractNumber("Fragment Instances Count:\\s(\\d+)"))
174+
.toArray();
175+
assertEquals(instanceCount.length, 1);
176+
177+
long totalInstances =
178+
Arrays.stream(lines).filter(line -> line.contains("FRAGMENT-INSTANCE")).count();
179+
assertEquals(totalInstances, instanceCount[0]);
180+
}
181+
182+
statement.execute("DROP TABLE testtb2");
183+
} finally {
184+
if (resultSet != null) {
185+
resultSet.close();
186+
}
187+
}
188+
}
189+
190+
@Test
191+
public void testCteQuerySuccess() throws SQLException {
192+
ResultSet resultSet = null;
193+
String sql =
194+
"explain analyze with cte1 as materialized (select * from testtb3) select * from testtb where testtb.deviceid in (select deviceid from cte1)";
195+
try (Connection conn = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
196+
Statement statement = conn.createStatement()) {
197+
statement.execute("Use testdb");
198+
statement.execute(
199+
"CREATE TABLE IF NOT EXISTS testtb3(deviceid STRING TAG, voltage FLOAT FIELD)");
200+
for (int i = 0; i < 50; i++) {
201+
statement.addBatch(
202+
String.format("insert into testtb3(deviceid, voltage) values('d%d', %d)", i, i));
203+
}
204+
statement.executeBatch();
205+
resultSet = statement.executeQuery(sql);
206+
StringBuilder sb = new StringBuilder();
207+
while (resultSet.next()) {
208+
sb.append(resultSet.getString(1)).append(System.lineSeparator());
209+
}
210+
211+
String result = sb.toString();
212+
Assert.assertTrue(
213+
"Main Query should contain CteScan node when the CTE query's result set exceeds threshold.",
214+
result.contains("CteScanNode(CteScanOperator)"));
215+
Assert.assertFalse(
216+
"Explain Analyze should not contain ExplainAnalyze node.",
217+
result.contains("ExplainAnalyzeNode"));
218+
219+
String[] plans = result.split("Main Query");
220+
for (String plan : plans) {
221+
String[] lines = plan.split(System.lineSeparator());
222+
long[] instanceCount =
223+
Arrays.stream(lines)
224+
.filter(line -> line.contains("Fragment Instances Count:"))
225+
.mapToLong(extractNumber("Fragment Instances Count:\\s(\\d+)"))
226+
.toArray();
227+
assertEquals(instanceCount.length, 1);
228+
229+
long totalInstances =
230+
Arrays.stream(lines).filter(line -> line.contains("FRAGMENT-INSTANCE")).count();
231+
assertEquals(totalInstances, instanceCount[0]);
232+
}
233+
234+
statement.execute("DROP TABLE testtb3");
235+
} finally {
236+
if (resultSet != null) {
237+
resultSet.close();
238+
}
239+
}
240+
}
241+
242+
private static ToLongFunction<String> extractNumber(String regex) {
243+
return line -> {
244+
Pattern pattern = Pattern.compile(regex);
245+
Matcher matcher = pattern.matcher(line);
246+
if (matcher.find()) {
247+
return Long.parseLong(matcher.group(1));
248+
}
249+
return 0;
250+
};
251+
}
252+
253+
private static void prepareData() {
254+
try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
255+
Statement statement = connection.createStatement()) {
256+
257+
for (String sql : creationSqls) {
258+
statement.execute(sql);
259+
}
260+
} catch (Exception e) {
261+
fail(e.getMessage());
262+
}
263+
}
264+
}

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,11 @@
4242
import org.apache.tsfile.utils.Pair;
4343

4444
import java.time.ZoneId;
45-
import java.util.HashMap;
4645
import java.util.Collections;
46+
import java.util.HashMap;
4747
import java.util.HashSet;
4848
import java.util.List;
4949
import java.util.Map;
50-
import java.util.List;
5150
import java.util.Optional;
5251
import java.util.Set;
5352
import java.util.concurrent.ConcurrentHashMap;

0 commit comments

Comments
 (0)