diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index ae34daf8a5c..1f8dd7ca362 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -105,6 +105,7 @@ public class Analyzer extends RuleExecutor { new ResolveRefs(), new ResolveOrdinalInOrderByAndGroupBy(), new ResolveMissingRefs(), + new ResolveFilterRefs(), new ResolveFunctions(), new ResolveAliases(), new ProjectedAggregations(), @@ -762,6 +763,68 @@ public class Analyzer extends RuleExecutor { } } + // + // Resolve aliases defined in SELECT that are referred inside the WHERE clause: + // SELECT int AS i FROM t WHERE i > 10 + // + // As such, identify all project and aggregates that have a Filter child + // and look at any resoled aliases that match and replace them. + private class ResolveFilterRefs extends AnalyzeRule { + + @Override + protected LogicalPlan rule(LogicalPlan plan) { + if (plan instanceof Project) { + Project p = (Project) plan; + if (p.child() instanceof Filter) { + Filter f = (Filter) p.child(); + Expression condition = f.condition(); + if (condition.resolved() == false && f.childrenResolved() == true) { + Expression newCondition = replaceAliases(condition, p.projections()); + if (newCondition != condition) { + return new Project(p.source(), new Filter(f.source(), f.child(), newCondition), p.projections()); + } + } + } + } + + if (plan instanceof Aggregate) { + Aggregate a = (Aggregate) plan; + if (a.child() instanceof Filter) { + Filter f = (Filter) a.child(); + Expression condition = f.condition(); + if (condition.resolved() == false && f.childrenResolved() == true) { + Expression newCondition = replaceAliases(condition, a.aggregates()); + if (newCondition != condition) { + return new Aggregate(a.source(), new Filter(f.source(), f.child(), newCondition), a.groupings(), + a.aggregates()); + } + } + } + } + + return plan; + } + + private Expression replaceAliases(Expression condition, List named) { + List aliases = new ArrayList<>(); + named.forEach(n -> { + if (n instanceof Alias) { + aliases.add((Alias) n); + } + }); + + return condition.transformDown(u -> { + boolean qualified = u.qualifier() != null; + for (Alias alias : aliases) { + if (qualified ? Objects.equals(alias.qualifiedName(), u.qualifiedName()) : Objects.equals(alias.name(), u.name())) { + return alias; + } + } + return u; + }, UnresolvedAttribute.class); + } + } + // to avoid creating duplicate functions // this rule does two iterations // 1. collect all functions diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java index 576bd233ac6..f4c8526bf47 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Alias.java @@ -7,8 +7,8 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate; -import org.elasticsearch.xpack.sql.tree.Source; import org.elasticsearch.xpack.sql.tree.NodeInfo; +import org.elasticsearch.xpack.sql.tree.Source; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.EsField; @@ -75,6 +75,10 @@ public class Alias extends NamedExpression { return qualifier; } + public String qualifiedName() { + return qualifier == null ? name() : qualifier + "." + name(); + } + @Override public Nullability nullable() { return child.nullable(); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index 992ab6e1905..415472bfe35 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -638,5 +638,16 @@ public class VerifierErrorMessagesTests extends ESTestCase { assertEquals("1:52: HAVING filter is unsupported for function [MAX(keyword)]", error("SELECT MAX(keyword) FROM test GROUP BY text HAVING MAX(keyword) > 10")); } -} + public void testProjectAliasInFilter() { + accept("SELECT int AS i FROM test WHERE i > 10"); + } + + public void testAggregateAliasInFilter() { + accept("SELECT int AS i FROM test WHERE i > 10 GROUP BY i HAVING MAX(i) > 10"); + } + + public void testProjectUnresolvedAliasInFilter() { + assertEquals("1:8: Unknown column [tni]", error("SELECT tni AS i FROM test WHERE i > 10 GROUP BY i")); + } +} \ No newline at end of file