mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-09 06:25:07 +00:00
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:
parent
d31eaf7313
commit
9584adf9d9
@ -223,12 +223,13 @@ public final class Verifier {
|
|||||||
validateInExpression(p, localFailures);
|
validateInExpression(p, localFailures);
|
||||||
validateConditional(p, localFailures);
|
validateConditional(p, localFailures);
|
||||||
|
|
||||||
|
checkFilterOnAggs(p, localFailures);
|
||||||
|
|
||||||
if (!groupingFailures.contains(p)) {
|
if (!groupingFailures.contains(p)) {
|
||||||
checkGroupBy(p, localFailures, resolvedFunctions, groupingFailures);
|
checkGroupBy(p, localFailures, resolvedFunctions, groupingFailures);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForScoreInsideFunctions(p, localFailures);
|
checkForScoreInsideFunctions(p, localFailures);
|
||||||
|
|
||||||
checkNestedUsedInGroupByOrHaving(p, localFailures);
|
checkNestedUsedInGroupByOrHaving(p, localFailures);
|
||||||
|
|
||||||
// everything checks out
|
// everything checks out
|
||||||
@ -370,7 +371,7 @@ public final class Verifier {
|
|||||||
if (!missing.isEmpty()) {
|
if (!missing.isEmpty()) {
|
||||||
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;
|
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;
|
||||||
localFailures.add(
|
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())));
|
Expressions.names(missing.keySet())));
|
||||||
groupingFailures.add(a);
|
groupingFailures.add(a);
|
||||||
return false;
|
return false;
|
||||||
@ -542,6 +543,23 @@ public final class Verifier {
|
|||||||
return false;
|
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) {
|
private static void checkForScoreInsideFunctions(LogicalPlan p, Set<Failure> localFailures) {
|
||||||
// Make sure that SCORE is only used in "top level" functions
|
// Make sure that SCORE is only used in "top level" functions
|
||||||
p.forEachExpressions(e ->
|
p.forEachExpressions(e ->
|
||||||
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function;
|
|||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.aggregate.AggregateFunction;
|
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 org.elasticsearch.xpack.sql.plan.QueryPlan;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -18,6 +19,10 @@ public abstract class Functions {
|
|||||||
return e instanceof AggregateFunction;
|
return e instanceof AggregateFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isGrouping(Expression e) {
|
||||||
|
return e instanceof GroupingFunction;
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<String, Function> collectFunctions(QueryPlan<?> plan) {
|
public static Map<String, Function> collectFunctions(QueryPlan<?> plan) {
|
||||||
Map<String, Function> resolvedFunctions = new LinkedHashMap<>();
|
Map<String, Function> resolvedFunctions = new LinkedHashMap<>();
|
||||||
plan.forEachExpressionsDown(e -> {
|
plan.forEachExpressionsDown(e -> {
|
||||||
|
@ -227,7 +227,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testGroupByHavingNonGrouped() {
|
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"));
|
error("SELECT AVG(int) FROM test GROUP BY text HAVING int > 10"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,12 +296,12 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testHavingOnColumn() {
|
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"));
|
error("SELECT int FROM test GROUP BY int HAVING int > 2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testHavingOnScalar() {
|
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)"));
|
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]",
|
": expected data type [KEYWORD], value provided is of type [INTEGER]",
|
||||||
error("SELECT * FROM test WHERE " + arbirtraryArgsfunction + "(null, null, 'foo', 4) > 1"));
|
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"));
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user