Skip to content

Commit 6cf8441

Browse files
committed
Refactoring Verifier
1 parent 4838b20 commit 6cf8441

File tree

1 file changed

+100
-67
lines changed
  • x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis

1 file changed

+100
-67
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Verifier.java

Lines changed: 100 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import java.util.Set;
6464
import java.util.function.BiConsumer;
6565
import java.util.function.Consumer;
66+
import java.util.function.Predicate;
6667
import java.util.stream.Stream;
6768

6869
import static org.elasticsearch.xpack.esql.common.Failure.fail;
@@ -627,9 +628,16 @@ private static void checkRemoteEnrich(LogicalPlan plan, Set<Failure> failures) {
627628
private static void checkMatchQueryPredicates(LogicalPlan plan, Set<Failure> failures) {
628629
if (plan instanceof Filter f) {
629630
Expression condition = f.condition();
630-
checkMatchPredicateTypes(plan, failures);
631-
checkCommandsBeforeMatchPredicates(plan, condition, failures);
632-
checkMatchPredicateConditions(condition, failures);
631+
checkMatchPredicateArgumentTypes(plan, failures);
632+
checkCommandsBeforeExpression(
633+
plan,
634+
condition,
635+
MatchQueryPredicate.class,
636+
lp -> lp instanceof Limit == false,
637+
e -> "[:] operator",
638+
failures
639+
);
640+
checkNotPresentInDisjunctions(condition, MatchQueryPredicate.class, mqp -> "[:] operator", failures);
633641
} else {
634642
plan.forEachExpression(
635643
MatchQueryPredicate.class,
@@ -647,7 +655,7 @@ private static void checkMatchQueryPredicates(LogicalPlan plan, Set<Failure> fai
647655
* early in the execution, rather than fail at the compute engine level.
648656
* In the future we will be able to handle MATCH at the compute and we will no longer need these checks.
649657
*/
650-
private static void checkMatchPredicateTypes(LogicalPlan plan, Set<Failure> failures) {
658+
private static void checkMatchPredicateArgumentTypes(LogicalPlan plan, Set<Failure> failures) {
651659
if (plan instanceof Filter f) {
652660
Expression condition = f.condition();
653661

@@ -670,37 +678,76 @@ private static void checkMatchPredicateTypes(LogicalPlan plan, Set<Failure> fail
670678
}
671679
}
672680

673-
private static void checkCommandsBeforeMatchPredicates(LogicalPlan plan, Expression condition, Set<Failure> failures) {
674-
condition.forEachDown(MatchQueryPredicate.class, qsf -> {
675-
plan.forEachDown(LogicalPlan.class, lp -> {
676-
if (lp instanceof Limit) {
677-
failures.add(
678-
fail(plan, "[:] operator cannot be used after {}", lp.sourceText().split(" ")[0].toUpperCase(Locale.ROOT))
679-
);
680-
}
681-
});
682-
});
683-
}
684-
685-
private static void checkMatchPredicateConditions(Expression condition, Set<Failure> failures) {
681+
/**
682+
* Checks a whether a condition contains a disjunction with the specified typeToken. Adds to failure if it does.
683+
*
684+
* @param condition condition to check for disjunctions
685+
* @param typeToken type that is forbidden in disjunctions
686+
* @param typeNameProvider provider for the type name to add in the failure message
687+
* @param failures failures collection to add to
688+
* @param <E> type of the token to look for
689+
*/
690+
private static <E extends Expression> void checkNotPresentInDisjunctions(
691+
Expression condition,
692+
Class<E> typeToken,
693+
java.util.function.Function<E, String> typeNameProvider,
694+
Set<Failure> failures
695+
) {
686696
condition.forEachUp(Or.class, or -> {
687-
checkMatchPredicateInDisjunction(failures, or, or.left());
688-
checkMatchPredicateInDisjunction(failures, or, or.right());
697+
checkNotPresentInDisjunctions(or.left(), or, typeToken, typeNameProvider, failures);
698+
checkNotPresentInDisjunctions(or.right(), or, typeToken, typeNameProvider, failures);
689699
});
690700
}
691701

692-
private static void checkMatchPredicateInDisjunction(Set<Failure> failures, Or or, Expression expression) {
693-
expression.forEachDown(MatchQueryPredicate.class, ftp -> {
694-
failures.add(fail(or, "Invalid condition [{}]. [:] operator can't be used as part of an or condition", or.sourceText()));
702+
/**
703+
* Checks a whether a condition contains a disjunction with the specified typeToken. Adds to failure if it does.
704+
*
705+
* @param parentExpression parent expression to add to the failure message
706+
* @param or disjunction that is being checked
707+
* @param typeToken type that is forbidden in disjunctions
708+
* @param failures failures collection to add to
709+
* @param <E> type of the token to look for
710+
*/
711+
private static <E extends Expression> void checkNotPresentInDisjunctions(
712+
Expression parentExpression,
713+
Or or,
714+
Class<E> typeToken,
715+
java.util.function.Function<E, String> elementName,
716+
Set<Failure> failures
717+
) {
718+
parentExpression.forEachDown(typeToken, ftp -> {
719+
failures.add(
720+
fail(or, "Invalid condition [{}]. " + elementName.apply(ftp) + " can't be used as part of an or condition", or.sourceText())
721+
);
695722
});
696723
}
697724

725+
/**
726+
* Checks full text query functions for invalid usage.
727+
*
728+
* @param plan root plan to check
729+
* @param failures failures found
730+
*/
698731
private static void checkFullTextQueryFunctions(LogicalPlan plan, Set<Failure> failures) {
699732
if (plan instanceof Filter f) {
700733
Expression condition = f.condition();
701-
checkCommandsBeforeQueryStringFunction(plan, condition, failures);
702-
checkCommandsBeforeMatchFunction(plan, condition, failures);
703-
checkFullTextFunctionsConditions(condition, failures);
734+
checkCommandsBeforeExpression(
735+
plan,
736+
condition,
737+
QueryString.class,
738+
lp -> (lp instanceof Filter || lp instanceof OrderBy || lp instanceof EsRelation),
739+
e -> "[" + e.functionName() + "] function",
740+
failures
741+
);
742+
checkCommandsBeforeExpression(
743+
plan,
744+
condition,
745+
Match.class,
746+
lp -> (lp instanceof Limit == false),
747+
e -> "[" + e.functionName() + "] function",
748+
failures
749+
);
750+
checkNotPresentInDisjunctions(condition, FullTextFunction.class, ftf -> "[" + ftf.functionName() + "] function", failures);
704751
checkFullTextFunctionsParents(condition, failures);
705752
} else {
706753
plan.forEachExpression(FullTextFunction.class, ftf -> {
@@ -709,32 +756,33 @@ private static void checkFullTextQueryFunctions(LogicalPlan plan, Set<Failure> f
709756
}
710757
}
711758

712-
private static void checkCommandsBeforeQueryStringFunction(LogicalPlan plan, Expression condition, Set<Failure> failures) {
713-
condition.forEachDown(QueryString.class, qsf -> {
714-
plan.forEachDown(LogicalPlan.class, lp -> {
715-
if ((lp instanceof Filter || lp instanceof OrderBy || lp instanceof EsRelation) == false) {
716-
failures.add(
717-
fail(
718-
plan,
719-
"[{}] function cannot be used after {}",
720-
qsf.functionName(),
721-
lp.sourceText().split(" ")[0].toUpperCase(Locale.ROOT)
722-
)
723-
);
724-
}
725-
});
726-
});
727-
}
728-
729-
private static void checkCommandsBeforeMatchFunction(LogicalPlan plan, Expression condition, Set<Failure> failures) {
730-
condition.forEachDown(Match.class, qsf -> {
759+
/**
760+
* Checks all commands that exist before a specific type satisfy conditions.
761+
*
762+
* @param plan plan that contains the condition
763+
* @param condition condition to check
764+
* @param typeToken type to check for. When a type is found in the condition, all plans before the root plan are checked
765+
* @param commandCheck check to perform on each command that precedes the plan that contains the typeToken
766+
* @param typeErrorMsgProvider provider for the type name in the error message
767+
* @param failures failures to add errors to
768+
* @param <E> class of the type to look for
769+
*/
770+
private static <E extends Expression> void checkCommandsBeforeExpression(
771+
LogicalPlan plan,
772+
Expression condition,
773+
Class<E> typeToken,
774+
Predicate<LogicalPlan> commandCheck,
775+
java.util.function.Function<E, String> typeErrorMsgProvider,
776+
Set<Failure> failures
777+
) {
778+
condition.forEachDown(typeToken, exp -> {
731779
plan.forEachDown(LogicalPlan.class, lp -> {
732-
if (lp instanceof Limit) {
780+
if (commandCheck.test(lp) == false) {
733781
failures.add(
734782
fail(
735783
plan,
736-
"[{}] function cannot be used after {}",
737-
qsf.functionName(),
784+
"{} cannot be used after {}",
785+
typeErrorMsgProvider.apply(exp),
738786
lp.sourceText().split(" ")[0].toUpperCase(Locale.ROOT)
739787
)
740788
);
@@ -743,26 +791,11 @@ private static void checkCommandsBeforeMatchFunction(LogicalPlan plan, Expressio
743791
});
744792
}
745793

746-
private static void checkFullTextFunctionsConditions(Expression condition, Set<Failure> failures) {
747-
condition.forEachUp(Or.class, or -> {
748-
checkFullTextFunctionInDisjunction(failures, or, or.left());
749-
checkFullTextFunctionInDisjunction(failures, or, or.right());
750-
});
751-
}
752-
753-
private static void checkFullTextFunctionInDisjunction(Set<Failure> failures, Or or, Expression expression) {
754-
expression.forEachDown(FullTextFunction.class, ftf -> {
755-
failures.add(
756-
fail(
757-
or,
758-
"Invalid condition [{}]. [{}] function can't be used as part of an or condition",
759-
or.sourceText(),
760-
ftf.functionName()
761-
)
762-
);
763-
});
764-
}
765-
794+
/**
795+
* Checks parents of a full text function to ensure they are allowed
796+
* @param condition condition that contains the full text function
797+
* @param failures failures to add errors to
798+
*/
766799
private static void checkFullTextFunctionsParents(Expression condition, Set<Failure> failures) {
767800
forEachFullTextFunctionParent(condition, (ftf, parent) -> {
768801
if ((parent instanceof FullTextFunction == false)

0 commit comments

Comments
 (0)