Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Project;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RareTopN;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Relation;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RelationSubquery;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Rename;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Sort;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Sort.SortOption;
Expand Down Expand Up @@ -118,6 +119,18 @@ public LogicalPlan visitRelation(Relation node, AnalysisContext context) {
return new LogicalRelation(node.getTableName());
}

@Override
public LogicalPlan visitRelationSubquery(RelationSubquery node, AnalysisContext context) {
LogicalPlan subquery = analyze(node.getChild().get(0), context);
context.push();
TypeEnvironment curEnv = context.peek();

// Put subquery alias in index namespace so the qualifier can be removed
// when analyzing qualified name in the subquery layer
curEnv.define(new Symbol(Namespace.INDEX_NAME, node.getAliasAsTableName()), STRUCT);
return subquery;
}

@Override
public LogicalPlan visitFilter(Filter node, AnalysisContext context) {
LogicalPlan child = node.getChild().get(0).accept(this, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Project;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RareTopN;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Relation;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RelationSubquery;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Rename;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Sort;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Values;
Expand Down Expand Up @@ -88,6 +89,10 @@ public T visitRelation(Relation node, C context) {
return visitChildren(node, context);
}

public T visitRelationSubquery(RelationSubquery node, C context) {
return visitChildren(node, context);
}

public T visitFilter(Filter node, C context) {
return visitChildren(node, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RareTopN;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RareTopN.CommandType;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Relation;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RelationSubquery;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Rename;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Sort;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.UnresolvedPlan;
Expand Down Expand Up @@ -124,6 +125,10 @@ public static UnresolvedExpression unresolvedAttr(String attr) {
return new UnresolvedAttribute(attr);
}

public static UnresolvedPlan relationSubquery(UnresolvedPlan subquery, String subqueryAlias) {
return new RelationSubquery(subquery, subqueryAlias);
}

private static Literal literal(Object value, DataType type) {
return new Literal(value, type);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.amazon.opendistroforelasticsearch.sql.ast.tree;

import com.amazon.opendistroforelasticsearch.sql.ast.AbstractNodeVisitor;
import com.amazon.opendistroforelasticsearch.sql.exception.SemanticCheckException;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Locale;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

/**
* Logical plan node of RelationSubquery.
*/
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@ToString
public class RelationSubquery extends UnresolvedPlan {
private UnresolvedPlan query;
private String alias;

/**
* Take subquery alias as table name.
*/
public String getAliasAsTableName() {
return alias;
}

@Override
public List<UnresolvedPlan> getChild() {
return ImmutableList.of(query);
}

@Override
public <T, C> T accept(AbstractNodeVisitor<T, C> nodeVisitor, C context) {
return nodeVisitor.visitRelationSubquery(this, context);
}

@Override
public UnresolvedPlan attach(UnresolvedPlan child) {
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.amazon.opendistroforelasticsearch.sql.expression.DSL;
import com.amazon.opendistroforelasticsearch.sql.expression.config.ExpressionConfig;
import com.amazon.opendistroforelasticsearch.sql.expression.window.WindowDefinition;
import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlan;
import com.amazon.opendistroforelasticsearch.sql.planner.logical.LogicalPlanDSL;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -347,6 +348,36 @@ public void window_function() {
ImmutablePair.of("ASC", AstDSL.qualifiedName("integer_value")))))));
}

/**
* SELECT name FROM (
* SELECT name, age FROM test
* ) AS a.
*/
@Test
public void from_subquery() {
assertAnalyzeEqual(
LogicalPlanDSL.project(
LogicalPlanDSL.project(
LogicalPlanDSL.relation("schema"),
DSL.named("string_value", DSL.ref("string_value", STRING)),
DSL.named("integer_value", DSL.ref("integer_value", INTEGER))
),
DSL.named("string_value", DSL.ref("string_value", STRING))
),
AstDSL.project(
AstDSL.relationSubquery(
AstDSL.project(
AstDSL.relation("schema"),
AstDSL.alias("string_value", AstDSL.qualifiedName("string_value")),
AstDSL.alias("integer_value", AstDSL.qualifiedName("integer_value"))
),
"schema"
),
AstDSL.alias("string_value", AstDSL.qualifiedName("string_value"))
)
);
}

/**
* SELECT name, AVG(age) FROM test GROUP BY name.
*/
Expand Down
3 changes: 2 additions & 1 deletion docs/category.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"user/dql/functions.rst",
"user/dql/window.rst",
"user/beyond/partiql.rst",
"user/dql/aggregations.rst"
"user/dql/aggregations.rst",
"user/dql/complex.rst"
]
}
32 changes: 32 additions & 0 deletions docs/user/dql/complex.rst
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,38 @@ Result set:
+------+-----+--+


Here is another example with aggregation function and GROUP BY in subquery::

od> SELECT avg_balance FROM (
... SELECT AVG(balance) AS avg_balance FROM accounts GROUP BY gender, age
... ) AS a;
fetched rows / total rows = 4/4
+---------------+
| avg_balance |
|---------------|
| 32838.0 |
| 39225.0 |
| 4180.0 |
| 5686.0 |
+---------------+


Query with multiple layers of subquery is supported as well, here follows a example::

od> SELECT name FROM (
... SELECT lastname AS name, age FROM (
... SELECT * FROM accounts WHERE gender = 'M'
... ) AS accounts WHERE age < 35
... ) AS accounts
fetched rows / total rows = 2/2
+--------+
| name |
|--------|
| Duke |
| Adams |
+--------+


JOINs
=====

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ public void selectFromSubqueryCountAndSum() throws IOException {
assertThat(result.query("/aggregations/balance/value"), equalTo(25714837.0));
}

@Ignore("Skip to avoid breaking test due to inconsistency in JDBC schema")
@Test
public void selectFromSubqueryWithoutAliasShouldPass() throws IOException {
JSONObject response = executeJdbcRequest(
Expand Down
2 changes: 2 additions & 0 deletions integ-test/src/test/resources/correctness/bugfixes/375.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SELECT flights.TEMP1 AS a, flights.TEMP2 AS b FROM (SELECT COUNT(*) AS TEMP1, SUM(AvgTicketPrice) AS TEMP2 FROM kibana_sample_data_flights) flights
SELECT flights.origin AS a FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights GROUP BY origin, price) flights
19 changes: 19 additions & 0 deletions integ-test/src/test/resources/correctness/queries/subquries.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
SELECT Origin FROM (SELECT * FROM kibana_sample_data_flights) AS f
SELECT f.Origin FROM (SELECT * FROM kibana_sample_data_flights) AS f
SELECT f.o FROM (SELECT Origin AS o FROM kibana_sample_data_flights) AS f
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights WHERE AvgTicketPrice > 100) AS f
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights) AS f WHERE f.price > 100
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights) AS f WHERE price > 100
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights WHERE AvgTicketPrice > 100) AS f WHERE price < 1000
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights) AS f ORDER BY f.price
SELECT origin FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights) AS f ORDER BY price DESC
SELECT origin, price FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights GROUP BY origin, price) AS f
SELECT origin, price FROM (SELECT Origin AS origin, AvgTicketPrice AS price FROM kibana_sample_data_flights) AS f GROUP BY origin, price
SELECT origin, price FROM (SELECT Origin AS origin, Dest AS dest, AvgTicketPrice AS price FROM kibana_sample_data_flights GROUP BY origin, dest, price) AS f GROUP BY origin, price
SELECT origin, price FROM (SELECT Origin AS origin, Dest AS dest, AvgTicketPrice AS price FROM kibana_sample_data_flights GROUP BY 1, 2, 3) AS f GROUP BY 1, 2
SELECT ABS(AvgTicketPrice) FROM (SELECT AvgTicketPrice FROM kibana_sample_data_flights) AS flights GROUP BY ABS(AvgTicketPrice)
SELECT Origin, Dest FROM (SELECT * FROM kibana_sample_data_flights WHERE AvgTicketPrice > 100 GROUP BY Origin, Dest, AvgTicketPrice) AS flights WHERE AvgTicketPrice < 1000 ORDER BY AvgTicketPrice
SELECT Origin, MIN(AvgTicketPrice) FROM (SELECT * FROM kibana_sample_data_flights) AS flights GROUP BY Origin ORDER BY MAX(AvgTicketPrice)
SELECT Origin FROM (SELECT Origin, AvgTicketPrice FROM kibana_sample_data_flights) AS flights GROUP BY Origin HAVING MIN(AvgTicketPrice) > 500
SELECT avg_price FROM (SELECT AVG(AvgTicketPrice) AS avg_price FROM kibana_sample_data_flights) AS flights
SELECT Dest FROM (SELECT Dest, OriginWeather FROM (SELECT Dest, OriginWeather, AvgTicketPrice FROM (SELECT Dest, Origin, OriginWeather, AvgTicketPrice FROM kibana_sample_data_flights WHERE Origin = 'Zurich Airport') AS flights_data WHERE AvgTicketPrice < 10000) AS flights WHERE OriginWeather = 'Clear') AS f
7 changes: 6 additions & 1 deletion sql/src/main/antlr/OpenDistroSQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,18 @@ selectElement
;

fromClause
: FROM tableName (AS? alias)?
: FROM relation
(whereClause)?
(groupByClause)?
(havingClause)?
(orderByClause)? // Place it under FROM for now but actually not necessary ex. A UNION B ORDER BY
;

relation
: tableName (AS? alias)? #tableAsRelation
| LR_BRACKET subquery=querySpecification RR_BRACKET AS? alias #subqueryAsRelation
;

whereClause
: WHERE expression
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.HavingClauseContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.SelectClauseContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.SelectElementContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.SubqueryAsRelationContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.TableAsRelationContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.WhereClauseContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.parser.ParserUtils.getTextInQuery;
import static java.util.Collections.emptyList;
Expand All @@ -30,6 +32,7 @@
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Filter;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Project;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Relation;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.RelationSubquery;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.UnresolvedPlan;
import com.amazon.opendistroforelasticsearch.sql.ast.tree.Values;
import com.amazon.opendistroforelasticsearch.sql.common.antlr.SyntaxCheckException;
Expand Down Expand Up @@ -100,11 +103,8 @@ public UnresolvedPlan visitSelectClause(SelectClauseContext ctx) {

@Override
public UnresolvedPlan visitFromClause(FromClauseContext ctx) {
UnresolvedExpression tableName = visitAstExpression(ctx.tableName());
String tableAlias = (ctx.alias() == null) ? null
: StringUtils.unquoteIdentifier(ctx.alias().getText());
UnresolvedPlan result = visit(ctx.relation());

UnresolvedPlan result = new Relation(tableName, tableAlias);
if (ctx.whereClause() != null) {
result = visit(ctx.whereClause()).attach(result);
}
Expand All @@ -127,6 +127,18 @@ public UnresolvedPlan visitFromClause(FromClauseContext ctx) {
return result;
}

@Override
public UnresolvedPlan visitTableAsRelation(TableAsRelationContext ctx) {
String tableAlias = (ctx.alias() == null) ? null
: StringUtils.unquoteIdentifier(ctx.alias().getText());
return new Relation(visitAstExpression(ctx.tableName()), tableAlias);
}

@Override
public UnresolvedPlan visitSubqueryAsRelation(SubqueryAsRelationContext ctx) {
return new RelationSubquery(visit(ctx.subquery), ctx.alias().getText());
}

@Override
public UnresolvedPlan visitWhereClause(WhereClauseContext ctx) {
return new Filter(visitAstExpression(ctx.expression()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.GroupByElementContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.OrderByElementContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.SelectElementContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.antlr.parser.OpenDistroSQLParser.SubqueryAsRelationContext;
import static com.amazon.opendistroforelasticsearch.sql.sql.parser.ParserUtils.getTextInQuery;

import com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL;
Expand Down Expand Up @@ -170,9 +171,9 @@ public QuerySpecificationCollector(String queryString) {
}

@Override
public Void visitQuerySpecification(QuerySpecificationContext ctx) {
// TODO: avoid collect sub-query
return super.visitQuerySpecification(ctx);
public Void visitSubqueryAsRelation(SubqueryAsRelationContext ctx) {
// skip collecting subquery for current layer
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.project;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.qualifiedName;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.relation;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.relationSubquery;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.sort;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.stringLiteral;
import static com.amazon.opendistroforelasticsearch.sql.ast.dsl.AstDSL.values;
Expand Down Expand Up @@ -406,6 +407,31 @@ public void can_build_order_by_multiple_field_names() {
buildAST("SELECT name, age FROM test ORDER BY name, age DESC"));
}

@Test
public void can_build_from_subquery() {
assertEquals(
project(
filter(
relationSubquery(
project(
relation("test"),
alias("firstname", qualifiedName("firstname"), "first"),
alias("lastname", qualifiedName("lastname"), "last")
),
"a"
),
function(">", qualifiedName("age"), intLiteral(20))
),
alias("a.first", qualifiedName("a", "first")),
alias("last", qualifiedName("last"))),
buildAST(
"SELECT a.first, last FROM ("
+ "SELECT firstname AS first, lastname AS last FROM test"
+ ") AS a where age > 20"
)
);
}

private UnresolvedPlan buildAST(String query) {
ParseTree parseTree = parser.parse(query);
return parseTree.accept(new AstBuilder(query));
Expand Down