SQL: Enhance Verifier to prevent aggregate or grouping functions from (#36799)

Improve Verifier to prevent aggregate or grouping functions from
 being used in a WHERE clause.

Fix #36798
This commit is contained in:
Costin Leau 2018-12-19 17:14:01 +02:00 committed by GitHub
parent d31eaf7313
commit 9584adf9d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 6 deletions

View File

@ -223,12 +223,13 @@ public final class Verifier {
validateInExpression(p, localFailures);
validateConditional(p, localFailures);
checkFilterOnAggs(p, localFailures);
if (!groupingFailures.contains(p)) {
checkGroupBy(p, localFailures, resolvedFunctions, groupingFailures);
}
checkForScoreInsideFunctions(p, localFailures);
checkNestedUsedInGroupByOrHaving(p, localFailures);
// everything checks out
@ -370,7 +371,7 @@ public final class Verifier {
if (!missing.isEmpty()) {
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;
localFailures.add(
fail(condition, "Cannot filter HAVING on non-aggregate" + plural + " %s; consider using WHERE instead",
fail(condition, "Cannot use HAVING filter on non-aggregate" + plural + " %s; use WHERE instead",
Expressions.names(missing.keySet())));
groupingFailures.add(a);
return false;
@ -542,6 +543,23 @@ public final class Verifier {
return false;
}
private static void checkFilterOnAggs(LogicalPlan p, Set<Failure> localFailures) {
if (p instanceof Filter) {
Filter filter = (Filter) p;
if ((filter.child() instanceof Aggregate) == false) {
filter.condition().forEachDown(f -> {
if (Functions.isAggregate(f) || Functions.isGrouping(f)) {
String type = Functions.isAggregate(f) ? "aggregate" : "grouping";
localFailures.add(fail(f,
"Cannot use WHERE filtering on %s function [%s], use HAVING instead", type, Expressions.name(f)));
}
}, Function.class);
}
}
}
private static void checkForScoreInsideFunctions(LogicalPlan p, Set<Failure> localFailures) {
// Make sure that SCORE is only used in "top level" functions
p.forEachExpressions(e ->

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunction;
import org.elasticsearch.xpack.sql.expression.function.grouping.GroupingFunction;
import org.elasticsearch.xpack.sql.plan.QueryPlan;
import java.util.LinkedHashMap;
@ -18,6 +19,10 @@ public abstract class Functions {
return e instanceof AggregateFunction;
}
public static boolean isGrouping(Expression e) {
return e instanceof GroupingFunction;
}
public static Map<String, Function> collectFunctions(QueryPlan<?> plan) {
Map<String, Function> resolvedFunctions = new LinkedHashMap<>();
plan.forEachExpressionsDown(e -> {

View File

@ -227,7 +227,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
}
public void testGroupByHavingNonGrouped() {
assertEquals("1:48: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
assertEquals("1:48: Cannot use HAVING filter on non-aggregate [int]; use WHERE instead",
error("SELECT AVG(int) FROM test GROUP BY text HAVING int > 10"));
}
@ -296,12 +296,12 @@ public class VerifierErrorMessagesTests extends ESTestCase {
}
public void testHavingOnColumn() {
assertEquals("1:42: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
assertEquals("1:42: Cannot use HAVING filter on non-aggregate [int]; use WHERE instead",
error("SELECT int FROM test GROUP BY int HAVING int > 2"));
}
public void testHavingOnScalar() {
assertEquals("1:42: Cannot filter HAVING on non-aggregate [int]; consider using WHERE instead",
assertEquals("1:42: Cannot use HAVING filter on non-aggregate [int]; use WHERE instead",
error("SELECT int FROM test GROUP BY int HAVING 2 < ABS(int)"));
}
@ -474,4 +474,15 @@ public class VerifierErrorMessagesTests extends ESTestCase {
": expected data type [KEYWORD], value provided is of type [INTEGER]",
error("SELECT * FROM test WHERE " + arbirtraryArgsfunction + "(null, null, 'foo', 4) > 1"));
}
}
public void testAggsInWhere() {
assertEquals("1:33: Cannot use WHERE filtering on aggregate function [MAX(int)], use HAVING instead",
error("SELECT MAX(int) FROM test WHERE MAX(int) > 10 GROUP BY bool"));
}
public void testHistogramInFilter() {
assertEquals("1:63: Cannot use WHERE filtering on grouping function [HISTOGRAM(date)], use HAVING instead",
error("SELECT HISTOGRAM(date, INTERVAL 1 MONTH) AS h FROM test WHERE "
+ "HISTOGRAM(date, INTERVAL 1 MONTH) > CAST('2000-01-01' AS DATE) GROUP BY h"));
}
}