Skip to content

Commit e8a2ef2

Browse files
committed
test(api): Add SQL planner tests and refactor test base for multi-language support
- Refactor UnifiedQueryTestBase with queryType() hook for subclass override - Add UnifiedSqlQueryPlannerTest covering SELECT, WHERE, GROUP BY, JOIN, ORDER BY, subquery, case sensitivity, namespaces, and error handling - Update UnifiedQueryContextTest to verify SQL context creation Signed-off-by: Chen Dai <daichen@amazon.com>
1 parent 4011216 commit e8a2ef2

3 files changed

Lines changed: 204 additions & 9 deletions

File tree

api/src/test/java/org/opensearch/sql/api/UnifiedQueryContextTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,15 @@ public void testMissingQueryType() {
6363
UnifiedQueryContext.builder().catalog("opensearch", testSchema).build();
6464
}
6565

66-
@Test(expected = IllegalArgumentException.class)
67-
public void testUnsupportedQueryType() {
66+
@Test
67+
public void testSqlQueryType() {
6868
UnifiedQueryContext context =
6969
UnifiedQueryContext.builder()
70-
.language(QueryType.SQL) // only PPL is supported for now
70+
.language(QueryType.SQL)
7171
.catalog("opensearch", testSchema)
7272
.build();
73-
new UnifiedQueryPlanner(context);
73+
UnifiedQueryPlanner planner = new UnifiedQueryPlanner(context);
74+
assertNotNull("SQL planner should be created", planner);
7475
}
7576

7677
@Test(expected = IllegalArgumentException.class)
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.api;
7+
8+
import static org.junit.Assert.assertNotNull;
9+
import static org.junit.Assert.assertThrows;
10+
11+
import java.util.Map;
12+
import org.apache.calcite.rel.RelNode;
13+
import org.apache.calcite.schema.Schema;
14+
import org.apache.calcite.schema.impl.AbstractSchema;
15+
import org.junit.Test;
16+
import org.opensearch.sql.executor.QueryType;
17+
18+
public class UnifiedSqlQueryPlannerTest extends UnifiedQueryTestBase {
19+
20+
@Override
21+
protected QueryType queryType() {
22+
return QueryType.SQL;
23+
}
24+
25+
@Test
26+
public void testSelectAll() {
27+
RelNode plan =
28+
planner.plan(
29+
"""
30+
SELECT *
31+
FROM catalog.employees\
32+
""");
33+
assertNotNull("Plan should be created", plan);
34+
}
35+
36+
@Test
37+
public void testSelectWithFilter() {
38+
RelNode plan =
39+
planner.plan(
40+
"""
41+
SELECT *
42+
FROM catalog.employees
43+
WHERE age > 30\
44+
""");
45+
assertNotNull("Plan should be created", plan);
46+
}
47+
48+
@Test
49+
public void testSelectWithAggregation() {
50+
RelNode plan =
51+
planner.plan(
52+
"""
53+
SELECT department, count(*)
54+
FROM catalog.employees
55+
GROUP BY department\
56+
""");
57+
assertNotNull("Plan should be created", plan);
58+
}
59+
60+
@Test
61+
public void testSelectWithJoin() {
62+
RelNode plan =
63+
planner.plan(
64+
"""
65+
SELECT a.id, b.name
66+
FROM catalog.employees a
67+
JOIN catalog.employees b ON a.id = b.age\
68+
""");
69+
assertNotNull("Plan should be created", plan);
70+
}
71+
72+
@Test
73+
public void testSelectWithOrderBy() {
74+
RelNode plan =
75+
planner.plan(
76+
"""
77+
SELECT *
78+
FROM catalog.employees
79+
ORDER BY age DESC\
80+
""");
81+
assertNotNull("Plan should be created", plan);
82+
}
83+
84+
@Test
85+
public void testSelectWithSubquery() {
86+
RelNode plan =
87+
planner.plan(
88+
"""
89+
SELECT *
90+
FROM catalog.employees
91+
WHERE age > (SELECT avg(age) FROM catalog.employees)\
92+
""");
93+
assertNotNull("Plan should be created", plan);
94+
}
95+
96+
@Test
97+
public void testSelectWithCte() {
98+
RelNode plan =
99+
planner.plan(
100+
"""
101+
WITH seniors AS (
102+
SELECT * FROM catalog.employees WHERE age > 30
103+
)
104+
SELECT *
105+
FROM seniors\
106+
""");
107+
assertNotNull("Plan should be created", plan);
108+
}
109+
110+
@Test
111+
public void testCaseInsensitiveIdentifiers() {
112+
// Verify Casing.UNCHANGED: lowercase table/column names resolve correctly
113+
RelNode plan =
114+
planner.plan(
115+
"""
116+
SELECT id, name
117+
FROM catalog.employees
118+
WHERE age > 30\
119+
""");
120+
assertNotNull("Plan should be created with lowercase identifiers", plan);
121+
}
122+
123+
@Test
124+
public void testDefaultNamespace() {
125+
UnifiedQueryContext sqlContext =
126+
UnifiedQueryContext.builder()
127+
.language(QueryType.SQL)
128+
.catalog("catalog", testSchema)
129+
.defaultNamespace("catalog")
130+
.build();
131+
UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext);
132+
133+
assertNotNull(
134+
"Plan should resolve unqualified table", sqlPlanner.plan("SELECT * FROM employees"));
135+
}
136+
137+
@Test
138+
public void testMultipleCatalogs() {
139+
UnifiedQueryContext sqlContext =
140+
UnifiedQueryContext.builder()
141+
.language(QueryType.SQL)
142+
.catalog("catalog1", testSchema)
143+
.catalog("catalog2", testSchema)
144+
.build();
145+
UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext);
146+
147+
RelNode plan =
148+
sqlPlanner.plan(
149+
"""
150+
SELECT a.id
151+
FROM catalog1.employees a
152+
JOIN catalog2.employees b ON a.id = b.id\
153+
""");
154+
assertNotNull("Plan should be created with multiple catalogs", plan);
155+
}
156+
157+
@Test
158+
public void testDefaultNamespaceMultiLevel() {
159+
AbstractSchema deepSchema =
160+
new AbstractSchema() {
161+
@Override
162+
protected Map<String, Schema> getSubSchemaMap() {
163+
return Map.of("opensearch", testSchema);
164+
}
165+
};
166+
UnifiedQueryContext sqlContext =
167+
UnifiedQueryContext.builder()
168+
.language(QueryType.SQL)
169+
.catalog("catalog", deepSchema)
170+
.defaultNamespace("catalog.opensearch")
171+
.build();
172+
UnifiedQueryPlanner sqlPlanner = new UnifiedQueryPlanner(sqlContext);
173+
174+
assertNotNull(
175+
"Plan should resolve with multi-level default namespace",
176+
sqlPlanner.plan("SELECT * FROM employees"));
177+
}
178+
179+
@Test
180+
public void testInvalidSqlThrowsException() {
181+
assertThrows(IllegalStateException.class, () -> planner.plan("SELECT FROM"));
182+
}
183+
}

api/src/testFixtures/java/org/opensearch/sql/api/UnifiedQueryTestBase.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,25 @@ protected Map<String, Table> getTableMap() {
5555
}
5656
};
5757

58-
context =
59-
UnifiedQueryContext.builder()
60-
.language(QueryType.PPL)
61-
.catalog(DEFAULT_CATALOG, testSchema)
62-
.build();
58+
context = buildContext(queryType());
6359
planner = new UnifiedQueryPlanner(context);
6460
}
6561

62+
/**
63+
* Returns the query type for this test class. Subclasses override to test different languages.
64+
*/
65+
protected QueryType queryType() {
66+
return QueryType.PPL;
67+
}
68+
69+
/** Builds a UnifiedQueryContext with the test schema for the given query type. */
70+
protected UnifiedQueryContext buildContext(QueryType queryType) {
71+
return UnifiedQueryContext.builder()
72+
.language(queryType)
73+
.catalog(DEFAULT_CATALOG, testSchema)
74+
.build();
75+
}
76+
6677
@After
6778
public void tearDown() throws Exception {
6879
if (context != null) {

0 commit comments

Comments
 (0)