Skip to content

Commit 17fc976

Browse files
LantaoJinpenghuo
authored andcommitted
Implement ppl IN subquery command with Calcite (opensearch-project#3371)
1 parent 18e832a commit 17fc976

File tree

25 files changed

+1546
-2414
lines changed

25 files changed

+1546
-2414
lines changed

core/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.opensearch.sql.ast.expression.When;
3737
import org.opensearch.sql.ast.expression.WindowFunction;
3838
import org.opensearch.sql.ast.expression.Xor;
39+
import org.opensearch.sql.ast.expression.subquery.InSubquery;
3940
import org.opensearch.sql.ast.statement.Explain;
4041
import org.opensearch.sql.ast.statement.Query;
4142
import org.opensearch.sql.ast.statement.Statement;
@@ -315,6 +316,10 @@ public T visitExplain(Explain node, C context) {
315316
return visitStatement(node, context);
316317
}
317318

319+
public T visitInSubquery(InSubquery node, C context) {
320+
return visitChildren(node, context);
321+
}
322+
318323
public T visitPaginate(Paginate paginate, C context) {
319324
return visitChildren(paginate, context);
320325
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression.subquery;
7+
8+
import java.util.List;
9+
import lombok.EqualsAndHashCode;
10+
import lombok.Getter;
11+
import lombok.RequiredArgsConstructor;
12+
import lombok.ToString;
13+
import org.opensearch.sql.ast.AbstractNodeVisitor;
14+
import org.opensearch.sql.ast.expression.UnresolvedExpression;
15+
import org.opensearch.sql.ast.tree.UnresolvedPlan;
16+
17+
@Getter
18+
@ToString
19+
@EqualsAndHashCode(callSuper = false)
20+
@RequiredArgsConstructor
21+
public class InSubquery extends UnresolvedExpression {
22+
private final List<UnresolvedExpression> value;
23+
private final UnresolvedPlan query;
24+
25+
@Override
26+
public List<UnresolvedExpression> getChild() {
27+
return value;
28+
}
29+
30+
@Override
31+
public <R, C> R accept(AbstractNodeVisitor<R, C> nodeVisitor, C context) {
32+
return nodeVisitor.visitInSubquery(this, context);
33+
}
34+
}

core/src/main/java/org/opensearch/sql/calcite/CalciteRelNodeVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class CalciteRelNodeVisitor extends AbstractNodeVisitor<RelNode, CalciteP
5151
private final CalciteAggCallVisitor aggVisitor;
5252

5353
public CalciteRelNodeVisitor() {
54-
this.rexVisitor = new CalciteRexNodeVisitor();
54+
this.rexVisitor = new CalciteRexNodeVisitor(this);
5555
this.aggVisitor = new CalciteAggCallVisitor(rexVisitor);
5656
}
5757

core/src/main/java/org/opensearch/sql/calcite/CalciteRexNodeVisitor.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import java.util.List;
1313
import java.util.Map;
1414
import java.util.stream.Collectors;
15+
import lombok.RequiredArgsConstructor;
16+
import org.apache.calcite.rel.RelNode;
1517
import org.apache.calcite.rel.type.RelDataType;
1618
import org.apache.calcite.rel.type.RelDataTypeFactory;
1719
import org.apache.calcite.rex.RexBuilder;
@@ -38,9 +40,14 @@
3840
import org.opensearch.sql.ast.expression.SpanUnit;
3941
import org.opensearch.sql.ast.expression.UnresolvedExpression;
4042
import org.opensearch.sql.ast.expression.Xor;
43+
import org.opensearch.sql.ast.expression.subquery.InSubquery;
44+
import org.opensearch.sql.ast.tree.UnresolvedPlan;
4145
import org.opensearch.sql.calcite.utils.BuiltinFunctionUtils;
46+
import org.opensearch.sql.exception.SemanticCheckException;
4247

48+
@RequiredArgsConstructor
4349
public class CalciteRexNodeVisitor extends AbstractNodeVisitor<RexNode, CalcitePlanContext> {
50+
private final CalciteRelNodeVisitor planVisitor;
4451

4552
public RexNode analyze(UnresolvedExpression unresolved, CalcitePlanContext context) {
4653
return unresolved.accept(this, context);
@@ -150,11 +157,18 @@ public RexNode visitEqualTo(EqualTo node, CalcitePlanContext context) {
150157
public RexNode visitQualifiedName(QualifiedName node, CalcitePlanContext context) {
151158
if (context.isResolvingJoinCondition()) {
152159
List<String> parts = node.getParts();
153-
if (parts.size() == 1) { // Handle the case of `id = cid`
160+
if (parts.size() == 1) {
161+
// Handle the case of `id = cid`
154162
try {
155-
return context.relBuilder.field(2, 0, parts.get(0));
156-
} catch (IllegalArgumentException i) {
157-
return context.relBuilder.field(2, 1, parts.get(0));
163+
// TODO what if there is join clause in InSubquery in join condition
164+
// for subquery in join condition
165+
return context.relBuilder.field(parts.get(0));
166+
} catch (IllegalArgumentException e) {
167+
try {
168+
return context.relBuilder.field(2, 0, parts.get(0));
169+
} catch (IllegalArgumentException ee) {
170+
return context.relBuilder.field(2, 1, parts.get(0));
171+
}
158172
}
159173
} else if (parts.size()
160174
== 2) { // Handle the case of `t1.id = t2.id` or `alias1.id = alias2.id`
@@ -242,4 +256,29 @@ public RexNode visitFunction(Function node, CalcitePlanContext context) {
242256
return context.rexBuilder.makeCall(
243257
BuiltinFunctionUtils.translate(node.getFuncName()), arguments);
244258
}
259+
260+
@Override
261+
public RexNode visitInSubquery(InSubquery node, CalcitePlanContext context) {
262+
List<RexNode> nodes = node.getChild().stream().map(child -> analyze(child, context)).toList();
263+
UnresolvedPlan subquery = node.getQuery();
264+
RelNode subqueryRel = subquery.accept(planVisitor, context);
265+
context.relBuilder.build();
266+
try {
267+
return context.relBuilder.in(subqueryRel, nodes);
268+
// TODO
269+
// The {@link org.apache.calcite.tools.RelBuilder#in(RexNode,java.util.function.Function)}
270+
// only support one expression. Change to follow code after calcite fixed.
271+
// return context.relBuilder.in(
272+
// nodes.getFirst(),
273+
// b -> {
274+
// RelNode subqueryRel = subquery.accept(planVisitor, context);
275+
// b.build();
276+
// return subqueryRel;
277+
// });
278+
} catch (AssertionError e) {
279+
throw new SemanticCheckException(
280+
"The number of columns in the left hand side of an IN subquery does not match the number"
281+
+ " of columns in the output of subquery");
282+
}
283+
}
245284
}

core/src/main/java/org/opensearch/sql/calcite/utils/OpenSearchTypeFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ public static RelDataType convertExprTypeToRelDataType(ExprType fieldType, boole
112112
return TYPE_FACTORY.createSqlType(SqlTypeName.BINARY, nullable);
113113
} else if (fieldType.legacyTypeName().equalsIgnoreCase("timestamp")) {
114114
return TYPE_FACTORY.createSqlType(SqlTypeName.TIMESTAMP, nullable);
115+
} else if (fieldType.legacyTypeName().equalsIgnoreCase("date")) {
116+
return TYPE_FACTORY.createSqlType(SqlTypeName.DATE, nullable);
117+
} else if (fieldType.legacyTypeName().equalsIgnoreCase("time")) {
118+
return TYPE_FACTORY.createSqlType(SqlTypeName.TIME, nullable);
115119
} else if (fieldType.legacyTypeName().equalsIgnoreCase("geo_point")) {
116120
return TYPE_FACTORY.createSqlType(SqlTypeName.GEOMETRY, nullable);
117121
} else if (fieldType.legacyTypeName().equalsIgnoreCase("text")) {

0 commit comments

Comments
 (0)