Skip to content

Commit

Permalink
Add parentRelationType to AnalysisContext
Browse files Browse the repository at this point in the history
Thanks to that ExpressionAnalyzer is able to recognize that reference to
outer relation was used.

Pull request: #5143
  • Loading branch information
kokosing authored and martint committed Apr 30, 2016
1 parent b907d7c commit 4fc9d7d
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 17 deletions.
Expand Up @@ -14,27 +14,33 @@
package com.facebook.presto.sql.analyzer;

import com.facebook.presto.sql.tree.WithQuery;
import com.google.common.base.Preconditions;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public class AnalysisContext
{
private final AnalysisContext parent;
private final Optional<AnalysisContext> parent;
private final RelationType parentRelationType;
private final Map<String, WithQuery> namedQueries = new HashMap<>();
private RelationType lateralTupleDescriptor = new RelationType();
private boolean approximate;

public AnalysisContext(AnalysisContext parent)
public AnalysisContext(AnalysisContext parent, RelationType parentRelationType)
{
this.parent = parent;
this.parent = Optional.of(parent);
this.parentRelationType = requireNonNull(parentRelationType, "parentRelationType is null");
this.approximate = parent.approximate;
}

public AnalysisContext()
{
parent = null;
parent = Optional.empty();
parentRelationType = new RelationType();
}

public void setLateralTupleDescriptor(RelationType lateralTupleDescriptor)
Expand All @@ -59,16 +65,16 @@ public void setApproximate(boolean approximate)

public void addNamedQuery(String name, WithQuery withQuery)
{
Preconditions.checkState(!namedQueries.containsKey(name), "Named query already registered: %s", name);
checkState(!namedQueries.containsKey(name), "Named query already registered: %s", name);
namedQueries.put(name, withQuery);
}

public WithQuery getNamedQuery(String name)
{
WithQuery result = namedQueries.get(name);

if (result == null && parent != null) {
return parent.getNamedQuery(name);
if (result == null && parent.isPresent()) {
return parent.get().getNamedQuery(name);
}

return result;
Expand All @@ -78,4 +84,14 @@ public boolean isNamedQueryDeclared(String name)
{
return namedQueries.containsKey(name);
}

public RelationType getParentRelationType()
{
return parentRelationType;
}

public Optional<AnalysisContext> getParent()
{
return parent;
}
}
Expand Up @@ -307,7 +307,12 @@ protected Type visitDereferenceExpression(DereferenceExpression node, StackableA
return field.getType();
}

assertColumnPrefix(qualifiedName, node);
if (!isColumnPrefix(qualifiedName, tupleDescriptor)) {
if (isReferenceToOuterRelation(context.getContext(), qualifiedName)) {
throw new SemanticException(NOT_SUPPORTED, node, "Correlated queries not yet supported. Invalid column reference: '%s'", qualifiedName);
}
throw createMissingAttributeException(node);
}
}

Type baseType = process(node.getBase(), context);
Expand All @@ -332,19 +337,33 @@ protected Type visitDereferenceExpression(DereferenceExpression node, StackableA
return rowFieldType;
}

private void assertColumnPrefix(QualifiedName qualifiedName, Expression node)
private boolean isColumnPrefix(QualifiedName qualifiedName, RelationType tupleDescriptor)
{
// Recursively check if its prefix is a column.
while (qualifiedName.getPrefix().isPresent()) {
qualifiedName = qualifiedName.getPrefix().get();
List<Field> matches = tupleDescriptor.resolveFields(qualifiedName);
if (!matches.isEmpty()) {
// The AMBIGUOUS_ATTRIBUTE exception will be thrown later with the right node if matches.size() > 1
return;
return true;
}
}
return false;
}

throw createMissingAttributeException(node);
private boolean isReferenceToOuterRelation(AnalysisContext context, QualifiedName qualifiedName)
{
AnalysisContext current = context;
while (current != null) {
RelationType type = current.getParentRelationType();
if (!type.resolveFields(qualifiedName).isEmpty()) {
return true;
}
if (isColumnPrefix(qualifiedName, type)) {
return true;
}
current = current.getParent().orElse(null);
}
return false;
}

private SemanticException createMissingAttributeException(Expression node)
Expand Down Expand Up @@ -883,7 +902,7 @@ protected Type visitInListExpression(InListExpression node, StackableAstVisitorC
protected Type visitSubqueryExpression(SubqueryExpression node, StackableAstVisitorContext<AnalysisContext> context)
{
StatementAnalyzer analyzer = statementAnalyzerFactory.apply(node);
RelationType descriptor = analyzer.process(node.getQuery(), context.getContext());
RelationType descriptor = analyzer.process(node.getQuery(), new AnalysisContext(context.getContext(), tupleDescriptor));

// Subquery should only produce one column
if (descriptor.getVisibleFieldCount() != 1) {
Expand Down
Expand Up @@ -819,7 +819,7 @@ private String getQueryPlan(Explain node, ExplainType.Type planType, ExplainForm
@Override
protected RelationType visitQuery(Query node, AnalysisContext parentContext)
{
AnalysisContext context = new AnalysisContext(parentContext);
AnalysisContext context = new AnalysisContext(parentContext, new RelationType());

if (node.getApproximate().isPresent()) {
if (!experimentalSyntaxEnabled) {
Expand Down Expand Up @@ -1062,7 +1062,7 @@ protected RelationType visitQuerySpecification(QuerySpecification node, Analysis
// TODO: extract candidate names from SELECT, WHERE, HAVING, GROUP BY and ORDER BY expressions
// to pass down to analyzeFrom

AnalysisContext context = new AnalysisContext(parentContext);
AnalysisContext context = new AnalysisContext(parentContext, new RelationType());

RelationType sourceType = analyzeFrom(node, context);

Expand Down Expand Up @@ -1162,7 +1162,7 @@ protected RelationType visitJoin(Join node, AnalysisContext context)
throw new SemanticException(NOT_SUPPORTED, node, "Natural join not supported");
}

AnalysisContext leftContext = new AnalysisContext(context);
AnalysisContext leftContext = new AnalysisContext(context, new RelationType());
RelationType left = process(node.getLeft(), context);
leftContext.setLateralTupleDescriptor(left);
RelationType right = process(node.getRight(), leftContext);
Expand Down
Expand Up @@ -5084,6 +5084,50 @@ public void testScalarSubqueryWithGroupBy()
"HAVING min(orderkey) < (SELECT sum(orderkey) FROM orders WHERE orderkey < 7)");
}

@Test
public void testCorrelatedScalarSubqueries()
throws Exception
{
String errorMsg = "line .*: Correlated queries not yet supported. Invalid column reference: .*";

assertQueryFails("SELECT (SELECT l.orderkey) FROM lineitem l", errorMsg);
assertQueryFails("SELECT * FROM lineitem l WHERE 1 = (SELECT l.orderkey)", errorMsg);
assertQueryFails("SELECT * FROM lineitem l ORDER BY (SELECT l.orderkey)", errorMsg);

// group by
assertQueryFails("SELECT max(l.quantity), l.orderkey, (SELECT l.orderkey) FROM lineitem l GROUP BY l.orderkey", errorMsg);
assertQueryFails("SELECT max(l.quantity), l.orderkey FROM lineitem l GROUP BY l.orderkey HAVING max(l.quantity) < (SELECT l.orderkey)", errorMsg);
assertQueryFails("SELECT max(l.quantity), l.orderkey FROM lineitem l GROUP BY l.orderkey, (SELECT l.orderkey)", errorMsg);

// join
assertQueryFails("SELECT * FROM lineitem l1 JOIN lineitem l2 ON l1.orderkey= (SELECT l2.orderkey)", errorMsg);
assertQueryFails("SELECT * FROM lineitem l1 WHERE 1 = (SELECT count(*) FROM lineitem l2 CROSS JOIN (SELECT l1.orderkey) l3)", errorMsg);

// subrelation
assertQueryFails("SELECT * FROM lineitem l WHERE l.orderkey= (SELECT * FROM (SELECT l.orderkey))", errorMsg);
}

@Test
public void testCorrelatedInPredicateSubqueries()
{
String errorMsg = "line .*: Correlated queries not yet supported. Invalid column reference: .*";

assertQueryFails("SELECT 1 IN (SELECT l.orderkey) FROM lineitem l", errorMsg);
assertQueryFails("SELECT * FROM lineitem l WHERE 1 IN (SELECT l.orderkey)", errorMsg);
assertQueryFails("SELECT * FROM lineitem l ORDER BY 1 IN (SELECT l.orderkey)", errorMsg);

// group by
assertQueryFails("SELECT max(l.quantity), l.orderkey, 1 IN (SELECT l.orderkey) FROM lineitem l GROUP BY l.orderkey", errorMsg);
assertQueryFails("SELECT max(l.quantity), l.orderkey FROM lineitem l GROUP BY l.orderkey HAVING max(l.quantity) IN (SELECT l.orderkey)", errorMsg);
assertQueryFails("SELECT max(l.quantity), l.orderkey FROM lineitem l GROUP BY l.orderkey, 1 IN (SELECT l.orderkey)", errorMsg);

// join
assertQueryFails("SELECT * FROM lineitem l1 JOIN lineitem l2 ON l1.orderkey IN (SELECT l2.orderkey)", errorMsg);

// subrelation
assertQueryFails("SELECT * FROM lineitem l WHERE l.orderkey = (SELECT * FROM (SELECT 1 IN (SELECT l.orderkey)))", errorMsg);
}

@Test
public void testPredicatePushdown()
throws Exception
Expand Down

0 comments on commit 4fc9d7d

Please sign in to comment.