Skip to content

Commit 4b995b7

Browse files
Add support for WHERE clause, column list in SELECT clause and for functions and expressions in the query.
Signed-off-by: Yury-Fridlyand <[email protected]>
1 parent 117e889 commit 4b995b7

File tree

13 files changed

+962
-95
lines changed

13 files changed

+962
-95
lines changed

core/src/main/java/org/opensearch/sql/ast/expression/Cast.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public class Cast extends UnresolvedExpression {
6565
private final UnresolvedExpression expression;
6666

6767
/**
68-
* Expression that represents ELSE statement result.
68+
* Expression that represents name of the target type.
6969
*/
7070
private final UnresolvedExpression convertedType;
7171

core/src/main/java/org/opensearch/sql/executor/pagination/CanPaginateVisitor.java

Lines changed: 200 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,52 @@
77

88
import org.opensearch.sql.ast.AbstractNodeVisitor;
99
import org.opensearch.sql.ast.Node;
10+
import org.opensearch.sql.ast.expression.Alias;
1011
import org.opensearch.sql.ast.expression.AllFields;
12+
import org.opensearch.sql.ast.expression.And;
13+
import org.opensearch.sql.ast.expression.Argument;
14+
import org.opensearch.sql.ast.expression.Between;
15+
import org.opensearch.sql.ast.expression.Case;
16+
import org.opensearch.sql.ast.expression.Cast;
17+
import org.opensearch.sql.ast.expression.Compare;
18+
import org.opensearch.sql.ast.expression.EqualTo;
19+
import org.opensearch.sql.ast.expression.Field;
20+
import org.opensearch.sql.ast.expression.Function;
21+
import org.opensearch.sql.ast.expression.HighlightFunction;
22+
import org.opensearch.sql.ast.expression.In;
23+
import org.opensearch.sql.ast.expression.Interval;
24+
import org.opensearch.sql.ast.expression.Literal;
25+
import org.opensearch.sql.ast.expression.Not;
26+
import org.opensearch.sql.ast.expression.Or;
27+
import org.opensearch.sql.ast.expression.QualifiedName;
28+
import org.opensearch.sql.ast.expression.RelevanceFieldList;
29+
import org.opensearch.sql.ast.expression.UnresolvedArgument;
30+
import org.opensearch.sql.ast.expression.UnresolvedAttribute;
31+
import org.opensearch.sql.ast.expression.When;
32+
import org.opensearch.sql.ast.expression.WindowFunction;
33+
import org.opensearch.sql.ast.expression.Xor;
34+
import org.opensearch.sql.ast.tree.Aggregation;
35+
import org.opensearch.sql.ast.tree.Filter;
36+
import org.opensearch.sql.ast.tree.Limit;
1137
import org.opensearch.sql.ast.tree.Project;
1238
import org.opensearch.sql.ast.tree.Relation;
39+
import org.opensearch.sql.ast.tree.Sort;
40+
import org.opensearch.sql.ast.tree.Values;
1341

1442
/**
1543
* Use this unresolved plan visitor to check if a plan can be serialized by PaginatedPlanCache.
16-
* If plan.accept(new CanPaginateVisitor(...)) returns true,
44+
* If <pre>plan.accept(new CanPaginateVisitor(...))</pre> returns <em>true</em>,
1745
* then PaginatedPlanCache.convertToCursor will succeed. Otherwise, it will fail.
1846
* The purpose of this visitor is to activate legacy engine fallback mechanism.
19-
* Currently, the conditions are:
20-
* - only projection of a relation is supported.
21-
* - projection only has * (a.k.a. allFields).
22-
* - Relation only scans one table
23-
* - The table is an open search index.
24-
* So it accepts only queries like `select * from $index`
47+
* Currently, V2 engine does not support queries with:
48+
* - aggregation (GROUP BY clause or aggregation functions like min/max)
49+
* - in memory aggregation (window function)
50+
* - ORDER BY clause
51+
* - LIMIT/OFFSET clause(s)
52+
* - without FROM clause
53+
* - JOIN
54+
* - a subquery
55+
* V2 also requires that the table being queried should be an OpenSearch index.
2556
* See PaginatedPlanCache.canConvertToCursor for usage.
2657
*/
2758
public class CanPaginateVisitor extends AbstractNodeVisitor<Boolean, Object> {
@@ -36,22 +67,176 @@ public Boolean visitRelation(Relation node, Object context) {
3667
return Boolean.TRUE;
3768
}
3869

70+
private Boolean canPaginate(Node node, Object context) {
71+
var childList = node.getChild();
72+
if (childList != null) {
73+
return childList.stream().allMatch(n -> n.accept(this, context));
74+
}
75+
return Boolean.TRUE;
76+
}
77+
78+
//For queries with WHERE clause:
79+
@Override
80+
public Boolean visitFilter(Filter node, Object context) {
81+
return canPaginate(node, context) && node.getCondition().accept(this, context);
82+
}
83+
84+
// Queries with GROUP BY clause are not supported
85+
@Override
86+
public Boolean visitAggregation(Aggregation node, Object context) {
87+
return Boolean.FALSE;
88+
}
89+
90+
// Queries with ORDER BY clause are not supported
91+
@Override
92+
public Boolean visitSort(Sort node, Object context) {
93+
return Boolean.FALSE;
94+
}
95+
96+
// Queries without FROM clause are not supported
97+
@Override
98+
public Boolean visitValues(Values node, Object context) {
99+
return Boolean.FALSE;
100+
}
101+
102+
@Override
103+
public Boolean visitLimit(Limit node, Object context) {
104+
return super.visitLimit(node, context);
105+
}
106+
107+
@Override
108+
public Boolean visitLiteral(Literal node, Object context) {
109+
return canPaginate(node, context);
110+
}
111+
112+
@Override
113+
public Boolean visitField(Field node, Object context) {
114+
return canPaginate(node, context) && node.getFieldArgs().stream()
115+
.allMatch(n -> n.accept(this, context));
116+
}
117+
118+
@Override
119+
public Boolean visitAlias(Alias node, Object context) {
120+
return canPaginate(node, context) && node.getDelegated().accept(this, context);
121+
}
122+
123+
@Override
124+
public Boolean visitAllFields(AllFields node, Object context) {
125+
return canPaginate(node, context);
126+
}
127+
128+
@Override
129+
public Boolean visitQualifiedName(QualifiedName node, Object context) {
130+
return canPaginate(node, context);
131+
}
132+
133+
@Override
134+
public Boolean visitEqualTo(EqualTo node, Object context) {
135+
return canPaginate(node, context);
136+
}
137+
138+
@Override
139+
public Boolean visitRelevanceFieldList(RelevanceFieldList node, Object context) {
140+
return canPaginate(node, context);
141+
}
142+
143+
@Override
144+
public Boolean visitInterval(Interval node, Object context) {
145+
return canPaginate(node, context);
146+
}
147+
148+
@Override
149+
public Boolean visitCompare(Compare node, Object context) {
150+
return canPaginate(node, context);
151+
}
152+
153+
@Override
154+
public Boolean visitNot(Not node, Object context) {
155+
return canPaginate(node, context);
156+
}
157+
158+
@Override
159+
public Boolean visitOr(Or node, Object context) {
160+
return canPaginate(node, context);
161+
}
162+
163+
@Override
164+
public Boolean visitAnd(And node, Object context) {
165+
return canPaginate(node, context);
166+
}
167+
168+
@Override
169+
public Boolean visitArgument(Argument node, Object context) {
170+
return canPaginate(node, context);
171+
}
172+
173+
@Override
174+
public Boolean visitXor(Xor node, Object context) {
175+
return canPaginate(node, context);
176+
}
177+
178+
@Override
179+
public Boolean visitFunction(Function node, Object context) {
180+
return canPaginate(node, context);
181+
}
182+
183+
@Override
184+
public Boolean visitIn(In node, Object context) {
185+
return canPaginate(node, context) && node.getValueList().stream()
186+
.allMatch(n -> n.accept(this, context));
187+
}
188+
189+
@Override
190+
public Boolean visitBetween(Between node, Object context) {
191+
return canPaginate(node, context);
192+
}
193+
194+
@Override
195+
public Boolean visitCase(Case node, Object context) {
196+
return canPaginate(node, context);
197+
}
198+
199+
@Override
200+
public Boolean visitWhen(When node, Object context) {
201+
return canPaginate(node, context);
202+
}
203+
204+
@Override
205+
public Boolean visitCast(Cast node, Object context) {
206+
return canPaginate(node, context) && node.getConvertedType().accept(this, context);
207+
}
208+
209+
@Override
210+
public Boolean visitHighlightFunction(HighlightFunction node, Object context) {
211+
return canPaginate(node, context);
212+
}
213+
214+
@Override
215+
public Boolean visitUnresolvedArgument(UnresolvedArgument node, Object context) {
216+
return canPaginate(node, context);
217+
}
218+
219+
@Override
220+
public Boolean visitUnresolvedAttribute(UnresolvedAttribute node, Object context) {
221+
return canPaginate(node, context);
222+
}
223+
39224
@Override
40225
public Boolean visitChildren(Node node, Object context) {
226+
// for all not listed (= unchecked) - false
41227
return Boolean.FALSE;
42228
}
43229

44230
@Override
45-
public Boolean visitProject(Project node, Object context) {
46-
// Allow queries with 'SELECT *' only. Those restriction could be removed, but consider
47-
// in-memory aggregation performed by window function (see WindowOperator).
231+
public Boolean visitWindowFunction(WindowFunction node, Object context) {
232+
// don't support in-memory aggregation
48233
// SELECT max(age) OVER (PARTITION BY city) ...
49-
var projections = node.getProjectList();
50-
if (projections.size() != 1) {
51-
return Boolean.FALSE;
52-
}
234+
return Boolean.FALSE;
235+
}
53236

54-
if (!(projections.get(0) instanceof AllFields)) {
237+
@Override
238+
public Boolean visitProject(Project node, Object context) {
239+
if (!node.getProjectList().stream().allMatch(n -> n.accept(this, context))) {
55240
return Boolean.FALSE;
56241
}
57242

0 commit comments

Comments
 (0)