From db5f8c581dd905e9ae40695f45613b731d4fd23b Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Thu, 21 Aug 2025 15:32:51 -0400
Subject: [PATCH 01/30] WIP POC for expression join
---
.../Debug_Elasticsearch__node_2_.xml | 4 +
.../Debug_Elasticsearch__node_3_.xml | 4 +
.../org/elasticsearch/TransportVersions.java | 1 +
.../compute/operator/lookup/QueryList.java | 32 +-
.../resources/lookup-join-expression.csv-spec | 333 ++++++++++++++++++
.../xpack/esql/action/LookupFromIndexIT.java | 3 +-
.../xpack/esql/action/EsqlCapabilities.java | 5 +
.../xpack/esql/analysis/Analyzer.java | 68 +++-
.../esql/enrich/AbstractLookupService.java | 4 +
.../enrich/BinaryComparisonQueryList.java | 84 +++++
.../esql/enrich/ExpressionQueryList.java | 89 ++++-
.../esql/enrich/LookupFromIndexOperator.java | 17 +-
.../esql/enrich/LookupFromIndexService.java | 40 ++-
.../xpack/esql/enrich/MatchConfig.java | 4 +-
.../logical/PushDownJoinPastProject.java | 2 +-
.../xpack/esql/parser/LogicalPlanBuilder.java | 27 +-
.../xpack/esql/plan/logical/InlineStats.java | 2 +-
.../xpack/esql/plan/logical/Lookup.java | 31 +-
.../esql/plan/logical/join/InlineJoin.java | 2 +-
.../xpack/esql/plan/logical/join/Join.java | 40 ++-
.../esql/plan/logical/join/JoinConfig.java | 112 +++++-
.../esql/plan/logical/join/LookupJoin.java | 23 +-
.../esql/plan/physical/LookupJoinExec.java | 37 +-
.../esql/planner/LocalExecutionPlanner.java | 20 +-
.../esql/planner/mapper/LocalMapper.java | 4 +-
.../xpack/esql/planner/mapper/Mapper.java | 3 +-
.../xpack/esql/analysis/ParsingTests.java | 6 +-
.../enrich/LookupFromIndexOperatorTests.java | 8 +-
.../optimizer/LogicalPlanOptimizerTests.java | 4 +-
.../PushDownAndCombineFiltersTests.java | 4 +-
.../logical/PushDownJoinPastProjectTests.java | 191 ++++++++++
.../esql/parser/StatementParserTests.java | 2 +-
.../plan/logical/CommandLicenseTests.java | 2 +-
.../plan/logical/JoinSerializationTests.java | 2 +-
.../xpack/esql/plan/logical/JoinTests.java | 4 +-
.../logical/LookupSerializationTests.java | 4 +-
.../LookupJoinExecSerializationTests.java | 4 +-
.../esql/tree/EsqlNodeSubclassTests.java | 3 +-
38 files changed, 1127 insertions(+), 98 deletions(-)
create mode 100644 x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join-expression.csv-spec
create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/BinaryComparisonQueryList.java
diff --git a/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml b/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
index 94bb079398ffd..76bb8f72eafc9 100644
--- a/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
+++ b/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
@@ -6,6 +6,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml b/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
index aaef20fec729b..a321a38f14e57 100644
--- a/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
+++ b/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
@@ -6,6 +6,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java
index 2d6c47eefffaf..1909fe9938eb7 100644
--- a/server/src/main/java/org/elasticsearch/TransportVersions.java
+++ b/server/src/main/java/org/elasticsearch/TransportVersions.java
@@ -359,6 +359,7 @@ static TransportVersion def(int id) {
public static final TransportVersion SEMANTIC_QUERY_MULTIPLE_INFERENCE_IDS = def(9_150_0_00);
public static final TransportVersion ESQL_LOOKUP_JOIN_PRE_JOIN_FILTER = def(9_151_0_00);
public static final TransportVersion INFERENCE_API_DISABLE_EIS_RATE_LIMITING = def(9_152_0_00);
+ public static final TransportVersion ESQL_LOOKUP_JOIN_ON_EXPRESSION = def(9_143_0_00);
/*
* STOP! READ THIS FIRST! No, really,
diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
index 4d93ab63aaa5d..02005dfb57864 100644
--- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
+++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
@@ -125,7 +125,7 @@ public final Query getQuery(int position) {
* Returns the query at the given position.
*/
@Nullable
- abstract Query doGetQuery(int position, int firstValueIndex, int valueCount);
+ public abstract Query doGetQuery(int position, int firstValueIndex, int valueCount);
private Query wrapSingleValueQuery(Query query) {
assert onlySingleValueParams != null : "Requested to wrap single value query without single value params";
@@ -159,13 +159,8 @@ private Query wrapSingleValueQuery(Query query) {
* using only the {@link ElementType} of the {@link Block} to determine the
* query.
*/
- public static QueryList rawTermQueryList(
- MappedFieldType field,
- SearchExecutionContext searchExecutionContext,
- AliasFilter aliasFilter,
- Block block
- ) {
- IntFunction
*/
@Override public T visitComparison(EsqlBaseParser.ComparisonContext ctx) { return visitChildren(ctx); }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation returns the result of calling
+ * {@link #visitChildren} on {@code ctx}.
+ */
+ @Override public T visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@@ -831,12 +838,12 @@ public class EsqlBaseParserBaseVisitor extends AbstractParseTreeVisitor im
* The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.
*/
- @Override public T visitJoinCondition(EsqlBaseParser.JoinConditionContext ctx) { return visitChildren(ctx); }
+ @Override public T visitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.
*/
- @Override public T visitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx) { return visitChildren(ctx); }
+ @Override public T visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) { return visitChildren(ctx); }
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
index 6ba930fb79419..49bb4302d0352 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
@@ -901,6 +901,16 @@ public interface EsqlBaseParserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitComparison(EsqlBaseParser.ComparisonContext ctx);
+ /**
+ * Enter a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
+ * @param ctx the parse tree
+ */
+ void enterComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
+ /**
+ * Exit a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
+ * @param ctx the parse tree
+ */
+ void exitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
/**
* Enter a parse tree produced by the {@code operatorExpressionDefault}
* labeled alternative in {@link EsqlBaseParser#operatorExpression}.
@@ -1248,23 +1258,27 @@ public interface EsqlBaseParserListener extends ParseTreeListener {
*/
void exitJoinTarget(EsqlBaseParser.JoinTargetContext ctx);
/**
- * Enter a parse tree produced by {@link EsqlBaseParser#joinCondition}.
+ * Enter a parse tree produced by the {@code fieldBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
*/
- void enterJoinCondition(EsqlBaseParser.JoinConditionContext ctx);
+ void enterFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinContext ctx);
/**
- * Exit a parse tree produced by {@link EsqlBaseParser#joinCondition}.
+ * Exit a parse tree produced by the {@code fieldBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
*/
- void exitJoinCondition(EsqlBaseParser.JoinConditionContext ctx);
+ void exitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinContext ctx);
/**
- * Enter a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * Enter a parse tree produced by the {@code expressionBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
*/
- void enterJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ void enterExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx);
/**
- * Exit a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * Exit a parse tree produced by the {@code expressionBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
*/
- void exitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ void exitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
index 683629cf94e19..7c2c4d4c8f306 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
@@ -545,6 +545,12 @@ public interface EsqlBaseParserVisitor extends ParseTreeVisitor {
* @return the visitor result
*/
T visitComparison(EsqlBaseParser.ComparisonContext ctx);
+ /**
+ * Visit a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
+ * @param ctx the parse tree
+ * @return the visitor result
+ */
+ T visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
/**
* Visit a parse tree produced by the {@code operatorExpressionDefault}
* labeled alternative in {@link EsqlBaseParser#operatorExpression}.
@@ -750,15 +756,17 @@ public interface EsqlBaseParserVisitor extends ParseTreeVisitor {
*/
T visitJoinTarget(EsqlBaseParser.JoinTargetContext ctx);
/**
- * Visit a parse tree produced by {@link EsqlBaseParser#joinCondition}.
+ * Visit a parse tree produced by the {@code fieldBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
* @return the visitor result
*/
- T visitJoinCondition(EsqlBaseParser.JoinConditionContext ctx);
+ T visitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinContext ctx);
/**
- * Visit a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * Visit a parse tree produced by the {@code expressionBasedLookupJoin}
+ * labeled alternative in {@link EsqlBaseParser#joinCondition}.
* @param ctx the parse tree
* @return the visitor result
*/
- T visitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ T visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
index 451be4db743da..d3ece791c3047 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
@@ -608,6 +608,11 @@ public Expression visitArithmeticBinary(EsqlBaseParser.ArithmeticBinaryContext c
@Override
public Expression visitComparison(EsqlBaseParser.ComparisonContext ctx) {
+ return expression(ctx.comparisonExpression());
+ }
+
+ @Override
+ public Expression visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) {
Expression left = expression(ctx.left);
Expression right = expression(ctx.right);
TerminalNode op = (TerminalNode) ctx.comparisonOperator().getChild(0);
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index e6f7640c9b0ef..01ef4e8425636 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -648,13 +648,31 @@ public PlanFactory visitJoinCommand(EsqlBaseParser.JoinCommandContext ctx) {
);
var condition = ctx.joinCondition();
+ var joinInfo = typedParsing(this, condition, JoinInfo.class);
- // ON only with un-qualified names for now
- var predicates = expressions(condition.joinPredicate());
+ return p -> {
+ boolean hasRemotes = p.anyMatch(node -> {
+ if (node instanceof UnresolvedRelation r) {
+ return Arrays.stream(Strings.splitStringByCommaToArray(r.indexPattern().indexPattern()))
+ .anyMatch(RemoteClusterAware::isRemoteIndexName);
+ } else {
+ return false;
+ }
+ });
+ if (hasRemotes && EsqlCapabilities.Cap.ENABLE_LOOKUP_JOIN_ON_REMOTE.isEnabled() == false) {
+ throw new ParsingException(source, "remote clusters are not supported with LOOKUP JOIN");
+ }
+ return new LookupJoin(source, p, right, joinInfo.joinFields(), hasRemotes, Predicates.combineAnd(joinInfo.joinExpressions()));
+ };
+ }
+
+ private record JoinInfo(List joinFields, List joinExpressions) {}
+
+ @Override
+ public JoinInfo visitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinContext ctx) {
+ var predicates = visitList(this, ctx.qualifiedName(), Expression.class);
List joinFields = new ArrayList<>(predicates.size());
- List joinExpressions = new ArrayList<>(predicates.size());
for (var f : predicates) {
- f = handleNotExpression(f);
// verify each field is an unresolved attribute
if (f instanceof UnresolvedAttribute ua) {
if (ua.qualifier() != null) {
@@ -665,8 +683,29 @@ public PlanFactory visitJoinCommand(EsqlBaseParser.JoinCommandContext ctx) {
);
}
joinFields.add(ua);
- } else if (f instanceof EsqlBinaryComparison comparison) {
- joinFields.add((Attribute) comparison.left());
+ } else {
+ throw new ParsingException(
+ f.source(),
+ "JOIN ON clause only supports fields or AND of Binary Expressions at the moment, found [{}]",
+ f.sourceText()
+ );
+ }
+ }
+ validateJoinFields(joinFields);
+ return new JoinInfo(joinFields, emptyList());
+ }
+
+ @Override
+ public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) {
+ var predicates = visitList(this, ctx.comparisonExpression(), Expression.class);
+ List joinFields = new ArrayList<>(predicates.size());
+ List joinExpressions = new ArrayList<>(predicates.size());
+ for (var f : predicates) {
+ f = handleNegationOfEquals(f);
+ if (f instanceof EsqlBinaryComparison comparison
+ && comparison.left() instanceof UnresolvedAttribute left
+ && comparison.right() instanceof UnresolvedAttribute) {
+ joinFields.add(left);
joinExpressions.add(f);
} else {
throw new ParsingException(
@@ -676,9 +715,12 @@ public PlanFactory visitJoinCommand(EsqlBaseParser.JoinCommandContext ctx) {
);
}
}
+ validateJoinFields(joinFields);
+ return new JoinInfo(joinFields, joinExpressions);
+ }
- var matchFieldsCount = joinFields.size();
- if (matchFieldsCount > 1) {
+ private void validateJoinFields(List joinFields) {
+ if (joinFields.size() > 1) {
Set matchFieldNames = new LinkedHashSet<>();
for (Attribute field : joinFields) {
if (matchFieldNames.add(field.name()) == false) {
@@ -688,29 +730,13 @@ public PlanFactory visitJoinCommand(EsqlBaseParser.JoinCommandContext ctx) {
field.name()
);
}
-
}
}
-
- return p -> {
- boolean hasRemotes = p.anyMatch(node -> {
- if (node instanceof UnresolvedRelation r) {
- return Arrays.stream(Strings.splitStringByCommaToArray(r.indexPattern().indexPattern()))
- .anyMatch(RemoteClusterAware::isRemoteIndexName);
- } else {
- return false;
- }
- });
- if (hasRemotes && EsqlCapabilities.Cap.ENABLE_LOOKUP_JOIN_ON_REMOTE.isEnabled() == false) {
- throw new ParsingException(source, "remote clusters are not supported with LOOKUP JOIN");
- }
- return new LookupJoin(source, p, right, joinFields, hasRemotes, Predicates.combineAnd(joinExpressions));
- };
}
- private Expression handleNotExpression(Expression f) {
+ private Expression handleNegationOfEquals(Expression f) {
if (f instanceof Not not && not.children().size() == 1 && not.children().get(0) instanceof Equals equals) {
- // we only support NOT on Equals, by converting it to NotEquals for now
+ // we only support NOT on Equals, by converting it to NotEquals
return equals.negate();
}
return f;
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java
index c0d33505096a9..8c13dd0e29863 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/ParsingTests.java
@@ -126,15 +126,15 @@ public void testTooBigQuery() {
public void testJoinOnConstant() {
assumeTrue("LOOKUP JOIN available as snapshot only", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled());
assertEquals(
- "1:55: JOIN ON clause only supports fields or AND of Binary Expressions at the moment, found [123]",
+ "1:58: mismatched input '' expecting {'::', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '%'}",
error("row languages = 1, gender = \"f\" | lookup join test on 123")
);
assertEquals(
- "1:55: JOIN ON clause only supports fields or AND of Binary Expressions at the moment, found [\"abc\"]",
+ "1:60: mismatched input '' expecting {'::', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '%'}",
error("row languages = 1, gender = \"f\" | lookup join test on \"abc\"")
);
assertEquals(
- "1:55: JOIN ON clause only supports fields or AND of Binary Expressions at the moment, found [false]",
+ "1:60: mismatched input '' expecting {'::', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '%'}",
error("row languages = 1, gender = \"f\" | lookup join test on false")
);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
index 4b41be29ae7fc..db9f19c06af4a 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
@@ -4632,9 +4632,7 @@ public void testMixedSingleDoubleParams() {
expectError(
LoggerMessageFormat.format(null, "from test | " + command, param1, param2, param3),
List.of(paramAsConstant("f1", "f1"), paramAsConstant("f2", "f2"), paramAsConstant("f3", "f3")),
- command.contains("join")
- ? "JOIN ON clause only supports fields or AND of Binary Expressions at the moment, found"
- : "declared as a constant, cannot be used as an identifier"
+ "declared as a constant, cannot be used as an identifier"
);
}
}
From b8f9ba6a25edbe07d1b6c2ed4e0b95096c1ac068 Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Thu, 4 Sep 2025 14:20:59 -0400
Subject: [PATCH 03/30] Add more UTs and bugfixes
---
.../Debug_Elasticsearch__node_2_.xml | 2 +-
.../Debug_Elasticsearch__node_3_.xml | 2 +-
.../xpack/esql/ccq/MultiClusterSpecIT.java | 1 +
.../resources/lookup-join-expression.csv-spec | 223 ++++++++++++++++++
.../xpack/esql/analysis/Analyzer.java | 49 +++-
.../esql/enrich/ExpressionQueryList.java | 8 +-
.../esql/enrich/LookupFromIndexService.java | 37 +--
.../xpack/esql/parser/LogicalPlanBuilder.java | 1 -
.../xpack/esql/plan/logical/join/Join.java | 20 +-
.../optimizer/LogicalPlanOptimizerTests.java | 61 +++++
10 files changed, 355 insertions(+), 49 deletions(-)
diff --git a/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml b/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
index e3328d2324613..94bb079398ffd 100644
--- a/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
+++ b/.idea/runConfigurations/Debug_Elasticsearch__node_2_.xml
@@ -8,4 +8,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml b/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
index b6432bb83e937..aaef20fec729b 100644
--- a/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
+++ b/.idea/runConfigurations/Debug_Elasticsearch__node_3_.xml
@@ -8,4 +8,4 @@
-
+
\ No newline at end of file
diff --git a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java
index 6546e86b7b70a..ff34ea4cb4f4e 100644
--- a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java
+++ b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java
@@ -118,6 +118,7 @@ public MultiClusterSpecIT(
"LookupJoinOnTwoFieldsMultipleTimes",
// Lookup join after LIMIT is not supported in CCS yet
"LookupJoinAfterLimitAndRemoteEnrich",
+ "LookupJoinExpressionAfterLimitAndRemoteEnrich",
// Lookup join after FORK is not support in CCS yet
"ForkBeforeLookupJoin"
);
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join-expression.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join-expression.csv-spec
index e808d2a2a3a81..f2c975a44f80b 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join-expression.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/lookup-join-expression.csv-spec
@@ -370,6 +370,45 @@ warning:Line 3:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON lef
warning:Line 3:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+left_id:integer | left_name:keyword | id_int:integer | name_str:keyword | is_active_left:boolean | is_active_bool:boolean
+1 | Alice | null | null | true | null
+[1, 19, 21] | Sophia | null | null | true | null
+2 | Bob | null | null | false | null
+3 | Charlie | 1 | Alice | true | true
+3 | Charlie | 1 | Alice | true | true
+4 | David | 2 | Bob | false | false
+4 | David | 3 | Charlie | false | false
+5 | Eve | 1 | Alice | true | true
+5 | Eve | 1 | Alice | true | true
+5 | Eve | 3 | Charlie | true | true
+6 | null | 1 | Alice | true | true
+6 | null | 1 | Alice | true | true
+6 | null | 3 | Charlie | true | true
+6 | null | 5 | Eve | true | true
+6 | null | 5 | Eve | true | true
+7 | Grace | 2 | Bob | false | false
+7 | Grace | 3 | Charlie | false | false
+7 | Grace | 4 | David | false | false
+8 | Hank | 1 | Alice | true | true
+8 | Hank | 1 | Alice | true | true
+;
+
+lookupMultiColMixedGtEqSwapLeftRight
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM multi_column_joinable
+| RENAME id_int AS left_id, name_str AS left_name, is_active_bool AS is_active_left
+| LOOKUP JOIN multi_column_joinable_lookup ON id_int < left_id AND is_active_left == is_active_bool
+| KEEP left_id, left_name, id_int, name_str, is_active_left, is_active_bool
+| SORT left_id, left_name, id_int, name_str, is_active_left, is_active_left
+| LIMIT 20
+;
+
+warning:Line 3:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_int < left_id AND is_active_left == is_active_bool] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 3:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+
left_id:integer | left_name:keyword | id_int:integer | name_str:keyword | is_active_left:boolean | is_active_bool:boolean
1 | Alice | null | null | true | null
[1, 19, 21] | Sophia | null | null | true | null
@@ -512,3 +551,187 @@ left_id:integer | left_name:keyword | left_is_active:boolean | left_ip:ip | id_
2 | Bob | false | 192.168.1.3 | 14 | Nina | true | 192.168.1.14
2 | Bob | false | 192.168.1.3 | 16 | Paul | true | 192.168.1.16
;
+
+
+lookupJoinOnSameLeftFieldTwice
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM multi_column_joinable
+| RENAME id_int AS id_left, name_str AS name_left, is_active_bool AS is_active_left, ip_addr AS ip_addr_left
+| LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND id_left < other2
+| KEEP id_left, name_left, extra1, other1, other2
+| SORT id_left, name_left, extra1, other1, other2
+;
+
+warning:Line 3:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND id_left < other2] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 3:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+id_left:integer | name_left:keyword | extra1:keyword | other1:keyword | other2:integer
+1 | Alice | foo | alpha | 1000
+1 | Alice | foo | beta | 2000
+[1, 19, 21] | Sophia | zyx | null | null
+2 | Bob | bar | gamma | 3000
+3 | Charlie | baz | delta | 4000
+3 | Charlie | baz | epsilon | 5000
+4 | David | qux | zeta | 6000
+5 | Eve | quux | eta | 7000
+5 | Eve | quux | theta | 8000
+6 | null | corge | null | null
+7 | Grace | grault | kappa | 10000
+8 | Hank | garply | lambda | 11000
+9 | Ivy | waldo | null | null
+10 | John | fred | null | null
+12 | Liam | xyzzy | nu | 13000
+13 | Mia | thud | xi | 14000
+14 | Nina | foo2 | omicron | 15000
+15 | Oscar | bar2 | null | null
+[17, 18] | Olivia | xyz | null | null
+null | Kate | plugh | null | null
+;
+
+lookupJoinOnSameRightFieldTwice
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM multi_column_joinable
+| RENAME id_int AS id_left, name_str AS name_left, is_active_bool AS is_active_left, ip_addr AS ip_addr_left
+| LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND extra2 > id_int
+| KEEP id_left, name_left, extra1, other1, other2
+| SORT id_left, name_left, extra1, other1, other2
+;
+
+warning:Line 3:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND extra2 > id_int] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 3:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+
+id_left:integer | name_left:keyword | extra1:keyword | other1:keyword | other2:integer
+1 | Alice | foo | alpha | 1000
+1 | Alice | foo | beta | 2000
+[1, 19, 21] | Sophia | zyx | null | null
+2 | Bob | bar | gamma | 3000
+3 | Charlie | baz | delta | 4000
+3 | Charlie | baz | epsilon | 5000
+4 | David | qux | zeta | 6000
+5 | Eve | quux | eta | 7000
+5 | Eve | quux | theta | 8000
+6 | null | corge | null | null
+7 | Grace | grault | kappa | 10000
+8 | Hank | garply | lambda | 11000
+9 | Ivy | waldo | null | null
+10 | John | fred | null | null
+12 | Liam | xyzzy | nu | 13000
+13 | Mia | thud | xi | 14000
+14 | Nina | foo2 | omicron | 15000
+15 | Oscar | bar2 | null | null
+[17, 18] | Olivia | xyz | null | null
+null | Kate | plugh | null | null
+;
+
+lookupJoinOnSameRightFieldTwiceEval
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM multi_column_joinable
+| RENAME id_int AS id_left, name_str AS name_left, is_active_bool AS is_active_left, ip_addr AS ip_addr_left
+| EVAL left_id2 = ((extra2 / 200)::integer)*2
+| LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND left_id2 != id_int
+| KEEP id_left, name_left, extra1, other1, other2, left_id2
+| SORT id_left, name_left, extra1, other1, other2, left_id2
+;
+
+warning:Line 4:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str AND left_id2 != id_int] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 4:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+id_left:integer | name_left:keyword | extra1:keyword | other1:keyword | other2:integer | left_id2:integer
+1 | Alice | foo | alpha | 1000 | 0
+1 | Alice | foo | beta | 2000 | 0
+[1, 19, 21] | Sophia | zyx | null | null | 210
+2 | Bob | bar | null | null | 2
+3 | Charlie | baz | delta | 4000 | 2
+3 | Charlie | baz | epsilon | 5000 | 2
+4 | David | qux | null | null | 4
+5 | Eve | quux | eta | 7000 | 4
+5 | Eve | quux | theta | 8000 | 4
+6 | null | corge | null | null | 6
+7 | Grace | grault | kappa | 10000 | 6
+8 | Hank | garply | null | null | 8
+9 | Ivy | waldo | null | null | 8
+10 | John | fred | null | null | 10
+12 | Liam | xyzzy | null | null | 12
+13 | Mia | thud | xi | 14000 | 12
+14 | Nina | foo2 | null | null | 14
+15 | Oscar | bar2 | null | null | 14
+[17, 18] | Olivia | xyz | null | null | 170
+null | Kate | plugh | null | null | 10
+;
+
+twoLookupJoinsInSameQuery
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM multi_column_joinable
+| WHERE id_int == 1
+| RENAME id_int AS id_left, name_str AS name_left
+| LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND name_left == name_str
+| RENAME other1 AS other1_from_first_join, id_int AS id_from_first_join, name_str AS name_from_first_join
+| LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND other1_from_first_join != other1
+| KEEP id_left, name_left, other1_from_first_join, other1
+| SORT id_left, name_left, other1_from_first_join, other1
+;
+
+warning:Line 2:9: evaluation of [id_int == 1] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 2:9: java.lang.IllegalArgumentException: single-value function encountered multi-value
+warning:Line 6:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_left == id_int AND other1_from_first_join != other1] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 6:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+id_left:integer | name_left:keyword | other1_from_first_join:keyword | other1:keyword
+1 | Alice | alpha | beta
+1 | Alice | beta | alpha
+;
+
+lookupOnExpressionOnTheCoordinator
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_boolean_expression
+
+FROM employees
+| SORT emp_no
+| LIMIT 3
+| EVAL language_code_left = languages
+| LOOKUP JOIN languages_lookup ON language_code_left == language_code
+| KEEP emp_no, language_code_left, language_name
+;
+
+emp_no:integer | language_code_left:integer | language_name:keyword
+10001 | 2 | French
+10002 | 5 | null
+10003 | 4 | German
+;
+
+lookupJoinExpressionWithPushableFilterOnRight
+required_capability: join_lookup_v12
+required_capability: lookup_join_on_multiple_fields
+
+FROM multi_column_joinable
+| RENAME id_int AS id_left, is_active_bool AS is_active_left
+| LOOKUP JOIN multi_column_joinable_lookup ON id_int == id_left and is_active_left == is_active_bool
+| WHERE other2 > 5000
+| KEEP id_int, name_str, extra1, other1, other2
+| SORT id_int, name_str, extra1, other1, other2
+| LIMIT 20
+;
+
+warning:Line 3:3: evaluation of [LOOKUP JOIN multi_column_joinable_lookup ON id_int == id_left and is_active_left == is_active_bool] failed, treating result as null. Only first 20 failures recorded.
+warning:Line 3:3: java.lang.IllegalArgumentException: LOOKUP JOIN encountered multi-value
+
+id_int:integer | name_str:keyword | extra1:keyword | other1:keyword | other2:integer
+4 | David | qux | zeta | 6000
+5 | Eve | quux | eta | 7000
+5 | Eve | quux | theta | 8000
+6 | null | corge | iota | 9000
+7 | Grace | grault | kappa | 10000
+8 | Hank | garply | lambda | 11000
+12 | Liam | xyzzy | nu | 13000
+13 | Mia | thud | xi | 14000
+14 | Nina | foo2 | omicron | 15000
+;
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
index 03bd5de627c7e..2235a793bf20b 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
@@ -720,7 +720,11 @@ private LogicalPlan resolveLookup(Lookup l, List childrenOutput) {
return l;
}
- private List resolveJoinFilters(List filters, List leftOutput, List rightOutput) {
+ private List resolveJoinFiltersAndSwapIfNeeded(
+ List filters,
+ List leftOutput,
+ List rightOutput
+ ) {
if (filters.isEmpty()) {
return emptyList();
}
@@ -729,11 +733,39 @@ private List resolveJoinFilters(List filters, List resolvedFilters = new ArrayList<>(filters.size());
for (Expression filter : filters) {
- resolvedFilters.add(filter.transformUp(UnresolvedAttribute.class, ua -> maybeResolveAttribute(ua, childrenOutput)));
+ Expression filterResolved = filter.transformUp(UnresolvedAttribute.class, ua -> maybeResolveAttribute(ua, childrenOutput));
+ resolvedFilters.add(resolveAndOrientJoinCondition(filterResolved, leftOutput, rightOutput));
}
return resolvedFilters;
}
+ private Expression resolveAndOrientJoinCondition(Expression condition, List leftOutput, List rightOutput) {
+ if (condition instanceof EsqlBinaryComparison comp
+ && comp.left() instanceof Attribute leftAttr
+ && comp.right() instanceof Attribute rightAttr) {
+
+ boolean leftIsFromLeft = leftOutput.contains(leftAttr);
+ boolean rightIsFromRight = rightOutput.contains(rightAttr);
+
+ if (leftIsFromLeft && rightIsFromRight) {
+ return comp; // Correct orientation
+ }
+
+ boolean leftIsFromRight = rightOutput.contains(leftAttr);
+ boolean rightIsFromLeft = leftOutput.contains(rightAttr);
+
+ if (leftIsFromRight && rightIsFromLeft) {
+ return comp.swapLeftAndRight(); // Swapped orientation
+ }
+
+ // Invalid orientation (e.g., both from left or both from right)
+ throw new EsqlIllegalArgumentException(
+ "Join condition must be between attributes on the left and right side, but found: " + condition.sourceText()
+ );
+ }
+ return condition; // Not a binary comparison between two attributes, no change needed.
+ }
+
private Join resolveLookupJoin(LookupJoin join) {
JoinConfig config = join.config();
// for now, support only (LEFT) USING clauses
@@ -763,16 +795,19 @@ private Join resolveLookupJoin(LookupJoin join) {
List resolvedFilters = new ArrayList<>();
List matchKeys;
if (join.config().joinOnConditions() != null) {
- resolvedFilters = resolveJoinFilters(
+ resolvedFilters = resolveJoinFiltersAndSwapIfNeeded(
Predicates.splitAnd(join.config().joinOnConditions()),
join.left().output(),
join.right().output()
);
- // build leftKeys and rightKeys using the left side of the resolvedFilters.
+ // build leftKeys and rightKeys using the correct side of the resolvedFilters.
+ // resolveJoinFiltersAndSwapIfNeeded already put the left and right on the correct side
for (Expression expression : resolvedFilters) {
- if (expression instanceof EsqlBinaryComparison binaryComparison) {
- leftKeys.add((Attribute) binaryComparison.left());
- rightKeys.add((Attribute) binaryComparison.right());
+ if (expression instanceof EsqlBinaryComparison binaryComparison
+ && binaryComparison.left() instanceof Attribute leftAttribute
+ && binaryComparison.right() instanceof Attribute rightAttribute) {
+ leftKeys.add(leftAttribute);
+ rightKeys.add(rightAttribute);
} else {
throw new EsqlIllegalArgumentException("Unsupported join filter expression: " + expression);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
index 6760b6978ca81..c8afd418b96c2 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
@@ -63,7 +63,7 @@ public ExpressionQueryList(
if (queryLists.size() < 2 && (rightPreJoinPlan instanceof FilterExec == false) && request.getJoinOnConditions() == null) {
throw new IllegalArgumentException("ExpressionQueryList must have at least two QueryLists or a pre-join filter");
}
- this.queryLists = queryLists;
+ this.queryLists = new ArrayList<>(queryLists);
this.context = context;
this.aliasFilter = aliasFilter;
buildJoinOnConditions(request, clusterService, warnings);
@@ -80,10 +80,8 @@ private void buildJoinOnConditions(LookupFromIndexService.TransportRequest reque
// the join on conditions are already populated via the queryLists
// there is nothing to do here
return;
- } else {
- // clear the join on conditions in the query lists
- // the join on condition needs to come from the expression
- queryLists.clear();
+ } else if (queryLists.isEmpty() == false) {
+ throw new IllegalArgumentException("ExpressionQueryList called with both join on expression and join on fields");
}
List expressions = Predicates.splitAnd(filter);
for (Expression expr : expressions) {
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
index 66ddb30904a4a..1ea1e6b21fedd 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
@@ -110,25 +110,28 @@ protected LookupEnrichQueryGenerator queryList(
Block inputBlock,
Warnings warnings
) {
- List queryLists = new ArrayList<>();
- for (int i = 0; i < request.matchFields.size(); i++) {
- MatchConfig matchField = request.matchFields.get(i);
- QueryList q = termQueryList(
- context.getFieldType(matchField.fieldName().string()),
- context,
- aliasFilter,
- request.inputPage.getBlock(matchField.channel()),
- matchField.type()
- ).onlySingleValues(warnings, "LOOKUP JOIN encountered multi-value");
- queryLists.add(q);
- }
+ if (request.joinOnConditions == null) {
+ List queryLists = new ArrayList<>();
+ for (int i = 0; i < request.matchFields.size(); i++) {
+ MatchConfig matchField = request.matchFields.get(i);
+ QueryList q = termQueryList(
+ context.getFieldType(matchField.fieldName().string()),
+ context,
+ aliasFilter,
+ request.inputPage.getBlock(matchField.channel()),
+ matchField.type()
+ ).onlySingleValues(warnings, "LOOKUP JOIN encountered multi-value");
+ queryLists.add(q);
+ }
- PhysicalPlan physicalPlan = request.rightPreJoinPlan;
- physicalPlan = localLookupNodePlanning(physicalPlan);
- if (queryLists.size() == 1 && physicalPlan instanceof FilterExec == false && request.joinOnConditions == null) {
- return queryLists.getFirst();
+ PhysicalPlan physicalPlan = localLookupNodePlanning(request.rightPreJoinPlan);
+ if (queryLists.size() == 1 && physicalPlan instanceof FilterExec == false) {
+ return queryLists.getFirst();
+ }
+ return new ExpressionQueryList(queryLists, context, physicalPlan, clusterService, request, aliasFilter, warnings);
}
- return new ExpressionQueryList(queryLists, context, physicalPlan, clusterService, request, aliasFilter, warnings);
+ PhysicalPlan physicalPlan = localLookupNodePlanning(request.rightPreJoinPlan);
+ return new ExpressionQueryList(List.of(), context, physicalPlan, clusterService, request, aliasFilter, warnings);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index 01ef4e8425636..e23ccab515609 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -715,7 +715,6 @@ public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLoo
);
}
}
- validateJoinFields(joinFields);
return new JoinInfo(joinFields, joinExpressions);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
index 4dd2b0afb0091..9ee063e344800 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
@@ -232,23 +232,9 @@ public static List computeOutput(List leftOutput, List index2.c AND index1.d > index2.d)
- // We keep a,b,d from the left side (a, b only on left, d is join key, so we keep it from the left)
- // we keep c,e,f from the right side(c is not part of the join key on the left so gets shadowed, e,f only on right)
- Set leftJoinKeyNames = config.leftFields()
- .stream()
- .map(Attribute::name)
- .collect(java.util.stream.Collectors.toSet());
- // as we can do (left_key > right_key) now as join condition, we want to preserve the right_key
- // unless it is also a key on the left side,
- // but we need to do name equality only
- List rightOutputWithoutMatchFields = rightOutput.stream()
- .filter(attr -> leftJoinKeyNames.contains(attr.name()) == false)
- .toList();
- output = mergeOutputAttributes(rightOutputWithoutMatchFields, leftOutput);
+ // We don't allow any attributes in the joinOnConditions that don't have unique names
+ // so right always overwrites left in case of name clashes
+ output = mergeOutputAttributes(rightOutput, leftOutput);
}
} else {
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
index b612da5c6599b..78d27f278aea8 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
@@ -8994,4 +8994,65 @@ public void testTranslateMetricsGroupedByTBucketInTSMode() {
Bucket bucket = as(Alias.unwrap(eval.fields().get(0)), Bucket.class);
assertThat(Expressions.attribute(bucket.field()).name(), equalTo("@timestamp"));
}
+
+ /**
+ * Limit[1000[INTEGER],true]
+ * \_Join[LEFT,[languages{f}#8, language_code{f}#16],[languages{f}#8],[language_code{f}#16],languages{f}#8 == language_code{
+ * f}#16]
+ * |_EsqlProject[[languages{f}#8]]
+ * | \_Limit[1000[INTEGER],false]
+ * | \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..]
+ * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#16, language_name{f}#17]
+ */
+ public void testLookupJoinExpressionSwapped() {
+ LogicalPlan plan = optimizedPlan("""
+ from test
+ | keep languages
+ | lookup join languages_lookup ON language_code == languages
+ """);
+
+ var limit = asLimit(plan, 1000, true);
+ var join = as(limit.child(), Join.class);
+ assertEquals("language_code == languages", join.config().joinOnConditions().toString());
+ var equals = as(join.config().joinOnConditions(), Equals.class);
+ // we expect left and right to be swapped
+ var left = as(equals.left(), Attribute.class);
+ var right = as(equals.right(), Attribute.class);
+ assertEquals("language_code", right.name());
+ assertEquals("languages", left.name());
+ var project = as(join.left(), EsqlProject.class);
+ var limitPastJoin = asLimit(project.child(), 1000, false);
+ as(limitPastJoin.child(), EsRelation.class);
+ as(join.right(), EsRelation.class);
+ }
+
+ public void testLookupJoinExpressionAmbigiousRight() {
+ String query = """
+ from test
+ | rename languages as language_code
+ | lookup join languages_lookup ON salary == language_code
+ """;
+ IllegalStateException e = expectThrows(IllegalStateException.class, () -> plan(query));
+ assertThat(e.getMessage(), containsString("Reference [language_code] is ambiguous; matches any of "));
+ }
+
+ public void testLookupJoinExpressionAmbigiousLeft() {
+ String query = """
+ from test
+ | rename languages as language_name
+ | lookup join languages_lookup ON language_name == language_code
+ """;
+ IllegalStateException e = expectThrows(IllegalStateException.class, () -> plan(query));
+ assertThat(e.getMessage(), containsString("Reference [language_name] is ambiguous; matches any of "));
+ }
+
+ public void testLookupJoinExpressionAmbigiousBoth() {
+ String query = """
+ from test
+ | rename languages as language_code
+ | lookup join languages_lookup ON language_code != language_code
+ """;
+ IllegalStateException e = expectThrows(IllegalStateException.class, () -> plan(query));
+ assertThat(e.getMessage(), containsString("Reference [language_code] is ambiguous; matches any of "));
+ }
}
From 5cdb9c129431cd071368651f521ac69855a05c9b Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Fri, 5 Sep 2025 16:57:14 -0400
Subject: [PATCH 04/30] Address code review comments and bugfix
---
.../_snippets/commands/types/lookup-join.md | 8 +-
.../xpack/esql/action/LookupJoinTypesIT.java | 583 ++++++++++++------
.../esql/enrich/LookupFromIndexService.java | 4 +-
.../xpack/esql/enrich/MatchConfig.java | 2 -
.../logical/PushDownJoinPastProject.java | 2 +-
.../xpack/esql/parser/LogicalPlanBuilder.java | 3 +-
.../optimizer/LogicalPlanOptimizerTests.java | 21 +-
.../logical/PushDownJoinPastProjectTests.java | 92 +--
8 files changed, 425 insertions(+), 290 deletions(-)
diff --git a/docs/reference/query-languages/esql/_snippets/commands/types/lookup-join.md b/docs/reference/query-languages/esql/_snippets/commands/types/lookup-join.md
index 3e54f0ad66277..f4be29cd9954d 100644
--- a/docs/reference/query-languages/esql/_snippets/commands/types/lookup-join.md
+++ b/docs/reference/query-languages/esql/_snippets/commands/types/lookup-join.md
@@ -5,17 +5,17 @@
| field from the left index | field from the lookup index |
| --- | --- |
| boolean | boolean |
-| byte | half_float, float, double, scaled_float, byte, short, integer, long |
+| byte | byte, short, integer, long, half_float, float, double, scaled_float |
| date | date |
| date_nanos | date_nanos |
| double | half_float, float, double, scaled_float, byte, short, integer, long |
| float | half_float, float, double, scaled_float, byte, short, integer, long |
| half_float | half_float, float, double, scaled_float, byte, short, integer, long |
-| integer | half_float, float, double, scaled_float, byte, short, integer, long |
+| integer | byte, short, integer, long, half_float, float, double, scaled_float |
| ip | ip |
| keyword | keyword |
-| long | half_float, float, double, scaled_float, byte, short, integer, long |
+| long | byte, short, integer, long, half_float, float, double, scaled_float |
| scaled_float | half_float, float, double, scaled_float, byte, short, integer, long |
-| short | half_float, float, double, scaled_float, byte, short, integer, long |
+| short | byte, short, integer, long, half_float, float, double, scaled_float |
| text | keyword |
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
index c305938c09b0b..e4ec05ab5441f 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
@@ -7,10 +7,14 @@
package org.elasticsearch.xpack.esql.action;
+import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+
+import org.apache.lucene.queryparser.ext.Extensions.Pair;
import org.apache.lucene.tests.util.LuceneTestCase;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.mapper.extras.MapperExtrasPlugin;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
@@ -21,6 +25,7 @@
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.DocsV3Support;
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
+import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison.BinaryComparisonOperation;
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
import org.elasticsearch.xpack.esql.plugin.EsqlPlugin;
import org.elasticsearch.xpack.spatial.SpatialPlugin;
@@ -47,14 +52,19 @@
import static org.elasticsearch.xpack.esql.core.type.DataType.AGGREGATE_METRIC_DOUBLE;
import static org.elasticsearch.xpack.esql.core.type.DataType.BOOLEAN;
import static org.elasticsearch.xpack.esql.core.type.DataType.BYTE;
+import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_POINT;
+import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_SHAPE;
import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME;
import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS;
+import static org.elasticsearch.xpack.esql.core.type.DataType.DENSE_VECTOR;
import static org.elasticsearch.xpack.esql.core.type.DataType.DOC_DATA_TYPE;
import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
import static org.elasticsearch.xpack.esql.core.type.DataType.FLOAT;
import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHASH;
import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHEX;
import static org.elasticsearch.xpack.esql.core.type.DataType.GEOTILE;
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT;
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
import static org.elasticsearch.xpack.esql.core.type.DataType.HALF_FLOAT;
import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
import static org.elasticsearch.xpack.esql.core.type.DataType.IP;
@@ -106,157 +116,224 @@ protected Collection> nodePlugins() {
);
}
- private static final Map testConfigurations = new HashMap<>();
+ @ParametersFactory
+ public static Iterable parametersFactory() {
+ List operations = new ArrayList<>();
+ operations.add(new Object[] { null });
+ for (BinaryComparisonOperation operation : BinaryComparisonOperation.values()) {
+ operations.add(new Object[] { operation });
+ }
+ return operations;
+ }
+
+ private final BinaryComparisonOperation operationParameterized;
+
+ public LookupJoinTypesIT(BinaryComparisonOperation operation) {
+ this.operationParameterized = operation;
+ }
+
+ private static final Map, TestConfigs> testConfigurations = new HashMap<>();
static {
- // Initialize the test configurations for string tests
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("strings", TestConfigs::new);
- configs.addPasses(KEYWORD, KEYWORD);
- configs.addPasses(TEXT, KEYWORD);
- configs.addFailsUnsupported(KEYWORD, TEXT);
- }
-
- // Test integer types
- var integerTypes = List.of(BYTE, SHORT, INTEGER, LONG);
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("integers", TestConfigs::new);
- for (DataType mainType : integerTypes) {
- for (DataType lookupType : integerTypes) {
- configs.addPasses(mainType, lookupType);
+ List operations = new ArrayList<>(List.of(BinaryComparisonOperation.values()));
+ operations.add(null); // null means field-based join
+ for (BinaryComparisonOperation operation : operations) {
+
+ // Initialize the test configurations for string tests
+ {
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("strings", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ configs.addPasses(KEYWORD, KEYWORD, operation);
+ configs.addPasses(TEXT, KEYWORD, operation);
+ configs.addFailsUnsupported(KEYWORD, TEXT, operation);
+ }
+
+ // Test integer types
+ var integerTypes = List.of(BYTE, SHORT, INTEGER, LONG);
+ {
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("integers", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType mainType : integerTypes) {
+ for (DataType lookupType : integerTypes) {
+ configs.addPasses(mainType, lookupType, operation);
+ }
}
}
- }
- // Test float and double
- var floatTypes = List.of(HALF_FLOAT, FLOAT, DOUBLE, SCALED_FLOAT);
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("floats", TestConfigs::new);
- for (DataType mainType : floatTypes) {
- for (DataType lookupType : floatTypes) {
- configs.addPasses(mainType, lookupType);
+ // Test float and double
+ var floatTypes = List.of(HALF_FLOAT, FLOAT, DOUBLE, SCALED_FLOAT);
+ {
+
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("floats", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType mainType : floatTypes) {
+ for (DataType lookupType : floatTypes) {
+ configs.addPasses(mainType, lookupType, operation);
+ }
}
}
- }
- // Tests for mixed-numerical types
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("mixed-numerical", TestConfigs::new);
- for (DataType mainType : integerTypes) {
- for (DataType lookupType : floatTypes) {
- configs.addPasses(mainType, lookupType);
- configs.addPasses(lookupType, mainType);
+ // Tests for mixed-numerical types
+ {
+
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("mixed-numerical", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType mainType : integerTypes) {
+ for (DataType lookupType : floatTypes) {
+ configs.addPasses(mainType, lookupType, operation);
+ configs.addPasses(lookupType, mainType, operation);
+ }
}
}
- }
- // Tests for mixed-date/time types
- var dateTypes = List.of(DATETIME, DATE_NANOS);
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("mixed-temporal", TestConfigs::new);
- for (DataType mainType : dateTypes) {
- for (DataType lookupType : dateTypes) {
- if (mainType != lookupType) {
- configs.addFails(mainType, lookupType);
+ // Tests for mixed-date/time types
+ var dateTypes = List.of(DATETIME, DATE_NANOS);
+ {
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("mixed-temporal", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType mainType : dateTypes) {
+ for (DataType lookupType : dateTypes) {
+ if (mainType != lookupType) {
+ configs.addFails(mainType, lookupType);
+ }
}
}
}
- }
- // Union types; non-exhaustive and can be extended
- {
- TestConfigs configs = testConfigurations.computeIfAbsent("union-types", TestConfigs::new);
- configs.addUnionTypePasses(SHORT, INTEGER, INTEGER);
- configs.addUnionTypePasses(BYTE, DOUBLE, LONG);
- configs.addUnionTypePasses(DATETIME, DATE_NANOS, DATE_NANOS);
- configs.addUnionTypePasses(DATE_NANOS, DATETIME, DATETIME);
- configs.addUnionTypePasses(SCALED_FLOAT, HALF_FLOAT, DOUBLE);
- configs.addUnionTypePasses(TEXT, KEYWORD, KEYWORD);
- }
+ // Union types; non-exhaustive and can be extended
+ {
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("union-types", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ configs.addUnionTypePasses(SHORT, INTEGER, INTEGER);
+ configs.addUnionTypePasses(BYTE, DOUBLE, LONG);
+ configs.addUnionTypePasses(DATETIME, DATE_NANOS, DATE_NANOS);
+ configs.addUnionTypePasses(DATE_NANOS, DATETIME, DATETIME);
+ configs.addUnionTypePasses(SCALED_FLOAT, HALF_FLOAT, DOUBLE);
+ configs.addUnionTypePasses(TEXT, KEYWORD, KEYWORD);
- // Tests for all unsupported types
- DataType[] unsupported = Join.UNSUPPORTED_TYPES;
- {
- Collection existing = testConfigurations.values();
- TestConfigs configs = testConfigurations.computeIfAbsent("unsupported", TestConfigs::new);
- for (DataType type : unsupported) {
- if (type == NULL
- || type == DOC_DATA_TYPE
- || type == TSID_DATA_TYPE
- || type == AGGREGATE_METRIC_DOUBLE
- || type == GEOHASH
- || type == GEOTILE
- || type == GEOHEX
- || type.esType() == null
- || type.isCounter()
- || DataType.isRepresentable(type) == false) {
- // Skip unmappable types, or types not supported in ES|QL in general
- continue;
- }
- if (existingIndex(existing, type, type)) {
- // Skip existing configurations
- continue;
+ }
+
+ // Tests for all unsupported types
+ DataType[] unsupported = Join.UNSUPPORTED_TYPES;
+ boolean isNonEqualityComparison = operation == BinaryComparisonOperation.GT
+ || operation == BinaryComparisonOperation.GTE
+ || operation == BinaryComparisonOperation.LT
+ || operation == BinaryComparisonOperation.LTE;
+ {
+
+ Collection existing = testConfigurations.values();
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("unsupported", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType type : unsupported) {
+ if (type == NULL
+ || type == DOC_DATA_TYPE
+ || type == TSID_DATA_TYPE
+ || type == AGGREGATE_METRIC_DOUBLE
+ || type == GEOHASH
+ || type == GEOTILE
+ || type == GEOHEX
+ || type.esType() == null
+ || type.isCounter()
+ || DataType.isRepresentable(type) == false) {
+ // Skip unmappable types, or types not supported in ES|QL in general
+ continue;
+ }
+ if (existingIndex(existing, type, type, operation)) {
+ // Skip existing configurations
+ continue;
+ }
+ if (operation != null && type == DENSE_VECTOR
+ || isNonEqualityComparison
+ && (type == GEO_POINT || type == GEO_SHAPE || type == CARTESIAN_POINT || type == CARTESIAN_SHAPE)) {
+ configs.addUnsupportedComparisonFails(type, type, operation);
+ } else {
+ configs.addFailsUnsupported(type, type, operation);
+ }
}
- configs.addFailsUnsupported(type, type);
}
- }
- // Tests for all types where left and right are the same type
- DataType[] supported = {
- BOOLEAN,
- LONG,
- INTEGER,
- DOUBLE,
- SHORT,
- BYTE,
- FLOAT,
- HALF_FLOAT,
- DATETIME,
- DATE_NANOS,
- IP,
- KEYWORD,
- SCALED_FLOAT };
- {
- Collection existing = testConfigurations.values();
- TestConfigs configs = testConfigurations.computeIfAbsent("same", TestConfigs::new);
- for (DataType type : supported) {
- assertThat("Claiming supported for unsupported type: " + type, List.of(unsupported).contains(type), is(false));
- if (existingIndex(existing, type, type) == false) {
- // Only add the configuration if it doesn't already exist
- configs.addPasses(type, type);
+ // Tests for all types where left and right are the same type
+ DataType[] supported = {
+ BOOLEAN,
+ LONG,
+ INTEGER,
+ DOUBLE,
+ SHORT,
+ BYTE,
+ FLOAT,
+ HALF_FLOAT,
+ DATETIME,
+ DATE_NANOS,
+ IP,
+ KEYWORD,
+ SCALED_FLOAT };
+ {
+ Collection existing = testConfigurations.values();
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("same", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType type : supported) {
+ assertThat("Claiming supported for unsupported type: " + type, List.of(unsupported).contains(type), is(false));
+ if (existingIndex(existing, type, type, operation) == false) {
+ // Only add the configuration if it doesn't already exist
+ if (type == BOOLEAN && isNonEqualityComparison) {
+ // Boolean does not support inequality operations
+ configs.addUnsupportedComparisonFails(type, type, operation);
+ } else {
+ configs.addPasses(type, type, operation);
+ }
+ }
}
}
- }
- // Assert that unsupported types are not in the supported list
- for (DataType type : unsupported) {
- assertThat("Claiming supported for unsupported type: " + type, List.of(supported).contains(type), is(false));
- }
+ // Assert that unsupported types are not in the supported list
+ for (DataType type : unsupported) {
+ assertThat("Claiming supported for unsupported type: " + type, List.of(supported).contains(type), is(false));
+ }
- // Assert that unsupported+supported covers all types:
- List missing = new ArrayList<>();
- for (DataType type : DataType.values()) {
- boolean isUnsupported = List.of(unsupported).contains(type);
- boolean isSupported = List.of(supported).contains(type);
- if (isUnsupported == false && isSupported == false) {
- missing.add(type);
+ // Assert that unsupported+supported covers all types:
+ List missing = new ArrayList<>();
+ for (DataType type : DataType.values()) {
+ boolean isUnsupported = List.of(unsupported).contains(type);
+ boolean isSupported = List.of(supported).contains(type);
+ if (isUnsupported == false && isSupported == false) {
+ missing.add(type);
+ }
}
- }
- assertThat(missing + " are not in the supported or unsupported list", missing.size(), is(0));
+ assertThat(missing + " are not in the supported or unsupported list", missing.size(), is(0));
- // Tests for all other type combinations
- {
- Collection existing = testConfigurations.values();
- TestConfigs configs = testConfigurations.computeIfAbsent("others", TestConfigs::new);
- for (DataType mainType : supported) {
- for (DataType lookupType : supported) {
- if (existingIndex(existing, mainType, lookupType) == false) {
- // Only add the configuration if it doesn't already exist
- configs.addFails(mainType, lookupType);
+ // Tests for all other type combinations
+ {
+ Collection existing = testConfigurations.values();
+
+ TestConfigs configs = testConfigurations.computeIfAbsent(
+ new Pair<>("others", operation),
+ x -> new TestConfigs(x.cur(), x.cud())
+ );
+ for (DataType mainType : supported) {
+ for (DataType lookupType : supported) {
+ if (existingIndex(existing, mainType, lookupType, operation) == false) {
+ // Only add the configuration if it doesn't already exist
+ configs.addFails(mainType, lookupType);
+ }
}
}
}
}
-
// Make sure we have never added two configurations with the same lookup index name.
// This prevents accidentally adding the same test config to two different groups.
Set knownTypes = new HashSet<>();
@@ -270,8 +347,17 @@ protected Collection> nodePlugins() {
}
}
- private static boolean existingIndex(Collection existing, DataType mainType, DataType lookupType) {
- String indexName = LOOKUP_INDEX_PREFIX + mainType.esType() + "_" + lookupType.esType();
+ static String stringForOperation(BinaryComparisonOperation operation) {
+ return operation == null ? "_field" : "_" + operation.name().toLowerCase();
+ }
+
+ private static boolean existingIndex(
+ Collection existing,
+ DataType mainType,
+ DataType lookupType,
+ BinaryComparisonOperation operation
+ ) {
+ String indexName = LOOKUP_INDEX_PREFIX + mainType.esType() + "_" + lookupType.esType() + stringForOperation(operation);
return existing.stream().anyMatch(c -> c.exists(indexName));
}
@@ -279,7 +365,7 @@ private static boolean existingIndex(Collection existing, DataType
public void testOutputSupportedTypes() throws Exception {
Set signatures = new LinkedHashSet<>();
for (TestConfigs configs : testConfigurations.values()) {
- if (configs.group.equals("unsupported") || configs.group.equals("union-types")) {
+ if (configs.group.equals("unsupported") || configs.group.equals("union-types") || configs.operation != null) {
continue;
}
for (TestConfig config : configs.configs.values()) {
@@ -300,43 +386,44 @@ public void testOutputSupportedTypes() throws Exception {
}
public void testLookupJoinStrings() {
- testLookupJoinTypes("strings");
+ testLookupJoinTypes("strings", operationParameterized);
+
}
public void testLookupJoinIntegers() {
- testLookupJoinTypes("integers");
+ testLookupJoinTypes("integers", operationParameterized);
}
public void testLookupJoinFloats() {
- testLookupJoinTypes("floats");
+ testLookupJoinTypes("floats", operationParameterized);
}
public void testLookupJoinMixedNumerical() {
- testLookupJoinTypes("mixed-numerical");
+ testLookupJoinTypes("mixed-numerical", operationParameterized);
}
public void testLookupJoinMixedTemporal() {
- testLookupJoinTypes("mixed-temporal");
+ testLookupJoinTypes("mixed-temporal", operationParameterized);
}
public void testLookupJoinSame() {
- testLookupJoinTypes("same");
+ testLookupJoinTypes("same", operationParameterized);
}
public void testLookupJoinUnsupported() {
- testLookupJoinTypes("unsupported");
+ testLookupJoinTypes("unsupported", operationParameterized);
}
public void testLookupJoinOthers() {
- testLookupJoinTypes("others");
+ testLookupJoinTypes("others", operationParameterized);
}
public void testLookupJoinUnionTypes() {
- testLookupJoinTypes("union-types");
+ testLookupJoinTypes("union-types", operationParameterized);
}
- private void testLookupJoinTypes(String group) {
- TestConfigs configs = testConfigurations.get(group);
+ private void testLookupJoinTypes(String group, BinaryComparisonOperation operation) {
+ TestConfigs configs = testConfigurations.get(new Pair<>(group, operation));
initIndexes(configs);
initData(configs);
for (TestConfig config : configs.values()) {
@@ -373,6 +460,13 @@ private void initData(TestConfigs configs) {
indexRandom(true, indexRequests);
}
+ static String suffixLeftFieldName(BinaryComparisonOperation operation) {
+ if (operation != null) {
+ return "_left";
+ }
+ return "";
+ }
+
private static String propertyFor(String fieldName, DataType type) {
return String.format(Locale.ROOT, "\"%s\": %s", fieldName, sampleDataTextFor(type));
}
@@ -446,10 +540,13 @@ private record TestDocument(String indexName, String id, String source) {};
private static class TestConfigs {
final String group;
final Map configs;
+ @Nullable
+ final BinaryComparisonOperation operation; // null means field based join
- TestConfigs(String group) {
+ TestConfigs(String group, BinaryComparisonOperation operation) {
this.group = group;
this.configs = new LinkedHashMap<>();
+ this.operation = operation;
}
public List indices() {
@@ -534,22 +631,24 @@ private void add(TestConfig config) {
configs.put(config.lookupIndexName(), config);
}
- private void addPasses(DataType mainType, DataType lookupType) {
- add(new TestConfigPasses(mainType, lookupType));
+ private void addPasses(DataType mainType, DataType lookupType, BinaryComparisonOperation operation) {
+ add(new TestConfigPasses(mainType, lookupType, operation));
}
private void addUnionTypePasses(DataType mainType, DataType otherMainType, DataType lookupType) {
- add(new TestConfigPassesUnionType(mainType, otherMainType, lookupType));
+ add(new TestConfigPassesUnionType(mainType, otherMainType, lookupType, operation));
}
private void addFails(DataType mainType, DataType lookupType) {
- String fieldName = LOOKUP_INDEX_PREFIX + lookupType.esType();
+ String fieldNameLeft = LOOKUP_INDEX_PREFIX + lookupType.esType() + suffixLeftFieldName(operation);
+ String fieldNameRight = LOOKUP_INDEX_PREFIX + lookupType.esType();
+
String errorMessage = String.format(
Locale.ROOT,
"JOIN left field [%s] of type [%s] is incompatible with right field [%s] of type [%s]",
- fieldName,
+ fieldNameLeft,
mainType.widenSmallNumeric(),
- fieldName,
+ fieldNameRight,
lookupType.widenSmallNumeric()
);
add(
@@ -557,12 +656,32 @@ private void addFails(DataType mainType, DataType lookupType) {
mainType,
lookupType,
VerificationException.class,
- e -> assertThat(e.getMessage(), containsString(errorMessage))
+ e -> assertThat(e.getMessage(), containsString(errorMessage)),
+ operation
)
);
}
- private void addFailsUnsupported(DataType mainType, DataType lookupType) {
+ private void addUnsupportedComparisonFails(DataType mainType, DataType lookupType, BinaryComparisonOperation operation) {
+ String fieldNameLeft = LOOKUP_INDEX_PREFIX + lookupType.esType() + suffixLeftFieldName(operation);
+ String fieldNameRight = LOOKUP_INDEX_PREFIX + lookupType.esType();
+
+ String errorMessage1 = String.format(
+ Locale.ROOT,
+ "first argument of [%s %s %s] must be",
+ fieldNameLeft,
+ operation.symbol(),
+ fieldNameRight
+ );
+ String errorMessage2 = String.format(Locale.ROOT, "found value [%s] type [%s]", fieldNameLeft, mainType.typeName());
+
+ add(new TestConfigFails<>(mainType, lookupType, VerificationException.class, e -> {
+ assertThat(e.getMessage().toLowerCase(Locale.ROOT), containsString(errorMessage1));
+ assertThat(e.getMessage().toLowerCase(Locale.ROOT), containsString(errorMessage2));
+ }, operation));
+ }
+
+ private void addFailsUnsupported(DataType mainType, DataType lookupType, BinaryComparisonOperation operation) {
String fieldName = "lookup_" + lookupType.esType();
String errorMessage = String.format(
Locale.ROOT,
@@ -575,7 +694,8 @@ private void addFailsUnsupported(DataType mainType, DataType lookupType) {
mainType,
lookupType,
VerificationException.class,
- e -> assertThat(e.getMessage(), containsString(errorMessage))
+ e -> assertThat(e.getMessage(), containsString(errorMessage)),
+ operation
)
);
}
@@ -586,6 +706,8 @@ interface TestConfig {
DataType lookupType();
+ BinaryComparisonOperation operation();
+
default TestMapping mainIndex() {
return new TestMapping(MAIN_INDEX, List.of(propertySpecFor(mainFieldName(), mainType())), null);
}
@@ -615,7 +737,7 @@ default void validateAdditionalMainIndex() {
}
default String lookupIndexName() {
- return LOOKUP_INDEX_PREFIX + mainType().esType() + "_" + lookupType().esType();
+ return LOOKUP_INDEX_PREFIX + mainType().esType() + "_" + lookupType().esType() + stringForOperation(operation());
}
default TestMapping lookupIndex() {
@@ -635,20 +757,39 @@ default String lookupFieldName() {
return LOOKUP_INDEX_PREFIX + lookupType().esType();
}
- default String testQuery() {
- String mainField = mainFieldName();
- String lookupField = lookupFieldName();
- String lookupIndex = lookupIndexName();
-
- return String.format(
- Locale.ROOT,
- "FROM %s | RENAME %s AS %s | LOOKUP JOIN %s ON %s | KEEP other",
- MAIN_INDEX,
- mainField,
- lookupField,
- lookupIndex,
- lookupField
- );
+ default String testQuery(BinaryComparisonOperation operation) {
+ if (operation != null) {
+ String mainField = mainFieldName();
+ String lookupField = lookupFieldName();
+ String lookupFieldLeft = lookupFieldName() + suffixLeftFieldName(operation);
+ String lookupIndex = lookupIndexName();
+
+ return String.format(
+ Locale.ROOT,
+ "FROM %s | EVAL %s = %s | LOOKUP JOIN %s ON %s %s %s | KEEP other",
+ MAIN_INDEX,
+ lookupFieldLeft,
+ mainField,
+ lookupIndex,
+ lookupFieldLeft,
+ operation.symbol(),
+ lookupField
+ );
+ } else {
+ String mainField = mainFieldName();
+ String lookupField = lookupFieldName();
+ String lookupIndex = lookupIndexName();
+
+ return String.format(
+ Locale.ROOT,
+ "FROM %s | RENAME %s AS %s | LOOKUP JOIN %s ON %s | KEEP other",
+ MAIN_INDEX,
+ mainField,
+ lookupField,
+ lookupIndex,
+ lookupField
+ );
+ }
}
void doTest();
@@ -682,15 +823,24 @@ private static void validateIndex(String indexName, String fieldName, Object exp
/**
* Test case for a pair of types that can successfully be used in {@code LOOKUP JOIN}.
*/
- private record TestConfigPasses(DataType mainType, DataType lookupType) implements TestConfig {
+ private record TestConfigPasses(DataType mainType, DataType lookupType, BinaryComparisonOperation operation) implements TestConfig {
@Override
public void doTest() {
- String query = testQuery();
+ String query = testQuery(operation);
try (var response = EsqlQueryRequestBuilder.newRequestBuilder(client()).query(query).get()) {
Iterator results = response.response().column(0).iterator();
assertTrue("Expected at least one result for query: " + query, results.hasNext());
Object indexedResult = response.response().column(0).iterator().next();
- assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
+ List valueProducingOperations = new ArrayList<>();
+ valueProducingOperations.add(null);
+ valueProducingOperations.addAll(
+ List.of(BinaryComparisonOperation.EQ, BinaryComparisonOperation.GTE, BinaryComparisonOperation.LTE)
+ );
+ if (valueProducingOperations.contains(operation)) {
+ assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
+ } else {
+ assertTrue("Expected valid result: " + query, (indexedResult == null) || (indexedResult.equals("value") == false));
+ }
}
}
}
@@ -698,15 +848,26 @@ public void doTest() {
/**
* Test case for a {@code LOOKUP JOIN} where a field with a mapping conflict is cast to the type of the lookup field.
*/
- private record TestConfigPassesUnionType(DataType mainType, DataType otherMainType, DataType lookupType) implements TestConfig {
+ private record TestConfigPassesUnionType(
+ DataType mainType,
+ DataType otherMainType,
+ DataType lookupType,
+ BinaryComparisonOperation operation
+ ) implements TestConfig {
@Override
public String lookupIndexName() {
// Override so it doesn't clash with other lookup indices from non-union type tests.
- return LOOKUP_INDEX_PREFIX + mainType().esType() + "_union_" + otherMainType().esType() + "_" + lookupType().esType();
+ return LOOKUP_INDEX_PREFIX
+ + mainType().esType()
+ + "_union_"
+ + otherMainType().esType()
+ + "_"
+ + lookupType().esType()
+ + stringForOperation(operation());
}
private String additionalIndexName() {
- return mainFieldName() + "_as_" + otherMainType().typeName();
+ return mainFieldName() + "_as_" + otherMainType().typeName() + stringForOperation(operation());
}
@Override
@@ -720,37 +881,67 @@ public void validateAdditionalMainIndex() {
}
@Override
- public String testQuery() {
- String mainField = mainFieldName();
- String lookupField = lookupFieldName();
- String lookupIndex = lookupIndexName();
-
- return String.format(
- Locale.ROOT,
- "FROM %s, %s | EVAL %s = %s::%s | LOOKUP JOIN %s ON %s | KEEP other",
- MAIN_INDEX,
- additionalIndexName(),
- lookupField,
- mainField,
- lookupType.typeName(),
- lookupIndex,
- lookupField
- );
+ public String testQuery(BinaryComparisonOperation operation) {
+ if (operation == null) {
+ String mainField = mainFieldName();
+ String lookupField = lookupFieldName();
+ String lookupIndex = lookupIndexName();
+
+ return String.format(
+ Locale.ROOT,
+ "FROM %s, %s | EVAL %s = %s::%s | LOOKUP JOIN %s ON %s | KEEP other",
+ MAIN_INDEX,
+ additionalIndexName(),
+ lookupField,
+ mainField,
+ lookupType.typeName(),
+ lookupIndex,
+ lookupField
+ );
+ } else {
+ String mainField = mainFieldName();
+ String lookupField = lookupFieldName();
+ String lookupIndex = lookupIndexName();
+ String lookupFieldLeft = lookupField + suffixLeftFieldName(operation);
+
+ return String.format(
+ Locale.ROOT,
+ "FROM %s, %s | EVAL %s = %s::%s | LOOKUP JOIN %s ON %s %s %s | KEEP other",
+ MAIN_INDEX,
+ additionalIndexName(),
+ lookupFieldLeft,
+ mainField,
+ lookupType.typeName(),
+ lookupIndex,
+ lookupFieldLeft,
+ operation.symbol(),
+ lookupField
+ );
+ }
}
@Override
public void doTest() {
- String query = testQuery();
+ String query = testQuery(operation);
try (var response = EsqlQueryRequestBuilder.newRequestBuilder(client()).query(query).get()) {
Iterator results = response.response().column(0).iterator();
assertTrue("Expected at least two results for query, but result was empty: " + query, results.hasNext());
Object indexedResult = results.next();
- assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
-
- assertTrue("Expected at least two results for query: " + query, results.hasNext());
- indexedResult = results.next();
- assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
+ List valueProducingOperations = new ArrayList<>();
+ valueProducingOperations.add(null);
+ valueProducingOperations.addAll(
+ List.of(BinaryComparisonOperation.EQ, BinaryComparisonOperation.GTE, BinaryComparisonOperation.LTE)
+ );
+ if (valueProducingOperations.contains(operation)) {
+ assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
+
+ assertTrue("Expected at least two results for query: " + query, results.hasNext());
+ indexedResult = results.next();
+ assertThat("Expected valid result: " + query, indexedResult, equalTo("value"));
+ } else {
+ assertTrue("Expected valid result: " + query, (indexedResult == null) || (indexedResult.equals("value") == false));
+ }
}
}
}
@@ -758,12 +949,16 @@ public void doTest() {
/**
* Test case for a pair of types that generate an error message when used in {@code LOOKUP JOIN}.
*/
- private record TestConfigFails(DataType mainType, DataType lookupType, Class exception, Consumer assertion)
- implements
- TestConfig {
+ private record TestConfigFails(
+ DataType mainType,
+ DataType lookupType,
+ Class exception,
+ Consumer assertion,
+ BinaryComparisonOperation operation
+ ) implements TestConfig {
@Override
public void doTest() {
- String query = testQuery();
+ String query = testQuery(operation);
E e = expectThrows(
exception(),
"Expected exception " + exception().getSimpleName() + " but no exception was thrown: " + query,
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
index 1ea1e6b21fedd..501b6034caac5 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
@@ -265,7 +265,7 @@ static TransportRequest readFrom(StreamInput in, BlockFactory blockFactory) thro
}
Expression joinOnConditions = null;
if (in.getTransportVersion().onOrAfter(TransportVersions.ESQL_LOOKUP_JOIN_ON_EXPRESSION)) {
- joinOnConditions = in.readOptionalNamedWriteable(Expression.class);
+ joinOnConditions = planIn.readOptionalNamedWriteable(Expression.class);
}
TransportRequest result = new TransportRequest(
sessionId,
@@ -332,7 +332,7 @@ public void writeTo(StreamOutput out) throws IOException {
planOut.writeOptionalNamedWriteable(rightPreJoinPlan);
}
if (out.getTransportVersion().onOrAfter(TransportVersions.ESQL_LOOKUP_JOIN_ON_EXPRESSION)) {
- out.writeOptionalNamedWriteable(joinOnConditions);
+ planOut.writeOptionalNamedWriteable(joinOnConditions);
} else {
if (joinOnConditions != null) {
throw new EsqlIllegalArgumentException("LOOKUP JOIN with ON conditions is not supported on remote node");
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
index 4690c22d52d42..fcb87d3a43e07 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
@@ -29,8 +29,6 @@ public MatchConfig(FieldAttribute.FieldName fieldName, int channel, DataType typ
}
public MatchConfig(FieldAttribute.FieldName fieldName, Layout.ChannelAndType input) {
- // TODO: Using exactAttribute was supposed to handle TEXT fields with KEYWORD subfields - but we don't allow these in lookup
- // indices, so the call to exactAttribute looks redundant now.
this(fieldName, input.channel(), input.type());
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProject.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProject.java
index 14da8082ffd05..004f421c49ad0 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProject.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProject.java
@@ -49,7 +49,7 @@ protected LogicalPlan rule(Join join) {
return join;
}
- if (join.left() instanceof Project project && join.config().type() == JoinTypes.LEFT && join.config().joinOnConditions() == null) {
+ if (join.left() instanceof Project project && join.config().type() == JoinTypes.LEFT) {
AttributeMap.Builder aliasBuilder = AttributeMap.builder();
project.forEachExpression(Alias.class, a -> aliasBuilder.put(a.toAttribute(), a.child()));
var aliasesFromProject = aliasBuilder.build();
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index e23ccab515609..9128d8f37a202 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -704,8 +704,9 @@ public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLoo
f = handleNegationOfEquals(f);
if (f instanceof EsqlBinaryComparison comparison
&& comparison.left() instanceof UnresolvedAttribute left
- && comparison.right() instanceof UnresolvedAttribute) {
+ && comparison.right() instanceof UnresolvedAttribute right) {
joinFields.add(left);
+ joinFields.add(right);
joinExpressions.add(f);
} else {
throw new ParsingException(
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
index 78d27f278aea8..c0925e3d7ca7a 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
@@ -8996,13 +8996,15 @@ public void testTranslateMetricsGroupedByTBucketInTSMode() {
}
/**
- * Limit[1000[INTEGER],true]
- * \_Join[LEFT,[languages{f}#8, language_code{f}#16],[languages{f}#8],[language_code{f}#16],languages{f}#8 == language_code{
+ *
+ * Project[[languages{f}#8, language_code{f}#16, language_name{f}#17]]
+ * \_Limit[1000[INTEGER],true]
+ * \_Join[LEFT,[languages{f}#8, language_code{f}#16],[languages{f}#8],[language_code{f}#16],languages{f}#8 == language_code{
* f}#16]
- * |_EsqlProject[[languages{f}#8]]
- * | \_Limit[1000[INTEGER],false]
- * | \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..]
- * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#16, language_name{f}#17]
+ * |_Limit[1000[INTEGER],false]
+ * | \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..]
+ * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#16, language_name{f}#17]
+ *
*/
public void testLookupJoinExpressionSwapped() {
LogicalPlan plan = optimizedPlan("""
@@ -9010,8 +9012,8 @@ public void testLookupJoinExpressionSwapped() {
| keep languages
| lookup join languages_lookup ON language_code == languages
""");
-
- var limit = asLimit(plan, 1000, true);
+ var project = as(plan, Project.class);
+ var limit = asLimit(project.child(), 1000, true);
var join = as(limit.child(), Join.class);
assertEquals("language_code == languages", join.config().joinOnConditions().toString());
var equals = as(join.config().joinOnConditions(), Equals.class);
@@ -9020,8 +9022,7 @@ public void testLookupJoinExpressionSwapped() {
var right = as(equals.right(), Attribute.class);
assertEquals("language_code", right.name());
assertEquals("languages", left.name());
- var project = as(join.left(), EsqlProject.class);
- var limitPastJoin = asLimit(project.child(), 1000, false);
+ var limitPastJoin = asLimit(join.left(), 1000, false);
as(limitPastJoin.child(), EsRelation.class);
as(join.right(), EsRelation.class);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProjectTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProjectTests.java
index 746f74bb307be..43989cc854229 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProjectTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownJoinPastProjectTests.java
@@ -241,13 +241,11 @@ public void testShadowingAfterPushdown2() {
}
/**
- * EsqlProject[[languages{f}#30, emp_no{f}#27, salary{f}#32]]
+ * Project[[languages{f}#30, emp_no{f}#27, salary{f}#32]]
* \_Limit[1000[INTEGER],true]
- * \_Join[LEFT,[lang{r}#10, languages{f}#30],[lang{r}#10],[languages{f}#30],lang{r}#10 == languages{f}#30]
- * |_EsqlProject[[_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, gender{f}#18, hire_date{f}#23, job{f}#24, job.raw{f}#25,
- * languages{f}#19 AS lang2#4, last_name{f}#20, long_noidx{f}#26, salary{f}#21, emp_no{f}#16 AS lang#10]]
- * | \_Limit[1000[INTEGER],false]
- * | \_EsRelation[test][_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, ..]
+ * \_Join[LEFT,[emp_no{f}#16, languages{f}#30],[emp_no{f}#16],[languages{f}#30],emp_no{f}#16 == languages{f}#30]
+ * |_Limit[1000[INTEGER],false]
+ * | \_EsRelation[test][_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, ..]
* \_EsRelation[test_lookup][LOOKUP][emp_no{f}#27, languages{f}#30, salary{f}#32]
*/
public void testShadowingAfterPushdownExpressionJoin() {
@@ -267,8 +265,7 @@ public void testShadowingAfterPushdownExpressionJoin() {
var limit1 = asLimit(project.child(), 1000, true);
var join = as(limit1.child(), Join.class);
var lookupRel = as(join.right(), EsRelation.class);
- var project2 = as(join.left(), Project.class);
- var limit2 = asLimit(project2.child(), 1000, false);
+ var limit2 = asLimit(join.left(), 1000, false);
var mainRel = as(limit2.child(), EsRelation.class);
var projections = project.projections();
@@ -285,33 +282,14 @@ public void testShadowingAfterPushdownExpressionJoin() {
var salary = as(projections.get(2), FieldAttribute.class);
assertEquals("salary", salary.fieldName().string());
assertTrue(lookupRel.outputSet().contains(salary));
-
- var project2Projections = project2.projections();
- var lang = as(project2Projections.stream().filter(p -> "lang".equals(p.name())).findFirst().get(), Alias.class);
- var langAttr = lang.toAttribute();
- var originalEmpNo = as(lang.child(), FieldAttribute.class);
- assertEquals("emp_no", originalEmpNo.fieldName().string());
- assertTrue(mainRel.outputSet().contains(originalEmpNo));
-
- var joinConfig = join.config();
- assertSame(JoinTypes.LEFT, joinConfig.type());
- var leftKeys = joinConfig.leftFields();
- assertEquals(1, leftKeys.size());
- assertTrue(leftKeys.get(0).semanticEquals(langAttr));
- var rightKeys = joinConfig.rightFields();
- assertEquals(1, rightKeys.size());
- assertEquals("languages", rightKeys.get(0).name());
- assertTrue(lookupRel.outputSet().contains(rightKeys.get(0)));
}
/**
- * EsqlProject[[languages{f}#24, emp_no{f}#21, salary{f}#26]]
+ * Project[[languages{f}#24, emp_no{f}#21, salary{f}#26]]
* \_Limit[1000[INTEGER],true]
- * \_Join[LEFT,[lang{r}#4, languages{f}#24],[lang{r}#4],[languages{f}#24],lang{r}#4 == languages{f}#24]
- * |_EsqlProject[[_meta_field{f}#16, emp_no{f}#10, first_name{f}#11, gender{f}#12, hire_date{f}#17, job{f}#18, job.raw{f}#19,
- * languages{f}#13 AS lang#4, last_name{f}#14, long_noidx{f}#20, salary{f}#15]]
- * | \_Limit[1000[INTEGER],false]
- * | \_EsRelation[test][_meta_field{f}#16, emp_no{f}#10, first_name{f}#11, ..]
+ * \_Join[LEFT,[languages{f}#24, languages{f}#13],[languages{f}#13],[languages{f}#24],languages{f}#13 == languages{f}#24]
+ * |_Limit[1000[INTEGER],false]
+ * | \_EsRelation[test][_meta_field{f}#16, emp_no{f}#10, first_name{f}#11, ..]
* \_EsRelation[test_lookup][LOOKUP][emp_no{f}#21, languages{f}#24, salary{f}#26]
*/
public void testShadowingAfterPushdownRenameExpressionJoin() {
@@ -329,8 +307,7 @@ public void testShadowingAfterPushdownRenameExpressionJoin() {
var limit1 = asLimit(project.child(), 1000, true);
var join = as(limit1.child(), Join.class);
var lookupRel = as(join.right(), EsRelation.class);
- var project2 = as(join.left(), Project.class);
- var limit2 = asLimit(project2.child(), 1000, false);
+ var limit2 = asLimit(join.left(), 1000, false);
var mainRel = as(limit2.child(), EsRelation.class);
var projections = project.projections();
@@ -347,34 +324,15 @@ public void testShadowingAfterPushdownRenameExpressionJoin() {
var salary = as(projections.get(2), FieldAttribute.class);
assertEquals("salary", salary.fieldName().string());
assertTrue(lookupRel.outputSet().contains(salary));
-
- var project2Projections = project2.projections();
- var lang = as(project2Projections.stream().filter(p -> "lang".equals(p.name())).findFirst().get(), Alias.class);
- var langAttr = lang.toAttribute();
- var originalLanguages = as(lang.child(), FieldAttribute.class);
- assertEquals("languages", originalLanguages.fieldName().string());
- assertTrue(mainRel.outputSet().contains(originalLanguages));
-
- var joinConfig = join.config();
- assertSame(JoinTypes.LEFT, joinConfig.type());
- var leftKeys = joinConfig.leftFields();
- assertEquals(1, leftKeys.size());
- assertTrue(leftKeys.get(0).semanticEquals(langAttr));
- var rightKeys = joinConfig.rightFields();
- assertEquals(1, rightKeys.size());
- assertEquals("languages", rightKeys.get(0).name());
- assertTrue(lookupRel.outputSet().contains(rightKeys.get(0)));
}
/**
- * EsqlProject[[languages{f}#25, emp_no{f}#22, salary{f}#27]]
+ * Project[[languages{f}#25, emp_no{f}#22, salary{f}#27]]
* \_Limit[1000[INTEGER],true]
- * \_Join[LEFT,[languages{f}#25, lang{r}#4],[lang{r}#4],[languages{f}#25],lang{r}#4 == languages{f}#25]
- * |_EsqlProject[[_meta_field{f}#17, emp_no{f}#11, first_name{f}#12, gender{f}#13, hire_date{f}#18, job{f}#19, job.raw{f}#20, l
- * ast_name{f}#15, long_noidx{f}#21, salary{f}#16, lang{r}#4]]
- * | \_Eval[[languages{f}#14 + 0[INTEGER] AS lang#4]]
- * | \_Limit[1000[INTEGER],false]
- * | \_EsRelation[test][_meta_field{f}#17, emp_no{f}#11, first_name{f}#12, ..]
+ * \_Join[LEFT,[lang{r}#4, languages{f}#25],[lang{r}#4],[languages{f}#25],lang{r}#4 == languages{f}#25]
+ * |_Eval[[languages{f}#14 + 0[INTEGER] AS lang#4]]
+ * | \_Limit[1000[INTEGER],false]
+ * | \_EsRelation[test][_meta_field{f}#17, emp_no{f}#11, first_name{f}#12, ..]
* \_EsRelation[test_lookup][LOOKUP][emp_no{f}#22, languages{f}#25, salary{f}#27]
*/
public void testShadowingAfterPushdownEvalExpressionJoin() {
@@ -393,8 +351,7 @@ public void testShadowingAfterPushdownEvalExpressionJoin() {
var limit1 = asLimit(project.child(), 1000, true);
var join = as(limit1.child(), Join.class);
var lookupRel = as(join.right(), EsRelation.class);
- var project2 = as(join.left(), Project.class);
- var eval = as(project2.child(), Eval.class);
+ var eval = as(join.left(), Eval.class);
var limit2 = asLimit(eval.child(), 1000, false);
var mainRel = as(limit2.child(), EsRelation.class);
@@ -412,23 +369,6 @@ public void testShadowingAfterPushdownEvalExpressionJoin() {
var salary = as(projections.get(2), FieldAttribute.class);
assertEquals("salary", salary.fieldName().string());
assertTrue(lookupRel.outputSet().contains(salary));
-
- var lang = as(eval.fields().get(0), Alias.class);
- var langAttr = lang.toAttribute();
- var add = as(lang.child(), org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Add.class);
- var originalLanguages = as(add.left(), FieldAttribute.class);
- assertEquals("languages", originalLanguages.fieldName().string());
- assertTrue(mainRel.outputSet().contains(originalLanguages));
-
- var joinConfig = join.config();
- assertSame(JoinTypes.LEFT, joinConfig.type());
- var leftKeys = joinConfig.leftFields();
- assertEquals(1, leftKeys.size());
- assertTrue(leftKeys.get(0).semanticEquals(langAttr));
- var rightKeys = joinConfig.rightFields();
- assertEquals(1, rightKeys.size());
- assertEquals("languages", rightKeys.get(0).name());
- assertTrue(lookupRel.outputSet().contains(rightKeys.get(0)));
}
private static void assertLeftJoinConfig(
From 0e02b5c8808a242a0bbee61c67c617e3d6eb95fd Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Fri, 5 Sep 2025 17:57:50 -0400
Subject: [PATCH 05/30] Fix compile error
---
.../org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
index e4ec05ab5441f..9039cd7722a95 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java
@@ -348,7 +348,7 @@ public LookupJoinTypesIT(BinaryComparisonOperation operation) {
}
static String stringForOperation(BinaryComparisonOperation operation) {
- return operation == null ? "_field" : "_" + operation.name().toLowerCase();
+ return operation == null ? "_field" : "_" + operation.name().toLowerCase(Locale.ROOT);
}
private static boolean existingIndex(
From f50d894bb533e07b11b34279071d793aa13dd8ce Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Mon, 8 Sep 2025 09:46:29 -0400
Subject: [PATCH 06/30] Address more code review comments
---
.../elasticsearch/xpack/esql/action/EsqlCapabilities.java | 2 +-
.../elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java | 3 +++
.../xpack/esql/plan/logical/join/JoinConfig.java | 2 ++
.../xpack/esql/planner/LocalExecutionPlanner.java | 6 +++++-
4 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
index 77665cb5050fc..b763846647db7 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
@@ -1442,7 +1442,7 @@ public enum Cap {
/**
* Allow lookup join on boolean expressions
*/
- LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION,
+ LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION(Build.current().isSnapshot()),
/**
* FORK with remote indices
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index 138c7d3f76915..ea9bb5a5ce27d 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -694,6 +694,9 @@ public JoinInfo visitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinCon
@Override
public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) {
+ if (Build.current().isSnapshot() == false) {
+ throw new ParsingException(ctx.getText(), "JOIN ON clause only supports fields at the moment, found [{}]", ctx.getText());
+ }
var predicates = visitList(this, ctx.comparisonExpression(), Expression.class);
List joinFields = new ArrayList<>(predicates.size());
List joinExpressions = new ArrayList<>(predicates.size());
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
index 2af78552f46ce..869c935151fa8 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
@@ -35,9 +35,11 @@ public final class JoinConfig implements Writeable {
private final Expression joinOnConditions;
/**
+ * @param type type of join
* @param matchFields fields either from the left or right fields which decide which side is kept
* @param leftFields matched with the right fields
* @param rightFields matched with the left fields
+ * @param joinOnConditions join conditions for expression based join. If null, we assume equi-join on the left/right fields
*/
public JoinConfig(
JoinType type,
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
index 22a4287f02b89..2f7836f83c03b 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
@@ -789,6 +789,11 @@ private PhysicalOperation planLookupJoin(LookupJoinExec join, LocalExecutionPlan
if (input == null) {
throw new IllegalArgumentException("can't plan [" + join + "][" + left + "]");
}
+
+ // TODO: Using exactAttribute was supposed to handle TEXT fields with KEYWORD subfields - but we don't allow these in lookup
+ // indices, so the call to exactAttribute looks redundant now.
+ FieldAttribute.FieldName fieldName = right.exactAttribute().fieldName();
+
// we support 2 types of joins: Field name joins and Expression joins
// for Field name join, we do not ship any join on expression.
// we built the Lucene query on the field name that is passed in the MatchConfig.fieldName
@@ -800,7 +805,6 @@ private PhysicalOperation planLookupJoin(LookupJoinExec join, LocalExecutionPlan
// It is not acceptable to just use the left or right side of the operator because the same field can be joined multiple times
// e.g. LOOKUP JOIN ON left_id < right_id_1 and left_id >= right_id_2
// we want to be able to optimize this in the future and only ship the left_id once
- FieldAttribute.FieldName fieldName = right.exactAttribute().fieldName();
if (join.isOnJoinExpression()) {
fieldName = new FieldAttribute.FieldName(left.name());
}
From 0ebaf9c3cfd258dbfcd249b1ae7fcf2ccea73716 Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Mon, 8 Sep 2025 12:38:09 -0400
Subject: [PATCH 07/30] Address more code review comments
---
.../xpack/esql/analysis/Analyzer.java | 11 +-
.../esql/expression/ExpressionWritables.java | 2 +-
.../xpack/esql/parser/LogicalPlanBuilder.java | 2 +-
.../xpack/esql/plan/logical/Lookup.java | 31 +--
.../esql/plan/logical/join/LookupJoin.java | 4 -
.../PushDownAndCombineFiltersTests.java | 149 +++++++++++++-
.../esql/parser/StatementParserTests.java | 193 +++++++++++++++---
...AbstractLogicalPlanSerializationTests.java | 2 +
.../plan/logical/JoinSerializationTests.java | 28 ++-
.../logical/LookupSerializationTests.java | 4 +-
10 files changed, 349 insertions(+), 77 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
index 55fc9113e44ec..ffa64e49ac1df 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
@@ -444,14 +444,7 @@ protected LogicalPlan rule(Lookup lookup, AnalyzerContext context) {
// postpone the resolution for ResolveRefs
}
- return new Lookup(
- source,
- lookup.child(),
- tableNameExpression,
- lookup.matchFields(),
- localRelation,
- lookup.getJoinOnConditions()
- );
+ return new Lookup(source, lookup.child(), tableNameExpression, lookup.matchFields(), localRelation);
}
private LocalRelation tableMapAsRelation(Source source, Map mapTable) {
@@ -713,7 +706,7 @@ private LogicalPlan resolveLookup(Lookup l, List childrenOutput) {
matchFields.add(matchFieldChildReference);
}
if (modified) {
- return new Lookup(l.source(), l.child(), l.tableName(), matchFields, l.localRelation(), l.getJoinOnConditions());
+ return new Lookup(l.source(), l.child(), l.tableName(), matchFields, l.localRelation());
}
return l;
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
index 20de89a53780d..e7ed0d72102a4 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
@@ -249,7 +249,7 @@ private static List arithmetics() {
return List.of(Add.ENTRY, Div.ENTRY, Mod.ENTRY, Mul.ENTRY, Sub.ENTRY);
}
- private static List binaryComparisons() {
+ public static List binaryComparisons() {
return List.of(Equals.ENTRY, GreaterThan.ENTRY, GreaterThanOrEqual.ENTRY, LessThan.ENTRY, LessThanOrEqual.ENTRY, NotEquals.ENTRY);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index ea9bb5a5ce27d..bb37ebcc41327 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -600,7 +600,7 @@ public PlanFactory visitLookupCommand(EsqlBaseParser.LookupCommandContext ctx) {
Literal tableName = Literal.keyword(source, visitIndexPattern(List.of(ctx.indexPattern())));
- return p -> new Lookup(source, p, tableName, matchFields, null /* localRelation will be resolved later*/, null);
+ return p -> new Lookup(source, p, tableName, matchFields, null /* localRelation will be resolved later*/);
}
@Override
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
index ce1d875e98e15..832001bb1c718 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
@@ -7,7 +7,6 @@
package org.elasticsearch.xpack.esql.plan.logical;
-import org.elasticsearch.TransportVersions;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -45,21 +44,18 @@ public class Lookup extends UnaryPlan implements SurrogateLogicalPlan, Telemetry
// afterward, it is converted into a Join (BinaryPlan) hence why here it is not a child
private final LocalRelation localRelation;
private List lazyOutput;
- private final Expression joinOnConditions;
public Lookup(
Source source,
LogicalPlan child,
Expression tableName,
List matchFields,
- @Nullable LocalRelation localRelation,
- @Nullable Expression joinOnConditions
+ @Nullable LocalRelation localRelation
) {
super(source, child);
this.tableName = tableName;
this.matchFields = matchFields;
this.localRelation = localRelation;
- this.joinOnConditions = joinOnConditions;
}
public Lookup(StreamInput in) throws IOException {
@@ -67,11 +63,6 @@ public Lookup(StreamInput in) throws IOException {
this.tableName = in.readNamedWriteable(Expression.class);
this.matchFields = in.readNamedWriteableCollectionAsList(Attribute.class);
this.localRelation = in.readBoolean() ? new LocalRelation(in) : null;
- if (in.getTransportVersion().onOrAfter(TransportVersions.ESQL_LOOKUP_JOIN_ON_EXPRESSION)) {
- this.joinOnConditions = in.readOptionalNamedWriteable(Expression.class);
- } else {
- this.joinOnConditions = null;
- }
}
@Override
@@ -86,11 +77,6 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeBoolean(true);
localRelation.writeTo(out);
}
- if (out.getTransportVersion().onOrAfter(TransportVersions.ESQL_LOOKUP_JOIN_ON_EXPRESSION)) {
- out.writeOptionalNamedWriteable(joinOnConditions);
- } else if (joinOnConditions != null) {
- throw new IOException("LOOKUP with ON conditions is not supported on remote node");
- }
}
@Override
@@ -129,11 +115,7 @@ public JoinConfig joinConfig() {
}
}
}
- return new JoinConfig(JoinTypes.LEFT, matchFields, leftFields, rightFields, joinOnConditions);
- }
-
- public Expression getJoinOnConditions() {
- return joinOnConditions;
+ return new JoinConfig(JoinTypes.LEFT, matchFields, leftFields, rightFields, null);
}
@Override
@@ -143,12 +125,12 @@ public boolean expressionsResolved() {
@Override
public UnaryPlan replaceChild(LogicalPlan newChild) {
- return new Lookup(source(), newChild, tableName, matchFields, localRelation, joinOnConditions);
+ return new Lookup(source(), newChild, tableName, matchFields, localRelation);
}
@Override
protected NodeInfo extends LogicalPlan> info() {
- return NodeInfo.create(this, Lookup::new, child(), tableName, matchFields, localRelation, joinOnConditions);
+ return NodeInfo.create(this, Lookup::new, child(), tableName, matchFields, localRelation);
}
@Override
@@ -176,12 +158,11 @@ public boolean equals(Object o) {
Lookup lookup = (Lookup) o;
return Objects.equals(tableName, lookup.tableName)
&& Objects.equals(matchFields, lookup.matchFields)
- && Objects.equals(localRelation, lookup.localRelation)
- && Objects.equals(joinOnConditions, lookup.joinOnConditions);
+ && Objects.equals(localRelation, lookup.localRelation);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), tableName, matchFields, localRelation, joinOnConditions);
+ return Objects.hash(super.hashCode(), tableName, matchFields, localRelation);
}
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
index 29ff17e218845..8b1f8fbad2a5e 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
@@ -42,10 +42,6 @@ public LookupJoin(
this(source, left, right, new UsingJoinType(LEFT, joinFields), emptyList(), emptyList(), emptyList(), isRemote, joinOnConditions);
}
- public LookupJoin(Source source, LogicalPlan left, LogicalPlan right, List joinFields) {
- this(source, left, right, new UsingJoinType(LEFT, joinFields), emptyList(), emptyList(), emptyList(), false, null);
- }
-
public LookupJoin(
Source source,
LogicalPlan left,
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
index 250481f06d3f8..81e45411fa260 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
@@ -15,6 +15,7 @@
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
import org.elasticsearch.xpack.esql.core.expression.MetadataAttribute;
+import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.aggregate.Count;
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
@@ -375,7 +376,7 @@ private static EsRelation relation(List fieldAttributes) {
}
public void testPushDownFilterPastLeftJoinWithPushable() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
EsRelation left = (EsRelation) join.left();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
@@ -393,7 +394,7 @@ public void testPushDownFilterPastLeftJoinWithPushable() {
}
public void testPushDownFilterPastLeftJoinWithExistingFilter() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
EsRelation left = (EsRelation) join.left();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
@@ -434,7 +435,7 @@ public void testPushDownFilterPastLeftJoinWithExistingFilter() {
}
public void testDoNotPushDownExistingFilterAgain() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
EsRelation left = (EsRelation) join.left();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
@@ -461,7 +462,7 @@ public void testDoNotPushDownExistingFilterAgain() {
}
public void testPushDownFilterPastLeftJoinWithExistingFilterCalledTwice() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
EsRelation left = (EsRelation) join.left();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
@@ -488,7 +489,7 @@ public void testPushDownFilterPastLeftJoinWithExistingFilterCalledTwice() {
}
public void testPushDownFilterPastLeftJoinWithNonPushable() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
// Non-pushable filter
@@ -503,7 +504,7 @@ public void testPushDownFilterPastLeftJoinWithNonPushable() {
}
public void testPushDownFilterPastLeftJoinWithPartiallyPushableAnd() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
EsRelation left = (EsRelation) join.left();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
@@ -525,7 +526,7 @@ public void testPushDownFilterPastLeftJoinWithPartiallyPushableAnd() {
}
public void testPushDownFilterPastLeftJoinWithOr() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
Expression pushableCondition = greaterThanOf(c, ONE);
@@ -543,7 +544,7 @@ public void testPushDownFilterPastLeftJoinWithOr() {
}
public void testPushDownFilterPastLeftJoinWithNotButStillPushable() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
Expression pushableCondition = greaterThanOf(c, ONE);
@@ -560,7 +561,7 @@ public void testPushDownFilterPastLeftJoinWithNotButStillPushable() {
}
public void testPushDownFilterPastLeftJoinWithNotNonPushable() {
- Join join = createLeftJoin();
+ Join join = createLeftJoinOnFields();
FieldAttribute c = (FieldAttribute) join.right().output().get(0);
Expression nonPushableCondition = new IsNull(EMPTY, c);
@@ -830,7 +831,7 @@ public void testPushDownFilterPastTwoLookupJoins() {
assertThat(rightFilter.condition().toString(), is("language_name > \"a\""));
}
- private Join createLeftJoin() {
+ private Join createLeftJoinOnFields() {
FieldAttribute a = getFieldAttribute("a");
FieldAttribute b = getFieldAttribute("b");
FieldAttribute c = getFieldAttribute("c");
@@ -840,4 +841,132 @@ private Join createLeftJoin() {
JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(b), List.of(a, b), List.of(b, c), null);
return new Join(EMPTY, left, right, joinConfig);
}
+
+ private Join createLeftJoinOnExpression() {
+ FieldAttribute a = getFieldAttribute("a");
+ FieldAttribute b1 = getFieldAttribute("b1");
+ FieldAttribute b2 = getFieldAttribute("b2");
+ FieldAttribute c = getFieldAttribute("c");
+ EsRelation left = relation(List.of(a, b1));
+ EsRelation right = relation(List.of(c, b2));
+ Expression joinOnCondition = new GreaterThanOrEqual(Source.EMPTY, b1, b2);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(b1), List.of(a, b1, b2), List.of(b2, c), joinOnCondition);
+ return new Join(EMPTY, left, right, joinConfig);
+ }
+
+ public void testLeftJoinOnExpressionPushable() {
+ Join join = createLeftJoinOnExpression();
+ EsRelation left = (EsRelation) join.left();
+ FieldAttribute c = (FieldAttribute) join.right().output().get(0);
+
+ // Pushable filter
+ Expression pushableCondition = greaterThanOf(c, ONE);
+ Filter filter = new Filter(EMPTY, join, pushableCondition);
+ LogicalPlan optimized = new PushDownAndCombineFilters().apply(filter, optimizerContext);
+ // The filter should still be on top
+ Filter topFilter = as(optimized, Filter.class);
+ assertEquals(pushableCondition, topFilter.condition());
+ Join optimizedJoin = as(topFilter.child(), Join.class);
+ assertEquals(left, optimizedJoin.left());
+ Filter rightFilter = as(optimizedJoin.right(), Filter.class);
+ assertEquals(pushableCondition, rightFilter.condition());
+ }
+
+ public void testPushDownFilterPastLeftJoinExpressionWithPartiallyPushableAnd() {
+ Join join = createLeftJoinOnExpression();
+ EsRelation left = (EsRelation) join.left();
+ FieldAttribute c = (FieldAttribute) join.right().output().get(0);
+
+ Expression pushableCondition = greaterThanOf(c, ONE);
+ Expression nonPushableCondition = new IsNull(EMPTY, c);
+
+ // Partially pushable filter
+ Expression partialCondition = new And(EMPTY, pushableCondition, nonPushableCondition);
+ Filter filter = new Filter(EMPTY, join, partialCondition);
+ LogicalPlan optimized = new PushDownAndCombineFilters().apply(filter, optimizerContext);
+ Filter topFilter = as(optimized, Filter.class);
+ // The top filter condition should be the original one
+ assertEquals(partialCondition, topFilter.condition());
+ Join optimizedJoin = as(topFilter.child(), Join.class);
+ assertEquals(left, optimizedJoin.left());
+ Filter rightFilter = as(optimizedJoin.right(), Filter.class);
+ // Only the pushable part should be a candidate
+ assertEquals(pushableCondition, rightFilter.condition());
+ }
+
+ /**
+ *Limit[1000[INTEGER],false]
+ * \_Filter[ISNULL(language_name{f}#17)]
+ * \_Join[LEFT,[languages{f}#8, language_code{f}#16],[languages{f}#8],[language_code{f}#16],languages{f}#8 > language_code{f
+ * }#16]
+ * |_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..]
+ * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#16, language_name{f}#17]
+ *
+ */
+ public void testDoNotPushDownIsNullFilterPastLookupJoinExpression() {
+ var plan = plan("""
+ FROM test
+ | LOOKUP JOIN languages_lookup ON languages > language_code
+ | WHERE language_name IS NULL
+ """);
+
+ var limit = as(plan, Limit.class);
+ var filter = as(limit.child(), Filter.class);
+ var join = as(filter.child(), Join.class);
+ assertThat(join.right(), instanceOf(EsRelation.class));
+ }
+
+ /**
+ * Limit[1000[INTEGER],false]
+ * \_Filter[ISNOTNULL(language_name{f}#21) AND language_name{f}#21 > a[KEYWORD] AND LIKE(language_name{f}#21, "*b", false)
+ * AND COALESCE(language_name{f}#21,c[KEYWORD]) == c[KEYWORD] AND RLIKE(language_name{f}#21, "f.*", false)]
+ * \_Join[LEFT,[languages{f}#12, language_code{f}#20],[languages{f}#12],[language_code{f}#20],languages{f}#12 == language_code
+ * {f}#20]
+ * |_EsRelation[test][_meta_field{f}#15, emp_no{f}#9, first_name{f}#10, g..]
+ * \_Filter[ISNOTNULL(language_name{f}#21) AND language_name{f}#21 > a[KEYWORD] AND LIKE(language_name{f}#21, "*b", false)
+ * AND RLIKE(language_name{f}#21, "f.*", false)]
+ * \_EsRelation[languages_lookup][LOOKUP][language_code{f}#20, language_name{f}#21]
+ */
+ public void testPushDownLookupJoinExpressionMultipleWhere() {
+ var plan = plan("""
+ FROM test
+ | LOOKUP JOIN languages_lookup ON languages <= language_code
+ | WHERE language_name IS NOT NULL
+ | WHERE language_name > "a"
+ | WHERE language_name LIKE "*b"
+ | WHERE COALESCE(language_name, "c") == "c"
+ | WHERE language_name RLIKE "f.*"
+ """);
+
+ var limit = as(plan, Limit.class);
+ var topFilter = as(limit.child(), Filter.class);
+
+ // Verify the top-level filter contains all 5 original conditions combined
+ Set expectedAllFilters = Set.of(
+ "language_name IS NOT NULL",
+ "language_name > \"a\"",
+ "language_name LIKE \"*b\"",
+ "COALESCE(language_name, \"c\") == \"c\"",
+ "language_name RLIKE \"f.*\""
+ );
+
+ Set actualAllFilters = new HashSet<>(Predicates.splitAnd(topFilter.condition()).stream().map(Object::toString).toList());
+ assertEquals(expectedAllFilters, actualAllFilters);
+
+ // Verify the join is below the top-level filter
+ var join = as(topFilter.child(), Join.class);
+
+ // Verify a new filter with only the pushable predicates has been pushed to the right side of the join
+ var rightFilter = as(join.right(), Filter.class);
+ Set expectedPushedFilters = Set.of(
+ "language_name IS NOT NULL",
+ "language_name > \"a\"",
+ "language_name LIKE \"*b\"",
+ "language_name RLIKE \"f.*\""
+ );
+ Set actualPushedFilters = new HashSet<>(
+ Predicates.splitAnd(rightFilter.condition()).stream().map(Object::toString).toList()
+ );
+ assertEquals(expectedPushedFilters, actualPushedFilters);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
index 189c5070d6510..32f5cc6fd919d 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java
@@ -13,6 +13,7 @@
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexMode;
+import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
import org.elasticsearch.xpack.esql.core.capabilities.UnresolvedException;
@@ -33,12 +34,14 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.ToInteger;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.regex.RLike;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.regex.WildcardLike;
+import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
import org.elasticsearch.xpack.esql.expression.predicate.logical.Not;
import org.elasticsearch.xpack.esql.expression.predicate.logical.Or;
import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Add;
import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Div;
import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Mod;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals;
+import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThan;
@@ -3129,25 +3132,103 @@ public void testValidFromPattern() {
assertThat(as(plan, UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(basePattern)));
}
- public void testValidJoinPattern() {
+ public void testValidJoinPatternFieldJoin() {
assumeTrue("LOOKUP JOIN requires corresponding capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled());
var basePattern = randomIndexPatterns(without(CROSS_CLUSTER));
var joinPattern = randomIndexPattern(without(WILDCARD_PATTERN), without(CROSS_CLUSTER), without(INDEX_SELECTOR));
- var onField = randomIdentifier();
+ var numberOfOnFields = randomIntBetween(1, 5);
+ List existingIdentifiers = new ArrayList<>();
+ StringBuilder onFields = new StringBuilder();
+ for (var i = 0; i < numberOfOnFields; i++) {
+ if (randomBoolean()) {
+ onFields.append(" ");
+ }
+ String onField = randomValueOtherThanMany(existingIdentifiers::contains, () -> randomIdentifier());
+ existingIdentifiers.add(onField);
+ onFields.append(onField);
+ if (randomBoolean()) {
+ onFields.append(" ");
+ }
+ if (i < numberOfOnFields - 1) {
+ onFields.append(", ");
+ }
- var plan = statement("FROM " + basePattern + " | LOOKUP JOIN " + joinPattern + " ON " + onField);
+ }
+ var plan = statement("FROM " + basePattern + " | LOOKUP JOIN " + joinPattern + " ON " + onFields);
var join = as(plan, LookupJoin.class);
assertThat(as(join.left(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(basePattern)));
assertThat(as(join.right(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(joinPattern)));
var joinType = as(join.config().type(), JoinTypes.UsingJoinType.class);
- assertThat(joinType.columns(), hasSize(1));
- assertThat(as(joinType.columns().getFirst(), UnresolvedAttribute.class).name(), equalTo(onField));
+ assertThat(joinType.columns(), hasSize(numberOfOnFields));
+ for (int i = 0; i < numberOfOnFields; i++) {
+ assertThat(as(joinType.columns().get(i), UnresolvedAttribute.class).name(), equalTo(existingIdentifiers.get(i)));
+ }
assertThat(joinType.coreJoin().joinName(), equalTo("LEFT OUTER"));
}
+ public void testValidJoinPatternExpressionJoin() {
+ assumeTrue("LOOKUP JOIN requires corresponding capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled());
+
+ var basePattern = randomIndexPatterns(without(CROSS_CLUSTER));
+ var joinPattern = randomIndexPattern(without(WILDCARD_PATTERN), without(CROSS_CLUSTER), without(INDEX_SELECTOR));
+ var numberOfExpressions = randomIntBetween(1, 5);
+
+ var expressions = new ArrayList, EsqlBinaryComparison.BinaryComparisonOperation>>();
+ StringBuilder onExpressionString = new StringBuilder();
+
+ for (var i = 0; i < numberOfExpressions; i++) {
+ var left = randomIdentifier();
+ var right = randomIdentifier();
+ var op = randomBinaryComparisonOperation();
+ expressions.add(new Tuple<>(new Tuple<>(left, right), op));
+
+ onExpressionString.append(left);
+ if (randomBoolean()) {
+ onExpressionString.append(" ");
+ }
+ onExpressionString.append(op.symbol());
+ if (randomBoolean()) {
+ onExpressionString.append(" ");
+ }
+ onExpressionString.append(right);
+
+ if (i < numberOfExpressions - 1) {
+ onExpressionString.append(" AND ");
+ }
+ }
+
+ var plan = statement("FROM " + basePattern + " | LOOKUP JOIN " + joinPattern + " ON " + onExpressionString);
+
+ var join = as(plan, LookupJoin.class);
+ assertThat(as(join.left(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(basePattern)));
+ assertThat(as(join.right(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(joinPattern)));
+
+ var joinType = join.config().type();
+ assertThat(joinType.joinName(), startsWith("LEFT OUTER"));
+
+ List actualExpressions = Predicates.splitAnd(join.config().joinOnConditions());
+ assertThat(actualExpressions.size(), equalTo(numberOfExpressions));
+
+ for (int i = 0; i < numberOfExpressions; i++) {
+ var expected = expressions.get(i);
+ var actual = actualExpressions.get(i);
+
+ assertThat(actual, instanceOf(EsqlBinaryComparison.class));
+ var actualComp = (EsqlBinaryComparison) actual;
+
+ assertThat(((UnresolvedAttribute) actualComp.left()).name(), equalTo(expected.v1().v1()));
+ assertThat(((UnresolvedAttribute) actualComp.right()).name(), equalTo(expected.v1().v2()));
+ assertThat(actualComp.getFunctionType(), equalTo(expected.v2()));
+ }
+ }
+
+ private EsqlBinaryComparison.BinaryComparisonOperation randomBinaryComparisonOperation() {
+ return randomFrom(EsqlBinaryComparison.BinaryComparisonOperation.values());
+ }
+
public void testInvalidFromPatterns() {
var sourceCommands = EsqlCapabilities.Cap.METRICS_COMMAND.isEnabled() ? new String[] { "FROM", "TS" } : new String[] { "FROM" };
var indexIsBlank = "Blank index specified in index pattern";
@@ -3270,25 +3351,48 @@ public void testInvalidPatternsWithIntermittentQuotes() {
}
}
- public void testValidJoinPatternWithRemote() {
+ public void testValidJoinPatternWithRemoteFieldJoin() {
+ testValidJoinPatternWithRemote(randomIdentifier());
+ }
+
+ public void testValidJoinPatternWithRemoteExpressionJoin() {
+ testValidJoinPatternWithRemote(singleExpressionJoinClause());
+ }
+
+ private void testValidJoinPatternWithRemote(String onClause) {
assumeTrue("LOOKUP JOIN requires corresponding capability", EsqlCapabilities.Cap.ENABLE_LOOKUP_JOIN_ON_REMOTE.isEnabled());
var fromPatterns = randomIndexPatterns(CROSS_CLUSTER);
var joinPattern = randomIndexPattern(without(CROSS_CLUSTER), without(WILDCARD_PATTERN), without(INDEX_SELECTOR));
- var plan = statement("FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier());
+ var plan = statement("FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause);
var join = as(plan, LookupJoin.class);
assertThat(as(join.left(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(fromPatterns)));
assertThat(as(join.right(), UnresolvedRelation.class).indexPattern().indexPattern(), equalTo(unquoteIndexPattern(joinPattern)));
}
- public void testInvalidJoinPatterns() {
+ public void testInvalidJoinPatternsFieldJoin() {
+ testInvalidJoinPatterns(randomIdentifier());
+ }
+
+ public void testInvalidJoinPatternsExpressionJoin() {
+ testInvalidJoinPatterns(singleExpressionJoinClause());
+ }
+
+ private String singleExpressionJoinClause() {
+ var left = randomIdentifier();
+ var right = randomValueOtherThan(left, ESTestCase::randomIdentifier);
+ var op = randomBinaryComparisonOperation();
+ return left + (randomBoolean() ? " " : "") + op.symbol() + (randomBoolean() ? " " : "") + right;
+ }
+
+ private void testInvalidJoinPatterns(String onClause) {
assumeTrue("LOOKUP JOIN requires corresponding capability", EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled());
{
// wildcard
var joinPattern = randomIndexPattern(WILDCARD_PATTERN, without(CROSS_CLUSTER), without(INDEX_SELECTOR));
expectError(
- "FROM " + randomIndexPatterns() + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + randomIndexPatterns() + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"invalid index pattern [" + unquoteIndexPattern(joinPattern) + "], * is not allowed in LOOKUP JOIN"
);
}
@@ -3297,7 +3401,7 @@ public void testInvalidJoinPatterns() {
var fromPatterns = randomIndexPatterns(without(CROSS_CLUSTER));
var joinPattern = randomIndexPattern(CROSS_CLUSTER, without(WILDCARD_PATTERN), without(INDEX_SELECTOR));
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"invalid index pattern [" + unquoteIndexPattern(joinPattern) + "], remote clusters are not supported with LOOKUP JOIN"
);
}
@@ -3307,7 +3411,7 @@ public void testInvalidJoinPatterns() {
var fromPatterns = quote(randomIdentifier()) + ":" + unquoteIndexPattern(randomIndexPattern(without(CROSS_CLUSTER)));
var joinPattern = randomIndexPattern();
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
// Since the from pattern is partially quoted, we get an error at the end of the partially quoted string.
" mismatched input ':'"
);
@@ -3318,7 +3422,7 @@ public void testInvalidJoinPatterns() {
var fromPatterns = randomIdentifier() + ":" + quote(randomIndexPatterns(without(CROSS_CLUSTER)));
var joinPattern = randomIndexPattern();
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
// Since the from pattern is partially quoted, we get an error at the beginning of the partially quoted
// index name that we're expecting an unquoted string.
"expecting UNQUOTED_SOURCE"
@@ -3330,7 +3434,7 @@ public void testInvalidJoinPatterns() {
// Generate a syntactically invalid (partial quoted) pattern.
var joinPattern = quote(randomIdentifier()) + ":" + unquoteIndexPattern(randomIndexPattern(without(CROSS_CLUSTER)));
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
// Since the join pattern is partially quoted, we get an error at the end of the partially quoted string.
"mismatched input ':'"
);
@@ -3341,7 +3445,7 @@ public void testInvalidJoinPatterns() {
// Generate a syntactically invalid (partially quoted) pattern.
var joinPattern = randomIdentifier() + ":" + quote(randomIndexPattern(without(CROSS_CLUSTER)));
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
// Since the from pattern is partially quoted, we get an error at the beginning of the partially quoted
// index name that we're expecting an unquoted string.
"no viable alternative at input"
@@ -3358,7 +3462,7 @@ public void testInvalidJoinPatterns() {
fromPatterns = unquoteIndexPattern(fromPatterns) + "::data";
var joinPattern = randomIndexPattern(without(CROSS_CLUSTER), without(WILDCARD_PATTERN), without(INDEX_SELECTOR));
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"mismatched input '::' expecting {"
);
}
@@ -3372,7 +3476,7 @@ public void testInvalidJoinPatterns() {
fromPatterns = "\"" + unquoteIndexPattern(fromPatterns) + "::data\"";
var joinPattern = randomIndexPattern(without(CROSS_CLUSTER), without(WILDCARD_PATTERN), without(INDEX_SELECTOR));
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"Selectors are not yet supported on remote cluster patterns"
);
}
@@ -3384,7 +3488,7 @@ public void testInvalidJoinPatterns() {
// it to chance.
joinPattern = unquoteIndexPattern(joinPattern);
expectError(
- "FROM " + randomIndexPatterns(without(CROSS_CLUSTER)) + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + randomIndexPatterns(without(CROSS_CLUSTER)) + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"no viable alternative at input "
);
}
@@ -3397,7 +3501,7 @@ public void testInvalidJoinPatterns() {
// it to chance.
joinPattern = "\"" + unquoteIndexPattern(joinPattern) + "\"";
expectError(
- "FROM " + randomIndexPatterns(without(CROSS_CLUSTER)) + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + randomIndexPatterns(without(CROSS_CLUSTER)) + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"invalid index pattern ["
+ unquoteIndexPattern(joinPattern)
+ "], index pattern selectors are not supported in LOOKUP JOIN"
@@ -3411,7 +3515,7 @@ public void testInvalidJoinPatterns() {
var joinPattern = quote(randomIdentifier()) + "::" + randomFrom("data", "failures");
// After the end of the partially quoted string, i.e. the index name, parser now expects "ON..." and not a selector string.
expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
+ "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause,
"mismatched input ':' expecting 'on'"
);
}
@@ -3423,10 +3527,7 @@ public void testInvalidJoinPatterns() {
var joinPattern = randomIdentifier() + "::" + quote(randomFrom("data", "failures"));
// After the index name and "::", parser expects an unquoted string, i.e. the selector string should not be
// partially quoted.
- expectError(
- "FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + randomIdentifier(),
- "no viable alternative at input"
- );
+ expectError("FROM " + fromPatterns + " | LOOKUP JOIN " + joinPattern + " ON " + onClause, "no viable alternative at input");
}
}
}
@@ -3551,6 +3652,7 @@ public void testForkAllReleasedCommands() {
( MV_EXPAND a )
( CHANGE_POINT a on b )
( LOOKUP JOIN idx2 ON f1 )
+ ( LOOKUP JOIN idx2 ON f2 >= f3 )
( ENRICH idx2 on f1 with f2 = f3 )
( FORK ( WHERE a:"baz" ) ( EVAL x = [ 1, 2, 3 ] ) )
( COMPLETION a=b WITH { "inference_id": "c" } )
@@ -3587,7 +3689,7 @@ public void testForkAllCommands() {
( RENAME a as c )
( MV_EXPAND a )
( CHANGE_POINT a on b )
- ( LOOKUP JOIN idx2 ON f1 )
+ ( LOOKUP JOIN idx2 ON f1 | LOOKUP JOIN idx3 ON f1 > f3)
( ENRICH idx2 on f1 with f2 = f3 )
( FORK ( WHERE a:"baz" ) ( EVAL x = [ 1, 2, 3 ] ) )
( COMPLETION a=b WITH { "inference_id": "c" } )
@@ -4414,6 +4516,41 @@ public void testDoubleParamsForIdentifier() {
);
}
+ // lookup join on expression
+ namedDoubleParams = List.of("??f1", "??f2", "??f3", "??f4");
+ positionalDoubleParams = List.of("??1", "??2", "??3", "??4");
+ anonymousDoubleParams = List.of("??", "??", "??", "??");
+ doubleParams.clear();
+ doubleParams.add(namedDoubleParams);
+ doubleParams.add(positionalDoubleParams);
+ doubleParams.add(anonymousDoubleParams);
+ for (List params : doubleParams) {
+ String query = LoggerMessageFormat.format(null, """
+ from test
+ | lookup join idx on {}.{} == {}.{}
+ | limit 1""", params.get(0), params.get(1), params.get(2), params.get(3));
+ LogicalPlan plan = statement(
+ query,
+ new QueryParams(
+ List.of(
+ paramAsConstant("f1", "f.1"),
+ paramAsConstant("f2", "f.2"),
+ paramAsConstant("f3", "f.3"),
+ paramAsConstant("f4", "f.4")
+ )
+ )
+ );
+ Limit limit = as(plan, Limit.class);
+ LookupJoin join = as(limit.child(), LookupJoin.class);
+ UnresolvedRelation ur = as(join.right(), UnresolvedRelation.class);
+ assertEquals(ur.indexPattern().indexPattern(), "idx");
+ assertTrue(join.config().type().joinName().contains("LEFT OUTER"));
+ EsqlBinaryComparison on = as(join.config().joinOnConditions(), EsqlBinaryComparison.class);
+ assertEquals(on.getFunctionType(), EsqlBinaryComparison.BinaryComparisonOperation.EQ);
+ assertEquals(as(on.left(), UnresolvedAttribute.class).name(), "f.1.f.2");
+ assertEquals(as(on.right(), UnresolvedAttribute.class).name(), "f.3.f.4");
+ }
+
namedDoubleParams = List.of("??f1", "??f2", "??f3", "??f4", "??f5", "??f6");
positionalDoubleParams = List.of("??1", "??2", "??3", "??4", "??5", "??6");
anonymousDoubleParams = List.of("??", "??", "??", "??", "??", "??");
@@ -4756,6 +4893,7 @@ public void testBracketsInIndexNames() {
expectError("from te()st", "line 1:8: token recognition error at: '('");
expectError("from test | enrich foo)", "line -1:-1: Invalid query [from test | enrich foo)]");
expectError("from test | lookup join foo) on bar", "line 1:28: token recognition error at: ')'");
+ expectError("from test | lookup join foo) on bar1 > bar2", "line 1:28: token recognition error at: ')'");
}
private void expectErrorForBracketsWithoutQuotes(String pattern) {
@@ -4766,6 +4904,7 @@ private void expectErrorForBracketsWithoutQuotes(String pattern) {
expectThrows(ParsingException.class, () -> processingCommand("from remote1:" + pattern + ",remote2:" + pattern));
expectThrows(ParsingException.class, () -> processingCommand("from test | lookup join " + pattern + " on bar"));
+ expectThrows(ParsingException.class, () -> processingCommand("from test | lookup join " + pattern + " on bar1 < bar2"));
expectThrows(ParsingException.class, () -> processingCommand("from test | enrich " + pattern));
}
@@ -4790,11 +4929,17 @@ private void expectSuccessForBracketsWithinQuotes(String indexName) {
if (indexName.contains("*")) {
expectThrows(ParsingException.class, () -> processingCommand("from test | lookup join \"" + indexName + "\" on bar"));
+ expectThrows(ParsingException.class, () -> processingCommand("from test | lookup join \"" + indexName + "\" on bar1 > bar2"));
} else {
plan = statement("from test | lookup join \"" + indexName + "\" on bar");
LookupJoin lookup = as(plan, LookupJoin.class);
UnresolvedRelation right = as(lookup.right(), UnresolvedRelation.class);
assertThat(right.indexPattern().indexPattern(), is(indexName));
+
+ plan = statement("from test | lookup join \"" + indexName + "\" on bar1 <= bar2");
+ lookup = as(plan, LookupJoin.class);
+ right = as(lookup.right(), UnresolvedRelation.class);
+ assertThat(right.indexPattern().indexPattern(), is(indexName));
}
}
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/AbstractLogicalPlanSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/AbstractLogicalPlanSerializationTests.java
index 4960bae3d62f4..d980b34b6744c 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/AbstractLogicalPlanSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/AbstractLogicalPlanSerializationTests.java
@@ -33,6 +33,8 @@ protected final NamedWriteableRegistry getNamedWriteableRegistry() {
entries.addAll(PlanWritables.others());
entries.addAll(ExpressionWritables.aggregates());
entries.addAll(ExpressionWritables.allExpressions());
+ entries.addAll(ExpressionWritables.binaryComparisons());
+ entries.addAll(ExpressionWritables.scalars());
return new NamedWriteableRegistry(entries);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
index c88ef47a72989..35ba499a52cf7 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
@@ -8,13 +8,17 @@
package org.elasticsearch.xpack.esql.plan.logical;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
+import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison;
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinConfig;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinType;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
public class JoinSerializationTests extends AbstractLogicalPlanSerializationTests {
@@ -32,7 +36,29 @@ private static JoinConfig randomJoinConfig() {
List matchFields = randomFieldAttributes(1, 10, false);
List leftFields = randomFieldAttributes(1, 10, false);
List rightFields = randomFieldAttributes(1, 10, false);
- return new JoinConfig(type, matchFields, leftFields, rightFields, null);
+ return new JoinConfig(type, matchFields, leftFields, rightFields, randomJoinOnExpression());
+ }
+
+ private static Expression randomJoinOnExpression() {
+ int randomInt = between(1, 20);
+ List expressionList = new ArrayList<>();
+ for (int i = 0; i < randomInt; i++) {
+ expressionList.add(randomJoinPredicate());
+ }
+ return Predicates.combineAnd(expressionList);
+
+ }
+
+ private static Expression randomJoinPredicate() {
+ return randomBinaryComparisonOperator().buildNewInstance(randomSource(), randomAttribute(), randomAttribute());
+ }
+
+ private static EsqlBinaryComparison.BinaryComparisonOperation randomBinaryComparisonOperator() {
+ return randomFrom(EsqlBinaryComparison.BinaryComparisonOperation.values());
+ }
+
+ private static Attribute randomAttribute() {
+ return randomFieldAttributes(1, 1, false).get(0);
}
@Override
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/LookupSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/LookupSerializationTests.java
index 8ce5b8c06f052..53f36e124ebb0 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/LookupSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/LookupSerializationTests.java
@@ -24,7 +24,7 @@ public static Lookup randomLookup(int depth) {
Expression tableName = AbstractExpressionSerializationTests.randomChild();
List matchFields = randomFieldAttributes(1, 10, false);
LocalRelation localRelation = randomBoolean() ? null : LocalRelationSerializationTests.randomLocalRelation();
- return new Lookup(source, child, tableName, matchFields, localRelation, null);
+ return new Lookup(source, child, tableName, matchFields, localRelation);
}
@Override
@@ -48,6 +48,6 @@ protected Lookup mutateInstance(Lookup instance) throws IOException {
() -> randomBoolean() ? null : LocalRelationSerializationTests.randomLocalRelation()
);
}
- return new Lookup(source, child, tableName, matchFields, localRelation, null);
+ return new Lookup(source, child, tableName, matchFields, localRelation);
}
}
From 26358d613cffc36fa15ed86ee6c9230b3d4b34ec Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Tue, 9 Sep 2025 11:32:15 -0400
Subject: [PATCH 08/30] Address code review comments part 3
---
.../compute/operator/lookup/QueryList.java | 5 ++---
.../xpack/esql/enrich/ExpressionQueryList.java | 2 +-
.../xpack/esql/enrich/LookupFromIndexOperator.java | 4 ++--
.../xpack/esql/enrich/LookupFromIndexService.java | 9 ++++-----
.../xpack/esql/enrich/MatchConfig.java | 13 ++++++-------
.../xpack/esql/parser/LogicalPlanBuilder.java | 3 ++-
.../xpack/esql/planner/LocalExecutionPlanner.java | 4 ++--
.../esql/enrich/LookupFromIndexOperatorTests.java | 2 +-
.../esql/enrich/MatchConfigSerializationTests.java | 7 +++----
9 files changed, 23 insertions(+), 26 deletions(-)
diff --git a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
index 93acfe8ba37cd..f300a400e60af 100644
--- a/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
+++ b/x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/operator/lookup/QueryList.java
@@ -155,9 +155,8 @@ private Query wrapSingleValueQuery(Query query) {
}
/**
- * Returns a list of term queries for the given field and the input block
- * using only the {@link ElementType} of the {@link Block} to determine the
- * query.
+ * Returns a function that reads values from the given block. The function
+ * takes the offset of the value to read and returns the value as an {@link Object}.
*/
public static IntFunction createBlockValueReader(Block block) {
return switch (block.elementType()) {
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
index c8afd418b96c2..f3bed73a9998e 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ExpressionQueryList.java
@@ -94,7 +94,7 @@ private void buildJoinOnConditions(LookupFromIndexService.TransportRequest reque
if (left instanceof Attribute leftAttribute) {
boolean matched = false;
for (int i = 0; i < request.getMatchFields().size(); i++) {
- if (request.getMatchFields().get(i).fieldName().string().equals(leftAttribute.name())) {
+ if (request.getMatchFields().get(i).fieldName().equals(leftAttribute.name())) {
Block block = request.getInputPage().getBlock(i);
Expression right = binaryComparison.right();
if (right instanceof Attribute rightAttribute) {
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperator.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperator.java
index 2bb86a7ebf98d..3fbb54aa3a365 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperator.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperator.java
@@ -61,7 +61,7 @@ public String describe() {
stringBuilder.append(" input_type=")
.append(matchField.type())
.append(" match_field=")
- .append(matchField.fieldName().string())
+ .append(matchField.fieldName())
.append(" inputChannel=")
.append(matchField.channel());
}
@@ -223,7 +223,7 @@ public String toString() {
stringBuilder.append(" input_type=")
.append(matchField.type())
.append(" match_field=")
- .append(matchField.fieldName().string())
+ .append(matchField.fieldName())
.append(" inputChannel=")
.append(matchField.channel());
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
index 501b6034caac5..309b7210c1a1f 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
@@ -35,7 +35,6 @@
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
import org.elasticsearch.xpack.esql.action.EsqlQueryAction;
import org.elasticsearch.xpack.esql.core.expression.Expression;
-import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
@@ -115,7 +114,7 @@ protected LookupEnrichQueryGenerator queryList(
for (int i = 0; i < request.matchFields.size(); i++) {
MatchConfig matchField = request.matchFields.get(i);
QueryList q = termQueryList(
- context.getFieldType(matchField.fieldName().string()),
+ context.getFieldType(matchField.fieldName()),
context,
aliasFilter,
request.inputPage.getBlock(matchField.channel()),
@@ -247,7 +246,7 @@ static TransportRequest readFrom(StreamInput in, BlockFactory blockFactory) thro
String matchField = in.readString();
// For older versions, we only support a single match field.
matchFields = new ArrayList<>(1);
- matchFields.add(new MatchConfig(new FieldAttribute.FieldName(matchField), 0, inputDataType));
+ matchFields.add(new MatchConfig(matchField, 0, inputDataType));
}
var source = Source.EMPTY;
if (in.getTransportVersion().onOrAfter(TransportVersions.V_8_17_0)) {
@@ -320,7 +319,7 @@ public void writeTo(StreamOutput out) throws IOException {
} else {
// older versions only support a single match field, we already checked this above when writing the datatype
// send the field name of the first and only match field here
- out.writeString(matchFields.get(0).fieldName().string());
+ out.writeString(matchFields.get(0).fieldName());
}
if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_17_0)) {
source.writeTo(planOut);
@@ -343,7 +342,7 @@ public void writeTo(StreamOutput out) throws IOException {
@Override
protected String extraDescription() {
return " ,match_fields="
- + matchFields.stream().map(x -> x.fieldName().string()).collect(Collectors.joining(", "))
+ + matchFields.stream().map(MatchConfig::fieldName).collect(Collectors.joining(", "))
+ ", right_pre_join_plan="
+ (rightPreJoinPlan == null ? "null" : rightPreJoinPlan.toString());
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
index fcb87d3a43e07..9025b6c3b6966 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/MatchConfig.java
@@ -10,7 +10,6 @@
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
-import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.planner.Layout;
@@ -18,32 +17,32 @@
import java.util.Objects;
public final class MatchConfig implements Writeable {
- private final FieldAttribute.FieldName fieldName;
+ private final String fieldName;
private final int channel;
private final DataType type;
- public MatchConfig(FieldAttribute.FieldName fieldName, int channel, DataType type) {
+ public MatchConfig(String fieldName, int channel, DataType type) {
this.fieldName = fieldName;
this.channel = channel;
this.type = type;
}
- public MatchConfig(FieldAttribute.FieldName fieldName, Layout.ChannelAndType input) {
+ public MatchConfig(String fieldName, Layout.ChannelAndType input) {
this(fieldName, input.channel(), input.type());
}
public MatchConfig(StreamInput in) throws IOException {
- this(new FieldAttribute.FieldName(in.readString()), in.readInt(), DataType.readFrom(in));
+ this(in.readString(), in.readInt(), DataType.readFrom(in));
}
@Override
public void writeTo(StreamOutput out) throws IOException {
- out.writeString(fieldName.string());
+ out.writeString(fieldName);
out.writeInt(channel);
type.writeTo(out);
}
- public FieldAttribute.FieldName fieldName() {
+ public String fieldName() {
return fieldName;
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index bb37ebcc41327..340c583738e0e 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -93,6 +93,7 @@
import java.util.function.Function;
import static java.util.Collections.emptyList;
+import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION;
import static org.elasticsearch.xpack.esql.core.util.StringUtils.WILDCARD;
import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputExpressions;
import static org.elasticsearch.xpack.esql.parser.ParserUtils.source;
@@ -694,7 +695,7 @@ public JoinInfo visitFieldBasedLookupJoin(EsqlBaseParser.FieldBasedLookupJoinCon
@Override
public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) {
- if (Build.current().isSnapshot() == false) {
+ if (LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION.isEnabled() == false) {
throw new ParsingException(ctx.getText(), "JOIN ON clause only supports fields at the moment, found [{}]", ctx.getText());
}
var predicates = visitList(this, ctx.comparisonExpression(), Expression.class);
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
index 2f7836f83c03b..0eec933d39d3f 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java
@@ -792,7 +792,7 @@ private PhysicalOperation planLookupJoin(LookupJoinExec join, LocalExecutionPlan
// TODO: Using exactAttribute was supposed to handle TEXT fields with KEYWORD subfields - but we don't allow these in lookup
// indices, so the call to exactAttribute looks redundant now.
- FieldAttribute.FieldName fieldName = right.exactAttribute().fieldName();
+ String fieldName = right.exactAttribute().fieldName().string();
// we support 2 types of joins: Field name joins and Expression joins
// for Field name join, we do not ship any join on expression.
@@ -806,7 +806,7 @@ private PhysicalOperation planLookupJoin(LookupJoinExec join, LocalExecutionPlan
// e.g. LOOKUP JOIN ON left_id < right_id_1 and left_id >= right_id_2
// we want to be able to optimize this in the future and only ship the left_id once
if (join.isOnJoinExpression()) {
- fieldName = new FieldAttribute.FieldName(left.name());
+ fieldName = left.name();
}
matchFields.add(new MatchConfig(fieldName, input));
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperatorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperatorTests.java
index 30fd2c038b38c..db3f7303be4bd 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperatorTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexOperatorTests.java
@@ -175,7 +175,7 @@ protected Operator.OperatorFactory simple(SimpleOptions options) {
List matchFields = new ArrayList<>();
for (int i = 0; i < numberOfJoinColumns; i++) {
- FieldAttribute.FieldName matchField = new FieldAttribute.FieldName("match" + i);
+ String matchField = "match" + i;
matchFields.add(new MatchConfig(matchField, i, inputDataType));
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/MatchConfigSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/MatchConfigSerializationTests.java
index 2df9831b5bd0d..00ee70b670d42 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/MatchConfigSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/enrich/MatchConfigSerializationTests.java
@@ -19,7 +19,6 @@
import org.elasticsearch.TransportVersion;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.AbstractWireSerializingTestCase;
-import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
import org.elasticsearch.xpack.esql.io.stream.PlanStreamOutput;
@@ -54,7 +53,7 @@ private MatchConfig randomMatchConfig() {
String name = randomAlphaOfLengthBetween(1, 100);
int channel = randomInt();
DataType type = randomFrom(DataType.types());
- return new MatchConfig(new FieldAttribute.FieldName(name), channel, type);
+ return new MatchConfig(name, channel, type);
}
@Override
@@ -66,8 +65,8 @@ private MatchConfig mutateMatchConfig(MatchConfig instance) {
int i = randomIntBetween(1, 3);
return switch (i) {
case 1 -> {
- String name = randomValueOtherThan(instance.fieldName().string(), () -> randomAlphaOfLengthBetween(1, 100));
- yield new MatchConfig(new FieldAttribute.FieldName(name), instance.channel(), instance.type());
+ String name = randomValueOtherThan(instance.fieldName(), () -> randomAlphaOfLengthBetween(1, 100));
+ yield new MatchConfig(name, instance.channel(), instance.type());
}
case 2 -> {
int channel = randomValueOtherThan(instance.channel(), () -> randomInt());
From 188a870a607ef681c6da7836cf24b24085cb8701 Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Tue, 9 Sep 2025 12:54:08 -0400
Subject: [PATCH 09/30] Enhance UT coverage
---
.../xpack/esql/action/LookupFromIndexIT.java | 42 +++++++++++++++----
.../enrich/BinaryComparisonQueryList.java | 21 ++++++----
.../plan/logical/JoinSerializationTests.java | 28 +------------
.../LookupJoinExecSerializationTests.java | 42 +++++++++++++++++--
.../esql/tree/EsqlNodeSubclassTests.java | 3 +-
5 files changed, 90 insertions(+), 46 deletions(-)
diff --git a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
index b741985a0a3ca..693a950de7562 100644
--- a/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
+++ b/x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupFromIndexIT.java
@@ -63,6 +63,8 @@
import org.elasticsearch.xpack.esql.core.type.EsField;
import org.elasticsearch.xpack.esql.enrich.LookupFromIndexOperator;
import org.elasticsearch.xpack.esql.enrich.MatchConfig;
+import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
+import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan;
import org.elasticsearch.xpack.esql.plan.logical.EsRelation;
import org.elasticsearch.xpack.esql.plan.logical.Filter;
@@ -225,6 +227,7 @@ public void populate(int docCount, List expected, Predicate fil
Map lookupDoc = new HashMap<>();
for (int f = 0; f < numFields; f++) {
lookupDoc.put("key" + f, lookupData[f][i]);
+ lookupDoc.put("rkey" + f, lookupData[f][i]);
}
lookupDoc.put("l", i);
docs.add(client().prepareIndex("lookup").setSource(lookupDoc));
@@ -263,14 +266,17 @@ private void runLookup(List keyTypes, PopulateIndices populateIndices,
.put(IndexSettings.MODE.getKey(), "lookup")
.put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1);
- String[] lookupMappers = new String[keyTypes.size() * 2 + 2];
- int lookupMappersCounter = 0;
- for (; lookupMappersCounter < keyTypes.size(); lookupMappersCounter++) {
- lookupMappers[2 * lookupMappersCounter] = "key" + lookupMappersCounter;
- lookupMappers[2 * lookupMappersCounter + 1] = "type=" + keyTypes.get(lookupMappersCounter).esType();
+ String[] lookupMappers = new String[keyTypes.size() * 4 + 2];
+ for (int i = 0; i < keyTypes.size(); i++) {
+ lookupMappers[2 * i] = "key" + i;
+ lookupMappers[2 * i + 1] = "type=" + keyTypes.get(i).esType();
+ }
+ for (int i = 0; i < keyTypes.size(); i++) {
+ lookupMappers[2 * (keyTypes.size() + i)] = "rkey" + i;
+ lookupMappers[2 * (keyTypes.size() + i) + 1] = "type=" + keyTypes.get(i).esType();
}
- lookupMappers[2 * lookupMappersCounter] = "l";
- lookupMappers[2 * lookupMappersCounter + 1] = "type=long";
+ lookupMappers[keyTypes.size() * 4] = "l";
+ lookupMappers[keyTypes.size() * 4 + 1] = "type=long";
client().admin().indices().prepareCreate("lookup").setSettings(lookupSettings).setMapping(lookupMappers).get();
client().admin().cluster().prepareHealth(TEST_REQUEST_TIMEOUT).setWaitForGreenStatus().get();
@@ -371,9 +377,27 @@ private void runLookup(List keyTypes, PopulateIndices populateIndices,
TEST_REQUEST_TIMEOUT
);
final String finalNodeWithShard = nodeWithShard;
+ boolean expressionJoin = randomBoolean();
List matchFields = new ArrayList<>();
+ List joinOnConditions = new ArrayList<>();
+ if (expressionJoin) {
+ for (int i = 0; i < keyTypes.size(); i++) {
+ FieldAttribute leftAttr = new FieldAttribute(
+ Source.EMPTY,
+ "key" + i,
+ new EsField("key" + i, keyTypes.get(0), Collections.emptyMap(), true, EsField.TimeSeriesFieldType.NONE)
+ );
+ FieldAttribute rightAttr = new FieldAttribute(
+ Source.EMPTY,
+ "rkey" + i,
+ new EsField("rkey" + i, keyTypes.get(i), Collections.emptyMap(), true, EsField.TimeSeriesFieldType.NONE)
+ );
+ joinOnConditions.add(new Equals(Source.EMPTY, leftAttr, rightAttr));
+ }
+ }
+ // the matchFields are shared for both types of join
for (int i = 0; i < keyTypes.size(); i++) {
- matchFields.add(new MatchConfig(new FieldAttribute.FieldName("key" + i), i + 1, keyTypes.get(i)));
+ matchFields.add(new MatchConfig("key" + i, i + 1, keyTypes.get(i)));
}
LookupFromIndexOperator.Factory lookup = new LookupFromIndexOperator.Factory(
matchFields,
@@ -386,7 +410,7 @@ private void runLookup(List keyTypes, PopulateIndices populateIndices,
List.of(new Alias(Source.EMPTY, "l", new ReferenceAttribute(Source.EMPTY, "l", DataType.LONG))),
Source.EMPTY,
filters,
- null
+ Predicates.combineAnd(joinOnConditions)
);
DriverContext driverContext = driverContext();
try (
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/BinaryComparisonQueryList.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/BinaryComparisonQueryList.java
index 4667c25204f03..9c38d8a66bda9 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/BinaryComparisonQueryList.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/BinaryComparisonQueryList.java
@@ -15,6 +15,8 @@
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.search.internal.AliasFilter;
+import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
+import org.elasticsearch.xpack.esql.capabilities.TranslationAware;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison;
import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates;
@@ -36,7 +38,7 @@ public class BinaryComparisonQueryList extends QueryList {
public BinaryComparisonQueryList(
MappedFieldType field,
SearchExecutionContext searchExecutionContext,
- Block block,
+ Block leftHandSideBlock,
EsqlBinaryComparison binaryComparison,
ClusterService clusterService,
AliasFilter aliasFilter,
@@ -46,15 +48,16 @@ public BinaryComparisonQueryList(
field,
searchExecutionContext,
aliasFilter,
- block,
+ leftHandSideBlock,
new OnlySingleValueParams(warnings, "LOOKUP JOIN encountered multi-value")
);
// swap left and right if the field is on the right
// We get a filter in the form left_expr >= right_expr
// here we will swap it to right_expr <= left_expr
- // and later in doGetQuery we will replace left_expr with the value from the block
+ // and later in doGetQuery we will replace left_expr with the value from the leftHandSideBlock
+ // We do that because binaryComparison expects the field to be on the left and the literal on the right to be translatable
this.binaryComparison = (EsqlBinaryComparison) binaryComparison.swapLeftAndRight();
- this.blockValueReader = QueryList.createBlockValueReader(block);
+ this.blockValueReader = QueryList.createBlockValueReader(leftHandSideBlock);
this.searchExecutionContext = searchExecutionContext;
lucenePushdownPredicates = LucenePushdownPredicates.from(
SearchContextStats.from(List.of(searchExecutionContext)),
@@ -78,9 +81,13 @@ public Query doGetQuery(int position, int firstValueIndex, int valueCount) {
new Literal(binaryComparison.right().source(), value, binaryComparison.right().dataType())
);
try {
- return comparison.asQuery(lucenePushdownPredicates, TranslatorHandler.TRANSLATOR_HANDLER)
- .toQueryBuilder()
- .toQuery(searchExecutionContext);
+ if (TranslationAware.Translatable.YES.equals(comparison.translatable(lucenePushdownPredicates))) {
+ return comparison.asQuery(lucenePushdownPredicates, TranslatorHandler.TRANSLATOR_HANDLER)
+ .toQueryBuilder()
+ .toQuery(searchExecutionContext);
+ } else {
+ throw new EsqlIllegalArgumentException("Cannot translate join condition: " + binaryComparison);
+ }
} catch (IOException e) {
throw new UncheckedIOException("Error while building query for join on filter:", e);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
index 35ba499a52cf7..5d1b2193c1dcd 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
@@ -8,19 +8,17 @@
package org.elasticsearch.xpack.esql.plan.logical;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
-import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.tree.Source;
-import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
-import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison;
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinConfig;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinType;
import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
+import static org.elasticsearch.xpack.esql.plan.physical.LookupJoinExecSerializationTests.randomJoinOnExpression;
+
public class JoinSerializationTests extends AbstractLogicalPlanSerializationTests {
@Override
protected Join createTestInstance() {
@@ -39,28 +37,6 @@ private static JoinConfig randomJoinConfig() {
return new JoinConfig(type, matchFields, leftFields, rightFields, randomJoinOnExpression());
}
- private static Expression randomJoinOnExpression() {
- int randomInt = between(1, 20);
- List expressionList = new ArrayList<>();
- for (int i = 0; i < randomInt; i++) {
- expressionList.add(randomJoinPredicate());
- }
- return Predicates.combineAnd(expressionList);
-
- }
-
- private static Expression randomJoinPredicate() {
- return randomBinaryComparisonOperator().buildNewInstance(randomSource(), randomAttribute(), randomAttribute());
- }
-
- private static EsqlBinaryComparison.BinaryComparisonOperation randomBinaryComparisonOperator() {
- return randomFrom(EsqlBinaryComparison.BinaryComparisonOperation.values());
- }
-
- private static Attribute randomAttribute() {
- return randomFieldAttributes(1, 1, false).get(0);
- }
-
@Override
protected Join mutateInstance(Join instance) throws IOException {
LogicalPlan left = instance.left();
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/LookupJoinExecSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/LookupJoinExecSerializationTests.java
index ac94839350e14..d4eb395e52609 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/LookupJoinExecSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/LookupJoinExecSerializationTests.java
@@ -8,12 +8,39 @@
package org.elasticsearch.xpack.esql.plan.physical;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
+import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
public class LookupJoinExecSerializationTests extends AbstractPhysicalPlanSerializationTests {
+
+ public static Expression randomJoinOnExpression() {
+ int randomInt = between(1, 20);
+ List expressionList = new ArrayList<>();
+ for (int i = 0; i < randomInt; i++) {
+ expressionList.add(randomJoinPredicate());
+ }
+ return Predicates.combineAnd(expressionList);
+
+ }
+
+ private static Expression randomJoinPredicate() {
+ return randomBinaryComparisonOperator().buildNewInstance(randomSource(), randomAttribute(), randomAttribute());
+ }
+
+ private static EsqlBinaryComparison.BinaryComparisonOperation randomBinaryComparisonOperator() {
+ return randomFrom(EsqlBinaryComparison.BinaryComparisonOperation.values());
+ }
+
+ private static Attribute randomAttribute() {
+ return randomFieldAttributes(1, 1, false).get(0);
+ }
+
public static LookupJoinExec randomLookupJoinExec(int depth) {
Source source = randomSource();
PhysicalPlan child = randomChild(depth);
@@ -21,7 +48,7 @@ public static LookupJoinExec randomLookupJoinExec(int depth) {
List leftFields = randomFields();
List rightFields = randomFields();
List addedFields = randomFields();
- return new LookupJoinExec(source, child, lookup, leftFields, rightFields, addedFields, null);
+ return new LookupJoinExec(source, child, lookup, leftFields, rightFields, addedFields, randomJoinOnExpression());
}
private static List randomFields() {
@@ -40,15 +67,24 @@ protected LookupJoinExec mutateInstance(LookupJoinExec instance) throws IOExcept
List leftFields = randomFields();
List rightFields = randomFields();
List addedFields = randomFields();
- switch (between(0, 4)) {
+ Expression joinOnConditions = instance.joinOnConditions();
+ switch (between(0, 5)) {
case 0 -> child = randomValueOtherThan(child, () -> randomChild(0));
case 1 -> lookup = randomValueOtherThan(lookup, () -> randomChild(0));
case 2 -> leftFields = randomValueOtherThan(leftFields, LookupJoinExecSerializationTests::randomFields);
case 3 -> rightFields = randomValueOtherThan(rightFields, LookupJoinExecSerializationTests::randomFields);
case 4 -> addedFields = randomValueOtherThan(addedFields, LookupJoinExecSerializationTests::randomFields);
+ case 5 -> joinOnConditions = mutateJoinOnCondition(joinOnConditions);
default -> throw new UnsupportedOperationException();
}
- return new LookupJoinExec(instance.source(), child, lookup, leftFields, rightFields, addedFields, null);
+ return new LookupJoinExec(instance.source(), child, lookup, leftFields, rightFields, addedFields, joinOnConditions);
+ }
+
+ private Expression mutateJoinOnCondition(Expression joinOnConditions) {
+ if (joinOnConditions != null) {
+ return null;
+ }
+ return randomJoinOnExpression();
}
@Override
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
index c21aa9693989c..c234c25edc57c 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
@@ -99,6 +99,7 @@
import static org.elasticsearch.xpack.esql.index.EsIndexSerializationTests.randomEsIndex;
import static org.elasticsearch.xpack.esql.index.EsIndexSerializationTests.randomIndexNameWithModes;
import static org.elasticsearch.xpack.esql.plan.AbstractNodeSerializationTests.randomFieldAttributes;
+import static org.elasticsearch.xpack.esql.plan.physical.LookupJoinExecSerializationTests.randomJoinOnExpression;
import static org.mockito.Mockito.mock;
/**
@@ -515,7 +516,7 @@ public void accept(Page page) {
List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
- null
+ randomJoinOnExpression()
);
}
From 537cdbfacc8d2c477aa9eb5eb280602558c1b8cd Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Wed, 10 Sep 2025 11:15:31 -0400
Subject: [PATCH 10/30] Fix UTs
---
.../esql/optimizer/LogicalPlanOptimizerTests.java | 11 +++++++++++
.../AbstractPhysicalPlanSerializationTests.java | 2 ++
2 files changed, 13 insertions(+)
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
index 6f1401559820a..ab01d950c5dd0 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
@@ -9056,4 +9056,15 @@ public void testLookupJoinExpressionAmbigiousBoth() {
IllegalStateException e = expectThrows(IllegalStateException.class, () -> plan(query));
assertThat(e.getMessage(), containsString("Reference [language_code] is ambiguous; matches any of "));
}
+
+ public void testLookupJoinExpressionAnd() {
+ String query = """
+ from test
+ | rename languages as language_code_left
+ | lookup join languages_lookup ON language_code_left != language_code and language_code_left > language_code
+ """;
+
+ LogicalPlan plan = optimizedPlan(query);
+ var project = as(plan, Project.class);
+ }
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/AbstractPhysicalPlanSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/AbstractPhysicalPlanSerializationTests.java
index c2a012cd8e853..219b0a3586a6c 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/AbstractPhysicalPlanSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/AbstractPhysicalPlanSerializationTests.java
@@ -50,6 +50,8 @@ protected final NamedWriteableRegistry getNamedWriteableRegistry() {
entries.addAll(PlanWritables.getNamedWriteables());
entries.addAll(ExpressionWritables.aggregates());
entries.addAll(ExpressionWritables.allExpressions());
+ entries.addAll(ExpressionWritables.scalars());
+ entries.addAll(ExpressionWritables.binaryComparisons());
entries.addAll(new SearchModule(Settings.EMPTY, List.of()).getNamedWriteables()); // Query builders
entries.add(Add.ENTRY); // Used by the eval tests
entries.add(AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral.ENTRY);
From 4716191bff31266d10a3f87a5e5bd6f97e6a5c94 Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Wed, 10 Sep 2025 11:16:33 -0400
Subject: [PATCH 11/30] Grammar change
---
.../esql/src/main/antlr/parser/Expression.g4 | 6 +-
.../plugin/esql/src/main/antlr/parser/Join.g4 | 10 +-
.../xpack/esql/parser/EsqlBaseParser.interp | 5 +-
.../xpack/esql/parser/EsqlBaseParser.java | 2284 +++++++++--------
.../parser/EsqlBaseParserBaseListener.java | 36 +-
.../parser/EsqlBaseParserBaseVisitor.java | 21 +-
.../esql/parser/EsqlBaseParserListener.java | 30 +-
.../esql/parser/EsqlBaseParserVisitor.java | 18 +-
.../xpack/esql/parser/ExpressionBuilder.java | 13 +-
.../xpack/esql/parser/LogicalPlanBuilder.java | 2 +-
10 files changed, 1266 insertions(+), 1159 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/antlr/parser/Expression.g4 b/x-pack/plugin/esql/src/main/antlr/parser/Expression.g4
index 9de7b5e8084bb..135a104b89139 100644
--- a/x-pack/plugin/esql/src/main/antlr/parser/Expression.g4
+++ b/x-pack/plugin/esql/src/main/antlr/parser/Expression.g4
@@ -30,11 +30,7 @@ matchBooleanExpression
valueExpression
: operatorExpression #valueExpressionDefault
- | comparisonExpression #comparison
- ;
-
-comparisonExpression
- : left=operatorExpression comparisonOperator right=operatorExpression
+ | left=operatorExpression comparisonOperator right=operatorExpression #comparison
;
operatorExpression
diff --git a/x-pack/plugin/esql/src/main/antlr/parser/Join.g4 b/x-pack/plugin/esql/src/main/antlr/parser/Join.g4
index b6f5f4c26467e..77b4ba9e00c67 100644
--- a/x-pack/plugin/esql/src/main/antlr/parser/Join.g4
+++ b/x-pack/plugin/esql/src/main/antlr/parser/Join.g4
@@ -19,7 +19,15 @@ joinTarget
joinCondition
: ON qualifiedName (COMMA qualifiedName)* #fieldBasedLookupJoin
- | ON comparisonExpression (AND comparisonExpression)* #expressionBasedLookupJoin
+ | ON joinPredicateExpression (AND joinPredicateExpression)* #expressionBasedLookupJoin
+ ;
+
+joinPredicate
+ : valueExpression
+ ;
+
+joinPredicateExpression
+ : left=operatorExpression comparisonOperator right=operatorExpression
;
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp
index 74189a783c56d..2a255e09f92a3 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.interp
@@ -345,7 +345,6 @@ booleanExpression
regexBooleanExpression
matchBooleanExpression
valueExpression
-comparisonExpression
operatorExpression
primaryExpression
functionExpression
@@ -363,7 +362,9 @@ comparisonOperator
joinCommand
joinTarget
joinCondition
+joinPredicate
+joinPredicateExpression
atn:
-[4, 1, 135, 877, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 186, 8, 1, 10, 1, 12, 1, 189, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 198, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 226, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 239, 8, 7, 10, 7, 12, 7, 242, 9, 7, 1, 8, 1, 8, 1, 8, 3, 8, 247, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 254, 8, 9, 10, 9, 12, 9, 257, 9, 9, 1, 10, 1, 10, 1, 10, 3, 10, 262, 8, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 273, 8, 13, 10, 13, 12, 13, 276, 9, 13, 1, 13, 3, 13, 279, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 290, 8, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 304, 8, 19, 10, 19, 12, 19, 307, 9, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 314, 8, 21, 1, 21, 1, 21, 3, 21, 318, 8, 21, 1, 22, 1, 22, 1, 22, 5, 22, 323, 8, 22, 10, 22, 12, 22, 326, 9, 22, 1, 23, 1, 23, 1, 23, 3, 23, 331, 8, 23, 1, 24, 1, 24, 1, 24, 3, 24, 336, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 345, 8, 24, 1, 25, 1, 25, 1, 25, 5, 25, 350, 8, 25, 10, 25, 12, 25, 353, 9, 25, 1, 26, 1, 26, 1, 26, 3, 26, 358, 8, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 367, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 372, 8, 27, 10, 27, 12, 27, 375, 9, 27, 1, 28, 1, 28, 1, 28, 5, 28, 380, 8, 28, 10, 28, 12, 28, 383, 9, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 3, 30, 390, 8, 30, 1, 31, 1, 31, 3, 31, 394, 8, 31, 1, 32, 1, 32, 3, 32, 398, 8, 32, 1, 33, 1, 33, 1, 33, 3, 33, 403, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 412, 8, 35, 10, 35, 12, 35, 415, 9, 35, 1, 36, 1, 36, 3, 36, 419, 8, 36, 1, 36, 1, 36, 3, 36, 423, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 435, 8, 39, 10, 39, 12, 39, 438, 9, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 448, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 3, 41, 454, 8, 41, 1, 42, 1, 42, 1, 42, 5, 42, 459, 8, 42, 10, 42, 12, 42, 462, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 3, 44, 470, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 493, 8, 50, 1, 50, 1, 50, 1, 50, 1, 50, 5, 50, 499, 8, 50, 10, 50, 12, 50, 502, 9, 50, 3, 50, 504, 8, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 3, 52, 511, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 522, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 529, 8, 54, 1, 55, 1, 55, 1, 55, 1, 56, 4, 56, 535, 8, 56, 11, 56, 12, 56, 536, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 549, 8, 58, 10, 58, 12, 58, 552, 9, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 560, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 571, 8, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 585, 8, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 599, 8, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 5, 66, 606, 8, 66, 10, 66, 12, 66, 609, 9, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 616, 8, 66, 1, 66, 1, 66, 1, 66, 3, 66, 621, 8, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 5, 66, 629, 8, 66, 10, 66, 12, 66, 632, 9, 66, 1, 67, 1, 67, 3, 67, 636, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 643, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 650, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 657, 8, 67, 10, 67, 12, 67, 660, 9, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 666, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 673, 8, 67, 10, 67, 12, 67, 676, 9, 67, 1, 67, 1, 67, 3, 67, 680, 8, 67, 1, 68, 1, 68, 1, 68, 3, 68, 685, 8, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 3, 69, 692, 8, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 702, 8, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 5, 71, 710, 8, 71, 10, 71, 12, 71, 713, 9, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 723, 8, 72, 1, 72, 1, 72, 1, 72, 5, 72, 728, 8, 72, 10, 72, 12, 72, 731, 9, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 5, 73, 739, 8, 73, 10, 73, 12, 73, 742, 9, 73, 1, 73, 1, 73, 3, 73, 746, 8, 73, 3, 73, 748, 8, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 3, 74, 755, 8, 74, 1, 75, 1, 75, 1, 75, 1, 75, 5, 75, 761, 8, 75, 10, 75, 12, 75, 764, 9, 75, 3, 75, 766, 8, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 3, 77, 776, 8, 77, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 5, 78, 791, 8, 78, 10, 78, 12, 78, 794, 9, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 5, 78, 802, 8, 78, 10, 78, 12, 78, 805, 9, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 1, 78, 5, 78, 813, 8, 78, 10, 78, 12, 78, 816, 9, 78, 1, 78, 1, 78, 3, 78, 820, 8, 78, 1, 79, 1, 79, 1, 80, 1, 80, 3, 80, 826, 8, 80, 1, 81, 3, 81, 829, 8, 81, 1, 81, 1, 81, 1, 82, 3, 82, 834, 8, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 3, 86, 850, 8, 86, 1, 86, 1, 86, 1, 86, 3, 86, 855, 8, 86, 1, 87, 1, 87, 1, 87, 1, 87, 5, 87, 861, 8, 87, 10, 87, 12, 87, 864, 9, 87, 1, 87, 1, 87, 1, 87, 1, 87, 5, 87, 870, 8, 87, 10, 87, 12, 87, 873, 9, 87, 3, 87, 875, 8, 87, 1, 87, 0, 5, 2, 116, 132, 142, 144, 88, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 0, 10, 2, 0, 49, 49, 103, 103, 1, 0, 97, 98, 2, 0, 53, 53, 59, 59, 2, 0, 62, 62, 65, 65, 2, 0, 38, 38, 49, 49, 1, 0, 83, 84, 1, 0, 85, 87, 2, 0, 61, 61, 74, 74, 2, 0, 76, 76, 78, 82, 2, 0, 23, 23, 25, 26, 916, 0, 176, 1, 0, 0, 0, 2, 179, 1, 0, 0, 0, 4, 197, 1, 0, 0, 0, 6, 225, 1, 0, 0, 0, 8, 227, 1, 0, 0, 0, 10, 230, 1, 0, 0, 0, 12, 232, 1, 0, 0, 0, 14, 235, 1, 0, 0, 0, 16, 246, 1, 0, 0, 0, 18, 250, 1, 0, 0, 0, 20, 258, 1, 0, 0, 0, 22, 263, 1, 0, 0, 0, 24, 266, 1, 0, 0, 0, 26, 269, 1, 0, 0, 0, 28, 289, 1, 0, 0, 0, 30, 291, 1, 0, 0, 0, 32, 293, 1, 0, 0, 0, 34, 295, 1, 0, 0, 0, 36, 297, 1, 0, 0, 0, 38, 299, 1, 0, 0, 0, 40, 308, 1, 0, 0, 0, 42, 311, 1, 0, 0, 0, 44, 319, 1, 0, 0, 0, 46, 327, 1, 0, 0, 0, 48, 344, 1, 0, 0, 0, 50, 346, 1, 0, 0, 0, 52, 366, 1, 0, 0, 0, 54, 368, 1, 0, 0, 0, 56, 376, 1, 0, 0, 0, 58, 384, 1, 0, 0, 0, 60, 389, 1, 0, 0, 0, 62, 393, 1, 0, 0, 0, 64, 397, 1, 0, 0, 0, 66, 402, 1, 0, 0, 0, 68, 404, 1, 0, 0, 0, 70, 407, 1, 0, 0, 0, 72, 416, 1, 0, 0, 0, 74, 424, 1, 0, 0, 0, 76, 427, 1, 0, 0, 0, 78, 430, 1, 0, 0, 0, 80, 447, 1, 0, 0, 0, 82, 449, 1, 0, 0, 0, 84, 455, 1, 0, 0, 0, 86, 463, 1, 0, 0, 0, 88, 469, 1, 0, 0, 0, 90, 471, 1, 0, 0, 0, 92, 475, 1, 0, 0, 0, 94, 478, 1, 0, 0, 0, 96, 481, 1, 0, 0, 0, 98, 485, 1, 0, 0, 0, 100, 488, 1, 0, 0, 0, 102, 505, 1, 0, 0, 0, 104, 510, 1, 0, 0, 0, 106, 514, 1, 0, 0, 0, 108, 517, 1, 0, 0, 0, 110, 530, 1, 0, 0, 0, 112, 534, 1, 0, 0, 0, 114, 538, 1, 0, 0, 0, 116, 542, 1, 0, 0, 0, 118, 553, 1, 0, 0, 0, 120, 555, 1, 0, 0, 0, 122, 566, 1, 0, 0, 0, 124, 575, 1, 0, 0, 0, 126, 580, 1, 0, 0, 0, 128, 586, 1, 0, 0, 0, 130, 589, 1, 0, 0, 0, 132, 620, 1, 0, 0, 0, 134, 679, 1, 0, 0, 0, 136, 681, 1, 0, 0, 0, 138, 691, 1, 0, 0, 0, 140, 693, 1, 0, 0, 0, 142, 701, 1, 0, 0, 0, 144, 722, 1, 0, 0, 0, 146, 732, 1, 0, 0, 0, 148, 754, 1, 0, 0, 0, 150, 756, 1, 0, 0, 0, 152, 769, 1, 0, 0, 0, 154, 775, 1, 0, 0, 0, 156, 819, 1, 0, 0, 0, 158, 821, 1, 0, 0, 0, 160, 825, 1, 0, 0, 0, 162, 828, 1, 0, 0, 0, 164, 833, 1, 0, 0, 0, 166, 837, 1, 0, 0, 0, 168, 839, 1, 0, 0, 0, 170, 841, 1, 0, 0, 0, 172, 854, 1, 0, 0, 0, 174, 874, 1, 0, 0, 0, 176, 177, 3, 2, 1, 0, 177, 178, 5, 0, 0, 1, 178, 1, 1, 0, 0, 0, 179, 180, 6, 1, -1, 0, 180, 181, 3, 4, 2, 0, 181, 187, 1, 0, 0, 0, 182, 183, 10, 1, 0, 0, 183, 184, 5, 48, 0, 0, 184, 186, 3, 6, 3, 0, 185, 182, 1, 0, 0, 0, 186, 189, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 3, 1, 0, 0, 0, 189, 187, 1, 0, 0, 0, 190, 198, 3, 22, 11, 0, 191, 198, 3, 12, 6, 0, 192, 198, 3, 98, 49, 0, 193, 194, 4, 2, 1, 0, 194, 198, 3, 24, 12, 0, 195, 196, 4, 2, 2, 0, 196, 198, 3, 94, 47, 0, 197, 190, 1, 0, 0, 0, 197, 191, 1, 0, 0, 0, 197, 192, 1, 0, 0, 0, 197, 193, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 5, 1, 0, 0, 0, 199, 226, 3, 40, 20, 0, 200, 226, 3, 8, 4, 0, 201, 226, 3, 74, 37, 0, 202, 226, 3, 68, 34, 0, 203, 226, 3, 42, 21, 0, 204, 226, 3, 70, 35, 0, 205, 226, 3, 76, 38, 0, 206, 226, 3, 78, 39, 0, 207, 226, 3, 82, 41, 0, 208, 226, 3, 90, 45, 0, 209, 226, 3, 100, 50, 0, 210, 226, 3, 92, 46, 0, 211, 226, 3, 170, 85, 0, 212, 226, 3, 108, 54, 0, 213, 226, 3, 122, 61, 0, 214, 226, 3, 106, 53, 0, 215, 226, 3, 110, 55, 0, 216, 226, 3, 120, 60, 0, 217, 218, 4, 3, 3, 0, 218, 226, 3, 126, 63, 0, 219, 220, 4, 3, 4, 0, 220, 226, 3, 124, 62, 0, 221, 222, 4, 3, 5, 0, 222, 226, 3, 128, 64, 0, 223, 224, 4, 3, 6, 0, 224, 226, 3, 130, 65, 0, 225, 199, 1, 0, 0, 0, 225, 200, 1, 0, 0, 0, 225, 201, 1, 0, 0, 0, 225, 202, 1, 0, 0, 0, 225, 203, 1, 0, 0, 0, 225, 204, 1, 0, 0, 0, 225, 205, 1, 0, 0, 0, 225, 206, 1, 0, 0, 0, 225, 207, 1, 0, 0, 0, 225, 208, 1, 0, 0, 0, 225, 209, 1, 0, 0, 0, 225, 210, 1, 0, 0, 0, 225, 211, 1, 0, 0, 0, 225, 212, 1, 0, 0, 0, 225, 213, 1, 0, 0, 0, 225, 214, 1, 0, 0, 0, 225, 215, 1, 0, 0, 0, 225, 216, 1, 0, 0, 0, 225, 217, 1, 0, 0, 0, 225, 219, 1, 0, 0, 0, 225, 221, 1, 0, 0, 0, 225, 223, 1, 0, 0, 0, 226, 7, 1, 0, 0, 0, 227, 228, 5, 17, 0, 0, 228, 229, 3, 132, 66, 0, 229, 9, 1, 0, 0, 0, 230, 231, 3, 58, 29, 0, 231, 11, 1, 0, 0, 0, 232, 233, 5, 13, 0, 0, 233, 234, 3, 14, 7, 0, 234, 13, 1, 0, 0, 0, 235, 240, 3, 16, 8, 0, 236, 237, 5, 58, 0, 0, 237, 239, 3, 16, 8, 0, 238, 236, 1, 0, 0, 0, 239, 242, 1, 0, 0, 0, 240, 238, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 15, 1, 0, 0, 0, 242, 240, 1, 0, 0, 0, 243, 244, 3, 48, 24, 0, 244, 245, 5, 54, 0, 0, 245, 247, 1, 0, 0, 0, 246, 243, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 3, 132, 66, 0, 249, 17, 1, 0, 0, 0, 250, 255, 3, 20, 10, 0, 251, 252, 5, 58, 0, 0, 252, 254, 3, 20, 10, 0, 253, 251, 1, 0, 0, 0, 254, 257, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 19, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 258, 261, 3, 48, 24, 0, 259, 260, 5, 54, 0, 0, 260, 262, 3, 132, 66, 0, 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 21, 1, 0, 0, 0, 263, 264, 5, 19, 0, 0, 264, 265, 3, 26, 13, 0, 265, 23, 1, 0, 0, 0, 266, 267, 5, 20, 0, 0, 267, 268, 3, 26, 13, 0, 268, 25, 1, 0, 0, 0, 269, 274, 3, 28, 14, 0, 270, 271, 5, 58, 0, 0, 271, 273, 3, 28, 14, 0, 272, 270, 1, 0, 0, 0, 273, 276, 1, 0, 0, 0, 274, 272, 1, 0, 0, 0, 274, 275, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 277, 279, 3, 38, 19, 0, 278, 277, 1, 0, 0, 0, 278, 279, 1, 0, 0, 0, 279, 27, 1, 0, 0, 0, 280, 281, 3, 30, 15, 0, 281, 282, 5, 57, 0, 0, 282, 283, 3, 34, 17, 0, 283, 290, 1, 0, 0, 0, 284, 285, 3, 34, 17, 0, 285, 286, 5, 56, 0, 0, 286, 287, 3, 32, 16, 0, 287, 290, 1, 0, 0, 0, 288, 290, 3, 36, 18, 0, 289, 280, 1, 0, 0, 0, 289, 284, 1, 0, 0, 0, 289, 288, 1, 0, 0, 0, 290, 29, 1, 0, 0, 0, 291, 292, 5, 103, 0, 0, 292, 31, 1, 0, 0, 0, 293, 294, 5, 103, 0, 0, 294, 33, 1, 0, 0, 0, 295, 296, 5, 103, 0, 0, 296, 35, 1, 0, 0, 0, 297, 298, 7, 0, 0, 0, 298, 37, 1, 0, 0, 0, 299, 300, 5, 102, 0, 0, 300, 305, 5, 103, 0, 0, 301, 302, 5, 58, 0, 0, 302, 304, 5, 103, 0, 0, 303, 301, 1, 0, 0, 0, 304, 307, 1, 0, 0, 0, 305, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 39, 1, 0, 0, 0, 307, 305, 1, 0, 0, 0, 308, 309, 5, 9, 0, 0, 309, 310, 3, 14, 7, 0, 310, 41, 1, 0, 0, 0, 311, 313, 5, 16, 0, 0, 312, 314, 3, 44, 22, 0, 313, 312, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 316, 5, 55, 0, 0, 316, 318, 3, 14, 7, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 43, 1, 0, 0, 0, 319, 324, 3, 46, 23, 0, 320, 321, 5, 58, 0, 0, 321, 323, 3, 46, 23, 0, 322, 320, 1, 0, 0, 0, 323, 326, 1, 0, 0, 0, 324, 322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 45, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 327, 330, 3, 16, 8, 0, 328, 329, 5, 17, 0, 0, 329, 331, 3, 132, 66, 0, 330, 328, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 47, 1, 0, 0, 0, 332, 333, 4, 24, 7, 0, 333, 335, 5, 93, 0, 0, 334, 336, 5, 97, 0, 0, 335, 334, 1, 0, 0, 0, 335, 336, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 337, 338, 5, 94, 0, 0, 338, 339, 5, 60, 0, 0, 339, 340, 5, 93, 0, 0, 340, 341, 3, 50, 25, 0, 341, 342, 5, 94, 0, 0, 342, 345, 1, 0, 0, 0, 343, 345, 3, 50, 25, 0, 344, 332, 1, 0, 0, 0, 344, 343, 1, 0, 0, 0, 345, 49, 1, 0, 0, 0, 346, 351, 3, 66, 33, 0, 347, 348, 5, 60, 0, 0, 348, 350, 3, 66, 33, 0, 349, 347, 1, 0, 0, 0, 350, 353, 1, 0, 0, 0, 351, 349, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 51, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 354, 355, 4, 26, 8, 0, 355, 357, 5, 93, 0, 0, 356, 358, 5, 124, 0, 0, 357, 356, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 360, 5, 94, 0, 0, 360, 361, 5, 60, 0, 0, 361, 362, 5, 93, 0, 0, 362, 363, 3, 54, 27, 0, 363, 364, 5, 94, 0, 0, 364, 367, 1, 0, 0, 0, 365, 367, 3, 54, 27, 0, 366, 354, 1, 0, 0, 0, 366, 365, 1, 0, 0, 0, 367, 53, 1, 0, 0, 0, 368, 373, 3, 60, 30, 0, 369, 370, 5, 60, 0, 0, 370, 372, 3, 60, 30, 0, 371, 369, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 373, 374, 1, 0, 0, 0, 374, 55, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0, 376, 381, 3, 52, 26, 0, 377, 378, 5, 58, 0, 0, 378, 380, 3, 52, 26, 0, 379, 377, 1, 0, 0, 0, 380, 383, 1, 0, 0, 0, 381, 379, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 57, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 384, 385, 7, 1, 0, 0, 385, 59, 1, 0, 0, 0, 386, 390, 5, 124, 0, 0, 387, 390, 3, 62, 31, 0, 388, 390, 3, 64, 32, 0, 389, 386, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 389, 388, 1, 0, 0, 0, 390, 61, 1, 0, 0, 0, 391, 394, 5, 72, 0, 0, 392, 394, 5, 91, 0, 0, 393, 391, 1, 0, 0, 0, 393, 392, 1, 0, 0, 0, 394, 63, 1, 0, 0, 0, 395, 398, 5, 90, 0, 0, 396, 398, 5, 92, 0, 0, 397, 395, 1, 0, 0, 0, 397, 396, 1, 0, 0, 0, 398, 65, 1, 0, 0, 0, 399, 403, 3, 58, 29, 0, 400, 403, 3, 62, 31, 0, 401, 403, 3, 64, 32, 0, 402, 399, 1, 0, 0, 0, 402, 400, 1, 0, 0, 0, 402, 401, 1, 0, 0, 0, 403, 67, 1, 0, 0, 0, 404, 405, 5, 11, 0, 0, 405, 406, 3, 156, 78, 0, 406, 69, 1, 0, 0, 0, 407, 408, 5, 15, 0, 0, 408, 413, 3, 72, 36, 0, 409, 410, 5, 58, 0, 0, 410, 412, 3, 72, 36, 0, 411, 409, 1, 0, 0, 0, 412, 415, 1, 0, 0, 0, 413, 411, 1, 0, 0, 0, 413, 414, 1, 0, 0, 0, 414, 71, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 416, 418, 3, 132, 66, 0, 417, 419, 7, 2, 0, 0, 418, 417, 1, 0, 0, 0, 418, 419, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 421, 5, 69, 0, 0, 421, 423, 7, 3, 0, 0, 422, 420, 1, 0, 0, 0, 422, 423, 1, 0, 0, 0, 423, 73, 1, 0, 0, 0, 424, 425, 5, 30, 0, 0, 425, 426, 3, 56, 28, 0, 426, 75, 1, 0, 0, 0, 427, 428, 5, 29, 0, 0, 428, 429, 3, 56, 28, 0, 429, 77, 1, 0, 0, 0, 430, 431, 5, 32, 0, 0, 431, 436, 3, 80, 40, 0, 432, 433, 5, 58, 0, 0, 433, 435, 3, 80, 40, 0, 434, 432, 1, 0, 0, 0, 435, 438, 1, 0, 0, 0, 436, 434, 1, 0, 0, 0, 436, 437, 1, 0, 0, 0, 437, 79, 1, 0, 0, 0, 438, 436, 1, 0, 0, 0, 439, 440, 3, 52, 26, 0, 440, 441, 5, 128, 0, 0, 441, 442, 3, 52, 26, 0, 442, 448, 1, 0, 0, 0, 443, 444, 3, 52, 26, 0, 444, 445, 5, 54, 0, 0, 445, 446, 3, 52, 26, 0, 446, 448, 1, 0, 0, 0, 447, 439, 1, 0, 0, 0, 447, 443, 1, 0, 0, 0, 448, 81, 1, 0, 0, 0, 449, 450, 5, 8, 0, 0, 450, 451, 3, 144, 72, 0, 451, 453, 3, 166, 83, 0, 452, 454, 3, 84, 42, 0, 453, 452, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 83, 1, 0, 0, 0, 455, 460, 3, 86, 43, 0, 456, 457, 5, 58, 0, 0, 457, 459, 3, 86, 43, 0, 458, 456, 1, 0, 0, 0, 459, 462, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 461, 85, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 463, 464, 3, 58, 29, 0, 464, 465, 5, 54, 0, 0, 465, 466, 3, 156, 78, 0, 466, 87, 1, 0, 0, 0, 467, 468, 5, 75, 0, 0, 468, 470, 3, 150, 75, 0, 469, 467, 1, 0, 0, 0, 469, 470, 1, 0, 0, 0, 470, 89, 1, 0, 0, 0, 471, 472, 5, 10, 0, 0, 472, 473, 3, 144, 72, 0, 473, 474, 3, 166, 83, 0, 474, 91, 1, 0, 0, 0, 475, 476, 5, 28, 0, 0, 476, 477, 3, 48, 24, 0, 477, 93, 1, 0, 0, 0, 478, 479, 5, 6, 0, 0, 479, 480, 3, 96, 48, 0, 480, 95, 1, 0, 0, 0, 481, 482, 5, 95, 0, 0, 482, 483, 3, 2, 1, 0, 483, 484, 5, 96, 0, 0, 484, 97, 1, 0, 0, 0, 485, 486, 5, 33, 0, 0, 486, 487, 5, 132, 0, 0, 487, 99, 1, 0, 0, 0, 488, 489, 5, 5, 0, 0, 489, 492, 3, 102, 51, 0, 490, 491, 5, 70, 0, 0, 491, 493, 3, 52, 26, 0, 492, 490, 1, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 503, 1, 0, 0, 0, 494, 495, 5, 75, 0, 0, 495, 500, 3, 104, 52, 0, 496, 497, 5, 58, 0, 0, 497, 499, 3, 104, 52, 0, 498, 496, 1, 0, 0, 0, 499, 502, 1, 0, 0, 0, 500, 498, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 504, 1, 0, 0, 0, 502, 500, 1, 0, 0, 0, 503, 494, 1, 0, 0, 0, 503, 504, 1, 0, 0, 0, 504, 101, 1, 0, 0, 0, 505, 506, 7, 4, 0, 0, 506, 103, 1, 0, 0, 0, 507, 508, 3, 52, 26, 0, 508, 509, 5, 54, 0, 0, 509, 511, 1, 0, 0, 0, 510, 507, 1, 0, 0, 0, 510, 511, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 512, 513, 3, 52, 26, 0, 513, 105, 1, 0, 0, 0, 514, 515, 5, 14, 0, 0, 515, 516, 3, 156, 78, 0, 516, 107, 1, 0, 0, 0, 517, 518, 5, 4, 0, 0, 518, 521, 3, 48, 24, 0, 519, 520, 5, 70, 0, 0, 520, 522, 3, 48, 24, 0, 521, 519, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 528, 1, 0, 0, 0, 523, 524, 5, 128, 0, 0, 524, 525, 3, 48, 24, 0, 525, 526, 5, 58, 0, 0, 526, 527, 3, 48, 24, 0, 527, 529, 1, 0, 0, 0, 528, 523, 1, 0, 0, 0, 528, 529, 1, 0, 0, 0, 529, 109, 1, 0, 0, 0, 530, 531, 5, 21, 0, 0, 531, 532, 3, 112, 56, 0, 532, 111, 1, 0, 0, 0, 533, 535, 3, 114, 57, 0, 534, 533, 1, 0, 0, 0, 535, 536, 1, 0, 0, 0, 536, 534, 1, 0, 0, 0, 536, 537, 1, 0, 0, 0, 537, 113, 1, 0, 0, 0, 538, 539, 5, 95, 0, 0, 539, 540, 3, 116, 58, 0, 540, 541, 5, 96, 0, 0, 541, 115, 1, 0, 0, 0, 542, 543, 6, 58, -1, 0, 543, 544, 3, 118, 59, 0, 544, 550, 1, 0, 0, 0, 545, 546, 10, 1, 0, 0, 546, 547, 5, 48, 0, 0, 547, 549, 3, 118, 59, 0, 548, 545, 1, 0, 0, 0, 549, 552, 1, 0, 0, 0, 550, 548, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 117, 1, 0, 0, 0, 552, 550, 1, 0, 0, 0, 553, 554, 3, 6, 3, 0, 554, 119, 1, 0, 0, 0, 555, 559, 5, 12, 0, 0, 556, 557, 3, 48, 24, 0, 557, 558, 5, 54, 0, 0, 558, 560, 1, 0, 0, 0, 559, 556, 1, 0, 0, 0, 559, 560, 1, 0, 0, 0, 560, 561, 1, 0, 0, 0, 561, 562, 3, 156, 78, 0, 562, 563, 5, 70, 0, 0, 563, 564, 3, 18, 9, 0, 564, 565, 3, 88, 44, 0, 565, 121, 1, 0, 0, 0, 566, 570, 5, 7, 0, 0, 567, 568, 3, 48, 24, 0, 568, 569, 5, 54, 0, 0, 569, 571, 1, 0, 0, 0, 570, 567, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 1, 0, 0, 0, 572, 573, 3, 144, 72, 0, 573, 574, 3, 88, 44, 0, 574, 123, 1, 0, 0, 0, 575, 576, 5, 27, 0, 0, 576, 577, 3, 28, 14, 0, 577, 578, 5, 70, 0, 0, 578, 579, 3, 56, 28, 0, 579, 125, 1, 0, 0, 0, 580, 581, 5, 18, 0, 0, 581, 584, 3, 44, 22, 0, 582, 583, 5, 55, 0, 0, 583, 585, 3, 14, 7, 0, 584, 582, 1, 0, 0, 0, 584, 585, 1, 0, 0, 0, 585, 127, 1, 0, 0, 0, 586, 587, 5, 31, 0, 0, 587, 588, 3, 56, 28, 0, 588, 129, 1, 0, 0, 0, 589, 590, 5, 22, 0, 0, 590, 131, 1, 0, 0, 0, 591, 592, 6, 66, -1, 0, 592, 593, 5, 67, 0, 0, 593, 621, 3, 132, 66, 8, 594, 621, 3, 138, 69, 0, 595, 621, 3, 134, 67, 0, 596, 598, 3, 138, 69, 0, 597, 599, 5, 67, 0, 0, 598, 597, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 600, 1, 0, 0, 0, 600, 601, 5, 63, 0, 0, 601, 602, 5, 95, 0, 0, 602, 607, 3, 138, 69, 0, 603, 604, 5, 58, 0, 0, 604, 606, 3, 138, 69, 0, 605, 603, 1, 0, 0, 0, 606, 609, 1, 0, 0, 0, 607, 605, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 610, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 610, 611, 5, 96, 0, 0, 611, 621, 1, 0, 0, 0, 612, 613, 3, 138, 69, 0, 613, 615, 5, 64, 0, 0, 614, 616, 5, 67, 0, 0, 615, 614, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 618, 5, 68, 0, 0, 618, 621, 1, 0, 0, 0, 619, 621, 3, 136, 68, 0, 620, 591, 1, 0, 0, 0, 620, 594, 1, 0, 0, 0, 620, 595, 1, 0, 0, 0, 620, 596, 1, 0, 0, 0, 620, 612, 1, 0, 0, 0, 620, 619, 1, 0, 0, 0, 621, 630, 1, 0, 0, 0, 622, 623, 10, 5, 0, 0, 623, 624, 5, 52, 0, 0, 624, 629, 3, 132, 66, 6, 625, 626, 10, 4, 0, 0, 626, 627, 5, 71, 0, 0, 627, 629, 3, 132, 66, 5, 628, 622, 1, 0, 0, 0, 628, 625, 1, 0, 0, 0, 629, 632, 1, 0, 0, 0, 630, 628, 1, 0, 0, 0, 630, 631, 1, 0, 0, 0, 631, 133, 1, 0, 0, 0, 632, 630, 1, 0, 0, 0, 633, 635, 3, 138, 69, 0, 634, 636, 5, 67, 0, 0, 635, 634, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, 1, 0, 0, 0, 637, 638, 5, 66, 0, 0, 638, 639, 3, 166, 83, 0, 639, 680, 1, 0, 0, 0, 640, 642, 3, 138, 69, 0, 641, 643, 5, 67, 0, 0, 642, 641, 1, 0, 0, 0, 642, 643, 1, 0, 0, 0, 643, 644, 1, 0, 0, 0, 644, 645, 5, 73, 0, 0, 645, 646, 3, 166, 83, 0, 646, 680, 1, 0, 0, 0, 647, 649, 3, 138, 69, 0, 648, 650, 5, 67, 0, 0, 649, 648, 1, 0, 0, 0, 649, 650, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 652, 5, 66, 0, 0, 652, 653, 5, 95, 0, 0, 653, 658, 3, 166, 83, 0, 654, 655, 5, 58, 0, 0, 655, 657, 3, 166, 83, 0, 656, 654, 1, 0, 0, 0, 657, 660, 1, 0, 0, 0, 658, 656, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 661, 1, 0, 0, 0, 660, 658, 1, 0, 0, 0, 661, 662, 5, 96, 0, 0, 662, 680, 1, 0, 0, 0, 663, 665, 3, 138, 69, 0, 664, 666, 5, 67, 0, 0, 665, 664, 1, 0, 0, 0, 665, 666, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 668, 5, 73, 0, 0, 668, 669, 5, 95, 0, 0, 669, 674, 3, 166, 83, 0, 670, 671, 5, 58, 0, 0, 671, 673, 3, 166, 83, 0, 672, 670, 1, 0, 0, 0, 673, 676, 1, 0, 0, 0, 674, 672, 1, 0, 0, 0, 674, 675, 1, 0, 0, 0, 675, 677, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 677, 678, 5, 96, 0, 0, 678, 680, 1, 0, 0, 0, 679, 633, 1, 0, 0, 0, 679, 640, 1, 0, 0, 0, 679, 647, 1, 0, 0, 0, 679, 663, 1, 0, 0, 0, 680, 135, 1, 0, 0, 0, 681, 684, 3, 48, 24, 0, 682, 683, 5, 56, 0, 0, 683, 685, 3, 10, 5, 0, 684, 682, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 686, 1, 0, 0, 0, 686, 687, 5, 57, 0, 0, 687, 688, 3, 156, 78, 0, 688, 137, 1, 0, 0, 0, 689, 692, 3, 142, 71, 0, 690, 692, 3, 140, 70, 0, 691, 689, 1, 0, 0, 0, 691, 690, 1, 0, 0, 0, 692, 139, 1, 0, 0, 0, 693, 694, 3, 142, 71, 0, 694, 695, 3, 168, 84, 0, 695, 696, 3, 142, 71, 0, 696, 141, 1, 0, 0, 0, 697, 698, 6, 71, -1, 0, 698, 702, 3, 144, 72, 0, 699, 700, 7, 5, 0, 0, 700, 702, 3, 142, 71, 3, 701, 697, 1, 0, 0, 0, 701, 699, 1, 0, 0, 0, 702, 711, 1, 0, 0, 0, 703, 704, 10, 2, 0, 0, 704, 705, 7, 6, 0, 0, 705, 710, 3, 142, 71, 3, 706, 707, 10, 1, 0, 0, 707, 708, 7, 5, 0, 0, 708, 710, 3, 142, 71, 2, 709, 703, 1, 0, 0, 0, 709, 706, 1, 0, 0, 0, 710, 713, 1, 0, 0, 0, 711, 709, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 143, 1, 0, 0, 0, 713, 711, 1, 0, 0, 0, 714, 715, 6, 72, -1, 0, 715, 723, 3, 156, 78, 0, 716, 723, 3, 48, 24, 0, 717, 723, 3, 146, 73, 0, 718, 719, 5, 95, 0, 0, 719, 720, 3, 132, 66, 0, 720, 721, 5, 96, 0, 0, 721, 723, 1, 0, 0, 0, 722, 714, 1, 0, 0, 0, 722, 716, 1, 0, 0, 0, 722, 717, 1, 0, 0, 0, 722, 718, 1, 0, 0, 0, 723, 729, 1, 0, 0, 0, 724, 725, 10, 1, 0, 0, 725, 726, 5, 56, 0, 0, 726, 728, 3, 10, 5, 0, 727, 724, 1, 0, 0, 0, 728, 731, 1, 0, 0, 0, 729, 727, 1, 0, 0, 0, 729, 730, 1, 0, 0, 0, 730, 145, 1, 0, 0, 0, 731, 729, 1, 0, 0, 0, 732, 733, 3, 148, 74, 0, 733, 747, 5, 95, 0, 0, 734, 748, 5, 85, 0, 0, 735, 740, 3, 132, 66, 0, 736, 737, 5, 58, 0, 0, 737, 739, 3, 132, 66, 0, 738, 736, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 745, 1, 0, 0, 0, 742, 740, 1, 0, 0, 0, 743, 744, 5, 58, 0, 0, 744, 746, 3, 150, 75, 0, 745, 743, 1, 0, 0, 0, 745, 746, 1, 0, 0, 0, 746, 748, 1, 0, 0, 0, 747, 734, 1, 0, 0, 0, 747, 735, 1, 0, 0, 0, 747, 748, 1, 0, 0, 0, 748, 749, 1, 0, 0, 0, 749, 750, 5, 96, 0, 0, 750, 147, 1, 0, 0, 0, 751, 755, 3, 66, 33, 0, 752, 755, 5, 62, 0, 0, 753, 755, 5, 65, 0, 0, 754, 751, 1, 0, 0, 0, 754, 752, 1, 0, 0, 0, 754, 753, 1, 0, 0, 0, 755, 149, 1, 0, 0, 0, 756, 765, 5, 88, 0, 0, 757, 762, 3, 152, 76, 0, 758, 759, 5, 58, 0, 0, 759, 761, 3, 152, 76, 0, 760, 758, 1, 0, 0, 0, 761, 764, 1, 0, 0, 0, 762, 760, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 766, 1, 0, 0, 0, 764, 762, 1, 0, 0, 0, 765, 757, 1, 0, 0, 0, 765, 766, 1, 0, 0, 0, 766, 767, 1, 0, 0, 0, 767, 768, 5, 89, 0, 0, 768, 151, 1, 0, 0, 0, 769, 770, 3, 166, 83, 0, 770, 771, 5, 57, 0, 0, 771, 772, 3, 154, 77, 0, 772, 153, 1, 0, 0, 0, 773, 776, 3, 156, 78, 0, 774, 776, 3, 150, 75, 0, 775, 773, 1, 0, 0, 0, 775, 774, 1, 0, 0, 0, 776, 155, 1, 0, 0, 0, 777, 820, 5, 68, 0, 0, 778, 779, 3, 164, 82, 0, 779, 780, 5, 97, 0, 0, 780, 820, 1, 0, 0, 0, 781, 820, 3, 162, 81, 0, 782, 820, 3, 164, 82, 0, 783, 820, 3, 158, 79, 0, 784, 820, 3, 62, 31, 0, 785, 820, 3, 166, 83, 0, 786, 787, 5, 93, 0, 0, 787, 792, 3, 160, 80, 0, 788, 789, 5, 58, 0, 0, 789, 791, 3, 160, 80, 0, 790, 788, 1, 0, 0, 0, 791, 794, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 795, 1, 0, 0, 0, 794, 792, 1, 0, 0, 0, 795, 796, 5, 94, 0, 0, 796, 820, 1, 0, 0, 0, 797, 798, 5, 93, 0, 0, 798, 803, 3, 158, 79, 0, 799, 800, 5, 58, 0, 0, 800, 802, 3, 158, 79, 0, 801, 799, 1, 0, 0, 0, 802, 805, 1, 0, 0, 0, 803, 801, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 806, 1, 0, 0, 0, 805, 803, 1, 0, 0, 0, 806, 807, 5, 94, 0, 0, 807, 820, 1, 0, 0, 0, 808, 809, 5, 93, 0, 0, 809, 814, 3, 166, 83, 0, 810, 811, 5, 58, 0, 0, 811, 813, 3, 166, 83, 0, 812, 810, 1, 0, 0, 0, 813, 816, 1, 0, 0, 0, 814, 812, 1, 0, 0, 0, 814, 815, 1, 0, 0, 0, 815, 817, 1, 0, 0, 0, 816, 814, 1, 0, 0, 0, 817, 818, 5, 94, 0, 0, 818, 820, 1, 0, 0, 0, 819, 777, 1, 0, 0, 0, 819, 778, 1, 0, 0, 0, 819, 781, 1, 0, 0, 0, 819, 782, 1, 0, 0, 0, 819, 783, 1, 0, 0, 0, 819, 784, 1, 0, 0, 0, 819, 785, 1, 0, 0, 0, 819, 786, 1, 0, 0, 0, 819, 797, 1, 0, 0, 0, 819, 808, 1, 0, 0, 0, 820, 157, 1, 0, 0, 0, 821, 822, 7, 7, 0, 0, 822, 159, 1, 0, 0, 0, 823, 826, 3, 162, 81, 0, 824, 826, 3, 164, 82, 0, 825, 823, 1, 0, 0, 0, 825, 824, 1, 0, 0, 0, 826, 161, 1, 0, 0, 0, 827, 829, 7, 5, 0, 0, 828, 827, 1, 0, 0, 0, 828, 829, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 831, 5, 51, 0, 0, 831, 163, 1, 0, 0, 0, 832, 834, 7, 5, 0, 0, 833, 832, 1, 0, 0, 0, 833, 834, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 5, 50, 0, 0, 836, 165, 1, 0, 0, 0, 837, 838, 5, 49, 0, 0, 838, 167, 1, 0, 0, 0, 839, 840, 7, 8, 0, 0, 840, 169, 1, 0, 0, 0, 841, 842, 7, 9, 0, 0, 842, 843, 5, 110, 0, 0, 843, 844, 3, 172, 86, 0, 844, 845, 3, 174, 87, 0, 845, 171, 1, 0, 0, 0, 846, 847, 4, 86, 15, 0, 847, 849, 3, 28, 14, 0, 848, 850, 5, 128, 0, 0, 849, 848, 1, 0, 0, 0, 849, 850, 1, 0, 0, 0, 850, 851, 1, 0, 0, 0, 851, 852, 5, 103, 0, 0, 852, 855, 1, 0, 0, 0, 853, 855, 3, 28, 14, 0, 854, 846, 1, 0, 0, 0, 854, 853, 1, 0, 0, 0, 855, 173, 1, 0, 0, 0, 856, 857, 5, 70, 0, 0, 857, 862, 3, 48, 24, 0, 858, 859, 5, 58, 0, 0, 859, 861, 3, 48, 24, 0, 860, 858, 1, 0, 0, 0, 861, 864, 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 862, 863, 1, 0, 0, 0, 863, 875, 1, 0, 0, 0, 864, 862, 1, 0, 0, 0, 865, 866, 5, 70, 0, 0, 866, 871, 3, 140, 70, 0, 867, 868, 5, 52, 0, 0, 868, 870, 3, 140, 70, 0, 869, 867, 1, 0, 0, 0, 870, 873, 1, 0, 0, 0, 871, 869, 1, 0, 0, 0, 871, 872, 1, 0, 0, 0, 872, 875, 1, 0, 0, 0, 873, 871, 1, 0, 0, 0, 874, 856, 1, 0, 0, 0, 874, 865, 1, 0, 0, 0, 875, 175, 1, 0, 0, 0, 84, 187, 197, 225, 240, 246, 255, 261, 274, 278, 289, 305, 313, 317, 324, 330, 335, 344, 351, 357, 366, 373, 381, 389, 393, 397, 402, 413, 418, 422, 436, 447, 453, 460, 469, 492, 500, 503, 510, 521, 528, 536, 550, 559, 570, 584, 598, 607, 615, 620, 628, 630, 635, 642, 649, 658, 665, 674, 679, 684, 691, 701, 709, 711, 722, 729, 740, 745, 747, 754, 762, 765, 775, 792, 803, 814, 819, 825, 828, 833, 849, 854, 862, 871, 874]
\ No newline at end of file
+[4, 1, 135, 884, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 188, 8, 1, 10, 1, 12, 1, 191, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 200, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 228, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 241, 8, 7, 10, 7, 12, 7, 244, 9, 7, 1, 8, 1, 8, 1, 8, 3, 8, 249, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 256, 8, 9, 10, 9, 12, 9, 259, 9, 9, 1, 10, 1, 10, 1, 10, 3, 10, 264, 8, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 275, 8, 13, 10, 13, 12, 13, 278, 9, 13, 1, 13, 3, 13, 281, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 292, 8, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 306, 8, 19, 10, 19, 12, 19, 309, 9, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 316, 8, 21, 1, 21, 1, 21, 3, 21, 320, 8, 21, 1, 22, 1, 22, 1, 22, 5, 22, 325, 8, 22, 10, 22, 12, 22, 328, 9, 22, 1, 23, 1, 23, 1, 23, 3, 23, 333, 8, 23, 1, 24, 1, 24, 1, 24, 3, 24, 338, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 347, 8, 24, 1, 25, 1, 25, 1, 25, 5, 25, 352, 8, 25, 10, 25, 12, 25, 355, 9, 25, 1, 26, 1, 26, 1, 26, 3, 26, 360, 8, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 3, 26, 369, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 374, 8, 27, 10, 27, 12, 27, 377, 9, 27, 1, 28, 1, 28, 1, 28, 5, 28, 382, 8, 28, 10, 28, 12, 28, 385, 9, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 3, 30, 392, 8, 30, 1, 31, 1, 31, 3, 31, 396, 8, 31, 1, 32, 1, 32, 3, 32, 400, 8, 32, 1, 33, 1, 33, 1, 33, 3, 33, 405, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 414, 8, 35, 10, 35, 12, 35, 417, 9, 35, 1, 36, 1, 36, 3, 36, 421, 8, 36, 1, 36, 1, 36, 3, 36, 425, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 437, 8, 39, 10, 39, 12, 39, 440, 9, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 450, 8, 40, 1, 41, 1, 41, 1, 41, 1, 41, 3, 41, 456, 8, 41, 1, 42, 1, 42, 1, 42, 5, 42, 461, 8, 42, 10, 42, 12, 42, 464, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 3, 44, 472, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 495, 8, 50, 1, 50, 1, 50, 1, 50, 1, 50, 5, 50, 501, 8, 50, 10, 50, 12, 50, 504, 9, 50, 3, 50, 506, 8, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 3, 52, 513, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 524, 8, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 531, 8, 54, 1, 55, 1, 55, 1, 55, 1, 56, 4, 56, 537, 8, 56, 11, 56, 12, 56, 538, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 551, 8, 58, 10, 58, 12, 58, 554, 9, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 3, 60, 562, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 573, 8, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 587, 8, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 601, 8, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 5, 66, 608, 8, 66, 10, 66, 12, 66, 611, 9, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 618, 8, 66, 1, 66, 1, 66, 1, 66, 3, 66, 623, 8, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 5, 66, 631, 8, 66, 10, 66, 12, 66, 634, 9, 66, 1, 67, 1, 67, 3, 67, 638, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 645, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 652, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 659, 8, 67, 10, 67, 12, 67, 662, 9, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 668, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 5, 67, 675, 8, 67, 10, 67, 12, 67, 678, 9, 67, 1, 67, 1, 67, 3, 67, 682, 8, 67, 1, 68, 1, 68, 1, 68, 3, 68, 687, 8, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 697, 8, 69, 1, 70, 1, 70, 1, 70, 1, 70, 3, 70, 703, 8, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 5, 70, 711, 8, 70, 10, 70, 12, 70, 714, 9, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 3, 71, 724, 8, 71, 1, 71, 1, 71, 1, 71, 5, 71, 729, 8, 71, 10, 71, 12, 71, 732, 9, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 5, 72, 740, 8, 72, 10, 72, 12, 72, 743, 9, 72, 1, 72, 1, 72, 3, 72, 747, 8, 72, 3, 72, 749, 8, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 3, 73, 756, 8, 73, 1, 74, 1, 74, 1, 74, 1, 74, 5, 74, 762, 8, 74, 10, 74, 12, 74, 765, 9, 74, 3, 74, 767, 8, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 3, 76, 777, 8, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 5, 77, 792, 8, 77, 10, 77, 12, 77, 795, 9, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 5, 77, 803, 8, 77, 10, 77, 12, 77, 806, 9, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 5, 77, 814, 8, 77, 10, 77, 12, 77, 817, 9, 77, 1, 77, 1, 77, 3, 77, 821, 8, 77, 1, 78, 1, 78, 1, 79, 1, 79, 3, 79, 827, 8, 79, 1, 80, 3, 80, 830, 8, 80, 1, 80, 1, 80, 1, 81, 3, 81, 835, 8, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 3, 85, 851, 8, 85, 1, 85, 1, 85, 1, 85, 3, 85, 856, 8, 85, 1, 86, 1, 86, 1, 86, 1, 86, 5, 86, 862, 8, 86, 10, 86, 12, 86, 865, 9, 86, 1, 86, 1, 86, 1, 86, 1, 86, 5, 86, 871, 8, 86, 10, 86, 12, 86, 874, 9, 86, 3, 86, 876, 8, 86, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 88, 0, 5, 2, 116, 132, 140, 142, 89, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 0, 10, 2, 0, 49, 49, 103, 103, 1, 0, 97, 98, 2, 0, 53, 53, 59, 59, 2, 0, 62, 62, 65, 65, 2, 0, 38, 38, 49, 49, 1, 0, 83, 84, 1, 0, 85, 87, 2, 0, 61, 61, 74, 74, 2, 0, 76, 76, 78, 82, 2, 0, 23, 23, 25, 26, 922, 0, 178, 1, 0, 0, 0, 2, 181, 1, 0, 0, 0, 4, 199, 1, 0, 0, 0, 6, 227, 1, 0, 0, 0, 8, 229, 1, 0, 0, 0, 10, 232, 1, 0, 0, 0, 12, 234, 1, 0, 0, 0, 14, 237, 1, 0, 0, 0, 16, 248, 1, 0, 0, 0, 18, 252, 1, 0, 0, 0, 20, 260, 1, 0, 0, 0, 22, 265, 1, 0, 0, 0, 24, 268, 1, 0, 0, 0, 26, 271, 1, 0, 0, 0, 28, 291, 1, 0, 0, 0, 30, 293, 1, 0, 0, 0, 32, 295, 1, 0, 0, 0, 34, 297, 1, 0, 0, 0, 36, 299, 1, 0, 0, 0, 38, 301, 1, 0, 0, 0, 40, 310, 1, 0, 0, 0, 42, 313, 1, 0, 0, 0, 44, 321, 1, 0, 0, 0, 46, 329, 1, 0, 0, 0, 48, 346, 1, 0, 0, 0, 50, 348, 1, 0, 0, 0, 52, 368, 1, 0, 0, 0, 54, 370, 1, 0, 0, 0, 56, 378, 1, 0, 0, 0, 58, 386, 1, 0, 0, 0, 60, 391, 1, 0, 0, 0, 62, 395, 1, 0, 0, 0, 64, 399, 1, 0, 0, 0, 66, 404, 1, 0, 0, 0, 68, 406, 1, 0, 0, 0, 70, 409, 1, 0, 0, 0, 72, 418, 1, 0, 0, 0, 74, 426, 1, 0, 0, 0, 76, 429, 1, 0, 0, 0, 78, 432, 1, 0, 0, 0, 80, 449, 1, 0, 0, 0, 82, 451, 1, 0, 0, 0, 84, 457, 1, 0, 0, 0, 86, 465, 1, 0, 0, 0, 88, 471, 1, 0, 0, 0, 90, 473, 1, 0, 0, 0, 92, 477, 1, 0, 0, 0, 94, 480, 1, 0, 0, 0, 96, 483, 1, 0, 0, 0, 98, 487, 1, 0, 0, 0, 100, 490, 1, 0, 0, 0, 102, 507, 1, 0, 0, 0, 104, 512, 1, 0, 0, 0, 106, 516, 1, 0, 0, 0, 108, 519, 1, 0, 0, 0, 110, 532, 1, 0, 0, 0, 112, 536, 1, 0, 0, 0, 114, 540, 1, 0, 0, 0, 116, 544, 1, 0, 0, 0, 118, 555, 1, 0, 0, 0, 120, 557, 1, 0, 0, 0, 122, 568, 1, 0, 0, 0, 124, 577, 1, 0, 0, 0, 126, 582, 1, 0, 0, 0, 128, 588, 1, 0, 0, 0, 130, 591, 1, 0, 0, 0, 132, 622, 1, 0, 0, 0, 134, 681, 1, 0, 0, 0, 136, 683, 1, 0, 0, 0, 138, 696, 1, 0, 0, 0, 140, 702, 1, 0, 0, 0, 142, 723, 1, 0, 0, 0, 144, 733, 1, 0, 0, 0, 146, 755, 1, 0, 0, 0, 148, 757, 1, 0, 0, 0, 150, 770, 1, 0, 0, 0, 152, 776, 1, 0, 0, 0, 154, 820, 1, 0, 0, 0, 156, 822, 1, 0, 0, 0, 158, 826, 1, 0, 0, 0, 160, 829, 1, 0, 0, 0, 162, 834, 1, 0, 0, 0, 164, 838, 1, 0, 0, 0, 166, 840, 1, 0, 0, 0, 168, 842, 1, 0, 0, 0, 170, 855, 1, 0, 0, 0, 172, 875, 1, 0, 0, 0, 174, 877, 1, 0, 0, 0, 176, 879, 1, 0, 0, 0, 178, 179, 3, 2, 1, 0, 179, 180, 5, 0, 0, 1, 180, 1, 1, 0, 0, 0, 181, 182, 6, 1, -1, 0, 182, 183, 3, 4, 2, 0, 183, 189, 1, 0, 0, 0, 184, 185, 10, 1, 0, 0, 185, 186, 5, 48, 0, 0, 186, 188, 3, 6, 3, 0, 187, 184, 1, 0, 0, 0, 188, 191, 1, 0, 0, 0, 189, 187, 1, 0, 0, 0, 189, 190, 1, 0, 0, 0, 190, 3, 1, 0, 0, 0, 191, 189, 1, 0, 0, 0, 192, 200, 3, 22, 11, 0, 193, 200, 3, 12, 6, 0, 194, 200, 3, 98, 49, 0, 195, 196, 4, 2, 1, 0, 196, 200, 3, 24, 12, 0, 197, 198, 4, 2, 2, 0, 198, 200, 3, 94, 47, 0, 199, 192, 1, 0, 0, 0, 199, 193, 1, 0, 0, 0, 199, 194, 1, 0, 0, 0, 199, 195, 1, 0, 0, 0, 199, 197, 1, 0, 0, 0, 200, 5, 1, 0, 0, 0, 201, 228, 3, 40, 20, 0, 202, 228, 3, 8, 4, 0, 203, 228, 3, 74, 37, 0, 204, 228, 3, 68, 34, 0, 205, 228, 3, 42, 21, 0, 206, 228, 3, 70, 35, 0, 207, 228, 3, 76, 38, 0, 208, 228, 3, 78, 39, 0, 209, 228, 3, 82, 41, 0, 210, 228, 3, 90, 45, 0, 211, 228, 3, 100, 50, 0, 212, 228, 3, 92, 46, 0, 213, 228, 3, 168, 84, 0, 214, 228, 3, 108, 54, 0, 215, 228, 3, 122, 61, 0, 216, 228, 3, 106, 53, 0, 217, 228, 3, 110, 55, 0, 218, 228, 3, 120, 60, 0, 219, 220, 4, 3, 3, 0, 220, 228, 3, 126, 63, 0, 221, 222, 4, 3, 4, 0, 222, 228, 3, 124, 62, 0, 223, 224, 4, 3, 5, 0, 224, 228, 3, 128, 64, 0, 225, 226, 4, 3, 6, 0, 226, 228, 3, 130, 65, 0, 227, 201, 1, 0, 0, 0, 227, 202, 1, 0, 0, 0, 227, 203, 1, 0, 0, 0, 227, 204, 1, 0, 0, 0, 227, 205, 1, 0, 0, 0, 227, 206, 1, 0, 0, 0, 227, 207, 1, 0, 0, 0, 227, 208, 1, 0, 0, 0, 227, 209, 1, 0, 0, 0, 227, 210, 1, 0, 0, 0, 227, 211, 1, 0, 0, 0, 227, 212, 1, 0, 0, 0, 227, 213, 1, 0, 0, 0, 227, 214, 1, 0, 0, 0, 227, 215, 1, 0, 0, 0, 227, 216, 1, 0, 0, 0, 227, 217, 1, 0, 0, 0, 227, 218, 1, 0, 0, 0, 227, 219, 1, 0, 0, 0, 227, 221, 1, 0, 0, 0, 227, 223, 1, 0, 0, 0, 227, 225, 1, 0, 0, 0, 228, 7, 1, 0, 0, 0, 229, 230, 5, 17, 0, 0, 230, 231, 3, 132, 66, 0, 231, 9, 1, 0, 0, 0, 232, 233, 3, 58, 29, 0, 233, 11, 1, 0, 0, 0, 234, 235, 5, 13, 0, 0, 235, 236, 3, 14, 7, 0, 236, 13, 1, 0, 0, 0, 237, 242, 3, 16, 8, 0, 238, 239, 5, 58, 0, 0, 239, 241, 3, 16, 8, 0, 240, 238, 1, 0, 0, 0, 241, 244, 1, 0, 0, 0, 242, 240, 1, 0, 0, 0, 242, 243, 1, 0, 0, 0, 243, 15, 1, 0, 0, 0, 244, 242, 1, 0, 0, 0, 245, 246, 3, 48, 24, 0, 246, 247, 5, 54, 0, 0, 247, 249, 1, 0, 0, 0, 248, 245, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 251, 3, 132, 66, 0, 251, 17, 1, 0, 0, 0, 252, 257, 3, 20, 10, 0, 253, 254, 5, 58, 0, 0, 254, 256, 3, 20, 10, 0, 255, 253, 1, 0, 0, 0, 256, 259, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 19, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 260, 263, 3, 48, 24, 0, 261, 262, 5, 54, 0, 0, 262, 264, 3, 132, 66, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 21, 1, 0, 0, 0, 265, 266, 5, 19, 0, 0, 266, 267, 3, 26, 13, 0, 267, 23, 1, 0, 0, 0, 268, 269, 5, 20, 0, 0, 269, 270, 3, 26, 13, 0, 270, 25, 1, 0, 0, 0, 271, 276, 3, 28, 14, 0, 272, 273, 5, 58, 0, 0, 273, 275, 3, 28, 14, 0, 274, 272, 1, 0, 0, 0, 275, 278, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 280, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 281, 3, 38, 19, 0, 280, 279, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 27, 1, 0, 0, 0, 282, 283, 3, 30, 15, 0, 283, 284, 5, 57, 0, 0, 284, 285, 3, 34, 17, 0, 285, 292, 1, 0, 0, 0, 286, 287, 3, 34, 17, 0, 287, 288, 5, 56, 0, 0, 288, 289, 3, 32, 16, 0, 289, 292, 1, 0, 0, 0, 290, 292, 3, 36, 18, 0, 291, 282, 1, 0, 0, 0, 291, 286, 1, 0, 0, 0, 291, 290, 1, 0, 0, 0, 292, 29, 1, 0, 0, 0, 293, 294, 5, 103, 0, 0, 294, 31, 1, 0, 0, 0, 295, 296, 5, 103, 0, 0, 296, 33, 1, 0, 0, 0, 297, 298, 5, 103, 0, 0, 298, 35, 1, 0, 0, 0, 299, 300, 7, 0, 0, 0, 300, 37, 1, 0, 0, 0, 301, 302, 5, 102, 0, 0, 302, 307, 5, 103, 0, 0, 303, 304, 5, 58, 0, 0, 304, 306, 5, 103, 0, 0, 305, 303, 1, 0, 0, 0, 306, 309, 1, 0, 0, 0, 307, 305, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 39, 1, 0, 0, 0, 309, 307, 1, 0, 0, 0, 310, 311, 5, 9, 0, 0, 311, 312, 3, 14, 7, 0, 312, 41, 1, 0, 0, 0, 313, 315, 5, 16, 0, 0, 314, 316, 3, 44, 22, 0, 315, 314, 1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 318, 5, 55, 0, 0, 318, 320, 3, 14, 7, 0, 319, 317, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 43, 1, 0, 0, 0, 321, 326, 3, 46, 23, 0, 322, 323, 5, 58, 0, 0, 323, 325, 3, 46, 23, 0, 324, 322, 1, 0, 0, 0, 325, 328, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 45, 1, 0, 0, 0, 328, 326, 1, 0, 0, 0, 329, 332, 3, 16, 8, 0, 330, 331, 5, 17, 0, 0, 331, 333, 3, 132, 66, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 47, 1, 0, 0, 0, 334, 335, 4, 24, 7, 0, 335, 337, 5, 93, 0, 0, 336, 338, 5, 97, 0, 0, 337, 336, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 340, 5, 94, 0, 0, 340, 341, 5, 60, 0, 0, 341, 342, 5, 93, 0, 0, 342, 343, 3, 50, 25, 0, 343, 344, 5, 94, 0, 0, 344, 347, 1, 0, 0, 0, 345, 347, 3, 50, 25, 0, 346, 334, 1, 0, 0, 0, 346, 345, 1, 0, 0, 0, 347, 49, 1, 0, 0, 0, 348, 353, 3, 66, 33, 0, 349, 350, 5, 60, 0, 0, 350, 352, 3, 66, 33, 0, 351, 349, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 51, 1, 0, 0, 0, 355, 353, 1, 0, 0, 0, 356, 357, 4, 26, 8, 0, 357, 359, 5, 93, 0, 0, 358, 360, 5, 124, 0, 0, 359, 358, 1, 0, 0, 0, 359, 360, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 362, 5, 94, 0, 0, 362, 363, 5, 60, 0, 0, 363, 364, 5, 93, 0, 0, 364, 365, 3, 54, 27, 0, 365, 366, 5, 94, 0, 0, 366, 369, 1, 0, 0, 0, 367, 369, 3, 54, 27, 0, 368, 356, 1, 0, 0, 0, 368, 367, 1, 0, 0, 0, 369, 53, 1, 0, 0, 0, 370, 375, 3, 60, 30, 0, 371, 372, 5, 60, 0, 0, 372, 374, 3, 60, 30, 0, 373, 371, 1, 0, 0, 0, 374, 377, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0, 375, 376, 1, 0, 0, 0, 376, 55, 1, 0, 0, 0, 377, 375, 1, 0, 0, 0, 378, 383, 3, 52, 26, 0, 379, 380, 5, 58, 0, 0, 380, 382, 3, 52, 26, 0, 381, 379, 1, 0, 0, 0, 382, 385, 1, 0, 0, 0, 383, 381, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 57, 1, 0, 0, 0, 385, 383, 1, 0, 0, 0, 386, 387, 7, 1, 0, 0, 387, 59, 1, 0, 0, 0, 388, 392, 5, 124, 0, 0, 389, 392, 3, 62, 31, 0, 390, 392, 3, 64, 32, 0, 391, 388, 1, 0, 0, 0, 391, 389, 1, 0, 0, 0, 391, 390, 1, 0, 0, 0, 392, 61, 1, 0, 0, 0, 393, 396, 5, 72, 0, 0, 394, 396, 5, 91, 0, 0, 395, 393, 1, 0, 0, 0, 395, 394, 1, 0, 0, 0, 396, 63, 1, 0, 0, 0, 397, 400, 5, 90, 0, 0, 398, 400, 5, 92, 0, 0, 399, 397, 1, 0, 0, 0, 399, 398, 1, 0, 0, 0, 400, 65, 1, 0, 0, 0, 401, 405, 3, 58, 29, 0, 402, 405, 3, 62, 31, 0, 403, 405, 3, 64, 32, 0, 404, 401, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 404, 403, 1, 0, 0, 0, 405, 67, 1, 0, 0, 0, 406, 407, 5, 11, 0, 0, 407, 408, 3, 154, 77, 0, 408, 69, 1, 0, 0, 0, 409, 410, 5, 15, 0, 0, 410, 415, 3, 72, 36, 0, 411, 412, 5, 58, 0, 0, 412, 414, 3, 72, 36, 0, 413, 411, 1, 0, 0, 0, 414, 417, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 415, 416, 1, 0, 0, 0, 416, 71, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 418, 420, 3, 132, 66, 0, 419, 421, 7, 2, 0, 0, 420, 419, 1, 0, 0, 0, 420, 421, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 423, 5, 69, 0, 0, 423, 425, 7, 3, 0, 0, 424, 422, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 73, 1, 0, 0, 0, 426, 427, 5, 30, 0, 0, 427, 428, 3, 56, 28, 0, 428, 75, 1, 0, 0, 0, 429, 430, 5, 29, 0, 0, 430, 431, 3, 56, 28, 0, 431, 77, 1, 0, 0, 0, 432, 433, 5, 32, 0, 0, 433, 438, 3, 80, 40, 0, 434, 435, 5, 58, 0, 0, 435, 437, 3, 80, 40, 0, 436, 434, 1, 0, 0, 0, 437, 440, 1, 0, 0, 0, 438, 436, 1, 0, 0, 0, 438, 439, 1, 0, 0, 0, 439, 79, 1, 0, 0, 0, 440, 438, 1, 0, 0, 0, 441, 442, 3, 52, 26, 0, 442, 443, 5, 128, 0, 0, 443, 444, 3, 52, 26, 0, 444, 450, 1, 0, 0, 0, 445, 446, 3, 52, 26, 0, 446, 447, 5, 54, 0, 0, 447, 448, 3, 52, 26, 0, 448, 450, 1, 0, 0, 0, 449, 441, 1, 0, 0, 0, 449, 445, 1, 0, 0, 0, 450, 81, 1, 0, 0, 0, 451, 452, 5, 8, 0, 0, 452, 453, 3, 142, 71, 0, 453, 455, 3, 164, 82, 0, 454, 456, 3, 84, 42, 0, 455, 454, 1, 0, 0, 0, 455, 456, 1, 0, 0, 0, 456, 83, 1, 0, 0, 0, 457, 462, 3, 86, 43, 0, 458, 459, 5, 58, 0, 0, 459, 461, 3, 86, 43, 0, 460, 458, 1, 0, 0, 0, 461, 464, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 85, 1, 0, 0, 0, 464, 462, 1, 0, 0, 0, 465, 466, 3, 58, 29, 0, 466, 467, 5, 54, 0, 0, 467, 468, 3, 154, 77, 0, 468, 87, 1, 0, 0, 0, 469, 470, 5, 75, 0, 0, 470, 472, 3, 148, 74, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 89, 1, 0, 0, 0, 473, 474, 5, 10, 0, 0, 474, 475, 3, 142, 71, 0, 475, 476, 3, 164, 82, 0, 476, 91, 1, 0, 0, 0, 477, 478, 5, 28, 0, 0, 478, 479, 3, 48, 24, 0, 479, 93, 1, 0, 0, 0, 480, 481, 5, 6, 0, 0, 481, 482, 3, 96, 48, 0, 482, 95, 1, 0, 0, 0, 483, 484, 5, 95, 0, 0, 484, 485, 3, 2, 1, 0, 485, 486, 5, 96, 0, 0, 486, 97, 1, 0, 0, 0, 487, 488, 5, 33, 0, 0, 488, 489, 5, 132, 0, 0, 489, 99, 1, 0, 0, 0, 490, 491, 5, 5, 0, 0, 491, 494, 3, 102, 51, 0, 492, 493, 5, 70, 0, 0, 493, 495, 3, 52, 26, 0, 494, 492, 1, 0, 0, 0, 494, 495, 1, 0, 0, 0, 495, 505, 1, 0, 0, 0, 496, 497, 5, 75, 0, 0, 497, 502, 3, 104, 52, 0, 498, 499, 5, 58, 0, 0, 499, 501, 3, 104, 52, 0, 500, 498, 1, 0, 0, 0, 501, 504, 1, 0, 0, 0, 502, 500, 1, 0, 0, 0, 502, 503, 1, 0, 0, 0, 503, 506, 1, 0, 0, 0, 504, 502, 1, 0, 0, 0, 505, 496, 1, 0, 0, 0, 505, 506, 1, 0, 0, 0, 506, 101, 1, 0, 0, 0, 507, 508, 7, 4, 0, 0, 508, 103, 1, 0, 0, 0, 509, 510, 3, 52, 26, 0, 510, 511, 5, 54, 0, 0, 511, 513, 1, 0, 0, 0, 512, 509, 1, 0, 0, 0, 512, 513, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 515, 3, 52, 26, 0, 515, 105, 1, 0, 0, 0, 516, 517, 5, 14, 0, 0, 517, 518, 3, 154, 77, 0, 518, 107, 1, 0, 0, 0, 519, 520, 5, 4, 0, 0, 520, 523, 3, 48, 24, 0, 521, 522, 5, 70, 0, 0, 522, 524, 3, 48, 24, 0, 523, 521, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 530, 1, 0, 0, 0, 525, 526, 5, 128, 0, 0, 526, 527, 3, 48, 24, 0, 527, 528, 5, 58, 0, 0, 528, 529, 3, 48, 24, 0, 529, 531, 1, 0, 0, 0, 530, 525, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 109, 1, 0, 0, 0, 532, 533, 5, 21, 0, 0, 533, 534, 3, 112, 56, 0, 534, 111, 1, 0, 0, 0, 535, 537, 3, 114, 57, 0, 536, 535, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 536, 1, 0, 0, 0, 538, 539, 1, 0, 0, 0, 539, 113, 1, 0, 0, 0, 540, 541, 5, 95, 0, 0, 541, 542, 3, 116, 58, 0, 542, 543, 5, 96, 0, 0, 543, 115, 1, 0, 0, 0, 544, 545, 6, 58, -1, 0, 545, 546, 3, 118, 59, 0, 546, 552, 1, 0, 0, 0, 547, 548, 10, 1, 0, 0, 548, 549, 5, 48, 0, 0, 549, 551, 3, 118, 59, 0, 550, 547, 1, 0, 0, 0, 551, 554, 1, 0, 0, 0, 552, 550, 1, 0, 0, 0, 552, 553, 1, 0, 0, 0, 553, 117, 1, 0, 0, 0, 554, 552, 1, 0, 0, 0, 555, 556, 3, 6, 3, 0, 556, 119, 1, 0, 0, 0, 557, 561, 5, 12, 0, 0, 558, 559, 3, 48, 24, 0, 559, 560, 5, 54, 0, 0, 560, 562, 1, 0, 0, 0, 561, 558, 1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 3, 154, 77, 0, 564, 565, 5, 70, 0, 0, 565, 566, 3, 18, 9, 0, 566, 567, 3, 88, 44, 0, 567, 121, 1, 0, 0, 0, 568, 572, 5, 7, 0, 0, 569, 570, 3, 48, 24, 0, 570, 571, 5, 54, 0, 0, 571, 573, 1, 0, 0, 0, 572, 569, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 3, 142, 71, 0, 575, 576, 3, 88, 44, 0, 576, 123, 1, 0, 0, 0, 577, 578, 5, 27, 0, 0, 578, 579, 3, 28, 14, 0, 579, 580, 5, 70, 0, 0, 580, 581, 3, 56, 28, 0, 581, 125, 1, 0, 0, 0, 582, 583, 5, 18, 0, 0, 583, 586, 3, 44, 22, 0, 584, 585, 5, 55, 0, 0, 585, 587, 3, 14, 7, 0, 586, 584, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 127, 1, 0, 0, 0, 588, 589, 5, 31, 0, 0, 589, 590, 3, 56, 28, 0, 590, 129, 1, 0, 0, 0, 591, 592, 5, 22, 0, 0, 592, 131, 1, 0, 0, 0, 593, 594, 6, 66, -1, 0, 594, 595, 5, 67, 0, 0, 595, 623, 3, 132, 66, 8, 596, 623, 3, 138, 69, 0, 597, 623, 3, 134, 67, 0, 598, 600, 3, 138, 69, 0, 599, 601, 5, 67, 0, 0, 600, 599, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 5, 63, 0, 0, 603, 604, 5, 95, 0, 0, 604, 609, 3, 138, 69, 0, 605, 606, 5, 58, 0, 0, 606, 608, 3, 138, 69, 0, 607, 605, 1, 0, 0, 0, 608, 611, 1, 0, 0, 0, 609, 607, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 610, 612, 1, 0, 0, 0, 611, 609, 1, 0, 0, 0, 612, 613, 5, 96, 0, 0, 613, 623, 1, 0, 0, 0, 614, 615, 3, 138, 69, 0, 615, 617, 5, 64, 0, 0, 616, 618, 5, 67, 0, 0, 617, 616, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 5, 68, 0, 0, 620, 623, 1, 0, 0, 0, 621, 623, 3, 136, 68, 0, 622, 593, 1, 0, 0, 0, 622, 596, 1, 0, 0, 0, 622, 597, 1, 0, 0, 0, 622, 598, 1, 0, 0, 0, 622, 614, 1, 0, 0, 0, 622, 621, 1, 0, 0, 0, 623, 632, 1, 0, 0, 0, 624, 625, 10, 5, 0, 0, 625, 626, 5, 52, 0, 0, 626, 631, 3, 132, 66, 6, 627, 628, 10, 4, 0, 0, 628, 629, 5, 71, 0, 0, 629, 631, 3, 132, 66, 5, 630, 624, 1, 0, 0, 0, 630, 627, 1, 0, 0, 0, 631, 634, 1, 0, 0, 0, 632, 630, 1, 0, 0, 0, 632, 633, 1, 0, 0, 0, 633, 133, 1, 0, 0, 0, 634, 632, 1, 0, 0, 0, 635, 637, 3, 138, 69, 0, 636, 638, 5, 67, 0, 0, 637, 636, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 639, 1, 0, 0, 0, 639, 640, 5, 66, 0, 0, 640, 641, 3, 164, 82, 0, 641, 682, 1, 0, 0, 0, 642, 644, 3, 138, 69, 0, 643, 645, 5, 67, 0, 0, 644, 643, 1, 0, 0, 0, 644, 645, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 647, 5, 73, 0, 0, 647, 648, 3, 164, 82, 0, 648, 682, 1, 0, 0, 0, 649, 651, 3, 138, 69, 0, 650, 652, 5, 67, 0, 0, 651, 650, 1, 0, 0, 0, 651, 652, 1, 0, 0, 0, 652, 653, 1, 0, 0, 0, 653, 654, 5, 66, 0, 0, 654, 655, 5, 95, 0, 0, 655, 660, 3, 164, 82, 0, 656, 657, 5, 58, 0, 0, 657, 659, 3, 164, 82, 0, 658, 656, 1, 0, 0, 0, 659, 662, 1, 0, 0, 0, 660, 658, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 663, 1, 0, 0, 0, 662, 660, 1, 0, 0, 0, 663, 664, 5, 96, 0, 0, 664, 682, 1, 0, 0, 0, 665, 667, 3, 138, 69, 0, 666, 668, 5, 67, 0, 0, 667, 666, 1, 0, 0, 0, 667, 668, 1, 0, 0, 0, 668, 669, 1, 0, 0, 0, 669, 670, 5, 73, 0, 0, 670, 671, 5, 95, 0, 0, 671, 676, 3, 164, 82, 0, 672, 673, 5, 58, 0, 0, 673, 675, 3, 164, 82, 0, 674, 672, 1, 0, 0, 0, 675, 678, 1, 0, 0, 0, 676, 674, 1, 0, 0, 0, 676, 677, 1, 0, 0, 0, 677, 679, 1, 0, 0, 0, 678, 676, 1, 0, 0, 0, 679, 680, 5, 96, 0, 0, 680, 682, 1, 0, 0, 0, 681, 635, 1, 0, 0, 0, 681, 642, 1, 0, 0, 0, 681, 649, 1, 0, 0, 0, 681, 665, 1, 0, 0, 0, 682, 135, 1, 0, 0, 0, 683, 686, 3, 48, 24, 0, 684, 685, 5, 56, 0, 0, 685, 687, 3, 10, 5, 0, 686, 684, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 689, 5, 57, 0, 0, 689, 690, 3, 154, 77, 0, 690, 137, 1, 0, 0, 0, 691, 697, 3, 140, 70, 0, 692, 693, 3, 140, 70, 0, 693, 694, 3, 166, 83, 0, 694, 695, 3, 140, 70, 0, 695, 697, 1, 0, 0, 0, 696, 691, 1, 0, 0, 0, 696, 692, 1, 0, 0, 0, 697, 139, 1, 0, 0, 0, 698, 699, 6, 70, -1, 0, 699, 703, 3, 142, 71, 0, 700, 701, 7, 5, 0, 0, 701, 703, 3, 140, 70, 3, 702, 698, 1, 0, 0, 0, 702, 700, 1, 0, 0, 0, 703, 712, 1, 0, 0, 0, 704, 705, 10, 2, 0, 0, 705, 706, 7, 6, 0, 0, 706, 711, 3, 140, 70, 3, 707, 708, 10, 1, 0, 0, 708, 709, 7, 5, 0, 0, 709, 711, 3, 140, 70, 2, 710, 704, 1, 0, 0, 0, 710, 707, 1, 0, 0, 0, 711, 714, 1, 0, 0, 0, 712, 710, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 141, 1, 0, 0, 0, 714, 712, 1, 0, 0, 0, 715, 716, 6, 71, -1, 0, 716, 724, 3, 154, 77, 0, 717, 724, 3, 48, 24, 0, 718, 724, 3, 144, 72, 0, 719, 720, 5, 95, 0, 0, 720, 721, 3, 132, 66, 0, 721, 722, 5, 96, 0, 0, 722, 724, 1, 0, 0, 0, 723, 715, 1, 0, 0, 0, 723, 717, 1, 0, 0, 0, 723, 718, 1, 0, 0, 0, 723, 719, 1, 0, 0, 0, 724, 730, 1, 0, 0, 0, 725, 726, 10, 1, 0, 0, 726, 727, 5, 56, 0, 0, 727, 729, 3, 10, 5, 0, 728, 725, 1, 0, 0, 0, 729, 732, 1, 0, 0, 0, 730, 728, 1, 0, 0, 0, 730, 731, 1, 0, 0, 0, 731, 143, 1, 0, 0, 0, 732, 730, 1, 0, 0, 0, 733, 734, 3, 146, 73, 0, 734, 748, 5, 95, 0, 0, 735, 749, 5, 85, 0, 0, 736, 741, 3, 132, 66, 0, 737, 738, 5, 58, 0, 0, 738, 740, 3, 132, 66, 0, 739, 737, 1, 0, 0, 0, 740, 743, 1, 0, 0, 0, 741, 739, 1, 0, 0, 0, 741, 742, 1, 0, 0, 0, 742, 746, 1, 0, 0, 0, 743, 741, 1, 0, 0, 0, 744, 745, 5, 58, 0, 0, 745, 747, 3, 148, 74, 0, 746, 744, 1, 0, 0, 0, 746, 747, 1, 0, 0, 0, 747, 749, 1, 0, 0, 0, 748, 735, 1, 0, 0, 0, 748, 736, 1, 0, 0, 0, 748, 749, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 751, 5, 96, 0, 0, 751, 145, 1, 0, 0, 0, 752, 756, 3, 66, 33, 0, 753, 756, 5, 62, 0, 0, 754, 756, 5, 65, 0, 0, 755, 752, 1, 0, 0, 0, 755, 753, 1, 0, 0, 0, 755, 754, 1, 0, 0, 0, 756, 147, 1, 0, 0, 0, 757, 766, 5, 88, 0, 0, 758, 763, 3, 150, 75, 0, 759, 760, 5, 58, 0, 0, 760, 762, 3, 150, 75, 0, 761, 759, 1, 0, 0, 0, 762, 765, 1, 0, 0, 0, 763, 761, 1, 0, 0, 0, 763, 764, 1, 0, 0, 0, 764, 767, 1, 0, 0, 0, 765, 763, 1, 0, 0, 0, 766, 758, 1, 0, 0, 0, 766, 767, 1, 0, 0, 0, 767, 768, 1, 0, 0, 0, 768, 769, 5, 89, 0, 0, 769, 149, 1, 0, 0, 0, 770, 771, 3, 164, 82, 0, 771, 772, 5, 57, 0, 0, 772, 773, 3, 152, 76, 0, 773, 151, 1, 0, 0, 0, 774, 777, 3, 154, 77, 0, 775, 777, 3, 148, 74, 0, 776, 774, 1, 0, 0, 0, 776, 775, 1, 0, 0, 0, 777, 153, 1, 0, 0, 0, 778, 821, 5, 68, 0, 0, 779, 780, 3, 162, 81, 0, 780, 781, 5, 97, 0, 0, 781, 821, 1, 0, 0, 0, 782, 821, 3, 160, 80, 0, 783, 821, 3, 162, 81, 0, 784, 821, 3, 156, 78, 0, 785, 821, 3, 62, 31, 0, 786, 821, 3, 164, 82, 0, 787, 788, 5, 93, 0, 0, 788, 793, 3, 158, 79, 0, 789, 790, 5, 58, 0, 0, 790, 792, 3, 158, 79, 0, 791, 789, 1, 0, 0, 0, 792, 795, 1, 0, 0, 0, 793, 791, 1, 0, 0, 0, 793, 794, 1, 0, 0, 0, 794, 796, 1, 0, 0, 0, 795, 793, 1, 0, 0, 0, 796, 797, 5, 94, 0, 0, 797, 821, 1, 0, 0, 0, 798, 799, 5, 93, 0, 0, 799, 804, 3, 156, 78, 0, 800, 801, 5, 58, 0, 0, 801, 803, 3, 156, 78, 0, 802, 800, 1, 0, 0, 0, 803, 806, 1, 0, 0, 0, 804, 802, 1, 0, 0, 0, 804, 805, 1, 0, 0, 0, 805, 807, 1, 0, 0, 0, 806, 804, 1, 0, 0, 0, 807, 808, 5, 94, 0, 0, 808, 821, 1, 0, 0, 0, 809, 810, 5, 93, 0, 0, 810, 815, 3, 164, 82, 0, 811, 812, 5, 58, 0, 0, 812, 814, 3, 164, 82, 0, 813, 811, 1, 0, 0, 0, 814, 817, 1, 0, 0, 0, 815, 813, 1, 0, 0, 0, 815, 816, 1, 0, 0, 0, 816, 818, 1, 0, 0, 0, 817, 815, 1, 0, 0, 0, 818, 819, 5, 94, 0, 0, 819, 821, 1, 0, 0, 0, 820, 778, 1, 0, 0, 0, 820, 779, 1, 0, 0, 0, 820, 782, 1, 0, 0, 0, 820, 783, 1, 0, 0, 0, 820, 784, 1, 0, 0, 0, 820, 785, 1, 0, 0, 0, 820, 786, 1, 0, 0, 0, 820, 787, 1, 0, 0, 0, 820, 798, 1, 0, 0, 0, 820, 809, 1, 0, 0, 0, 821, 155, 1, 0, 0, 0, 822, 823, 7, 7, 0, 0, 823, 157, 1, 0, 0, 0, 824, 827, 3, 160, 80, 0, 825, 827, 3, 162, 81, 0, 826, 824, 1, 0, 0, 0, 826, 825, 1, 0, 0, 0, 827, 159, 1, 0, 0, 0, 828, 830, 7, 5, 0, 0, 829, 828, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 831, 1, 0, 0, 0, 831, 832, 5, 51, 0, 0, 832, 161, 1, 0, 0, 0, 833, 835, 7, 5, 0, 0, 834, 833, 1, 0, 0, 0, 834, 835, 1, 0, 0, 0, 835, 836, 1, 0, 0, 0, 836, 837, 5, 50, 0, 0, 837, 163, 1, 0, 0, 0, 838, 839, 5, 49, 0, 0, 839, 165, 1, 0, 0, 0, 840, 841, 7, 8, 0, 0, 841, 167, 1, 0, 0, 0, 842, 843, 7, 9, 0, 0, 843, 844, 5, 110, 0, 0, 844, 845, 3, 170, 85, 0, 845, 846, 3, 172, 86, 0, 846, 169, 1, 0, 0, 0, 847, 848, 4, 85, 15, 0, 848, 850, 3, 28, 14, 0, 849, 851, 5, 128, 0, 0, 850, 849, 1, 0, 0, 0, 850, 851, 1, 0, 0, 0, 851, 852, 1, 0, 0, 0, 852, 853, 5, 103, 0, 0, 853, 856, 1, 0, 0, 0, 854, 856, 3, 28, 14, 0, 855, 847, 1, 0, 0, 0, 855, 854, 1, 0, 0, 0, 856, 171, 1, 0, 0, 0, 857, 858, 5, 70, 0, 0, 858, 863, 3, 48, 24, 0, 859, 860, 5, 58, 0, 0, 860, 862, 3, 48, 24, 0, 861, 859, 1, 0, 0, 0, 862, 865, 1, 0, 0, 0, 863, 861, 1, 0, 0, 0, 863, 864, 1, 0, 0, 0, 864, 876, 1, 0, 0, 0, 865, 863, 1, 0, 0, 0, 866, 867, 5, 70, 0, 0, 867, 872, 3, 176, 88, 0, 868, 869, 5, 52, 0, 0, 869, 871, 3, 176, 88, 0, 870, 868, 1, 0, 0, 0, 871, 874, 1, 0, 0, 0, 872, 870, 1, 0, 0, 0, 872, 873, 1, 0, 0, 0, 873, 876, 1, 0, 0, 0, 874, 872, 1, 0, 0, 0, 875, 857, 1, 0, 0, 0, 875, 866, 1, 0, 0, 0, 876, 173, 1, 0, 0, 0, 877, 878, 3, 138, 69, 0, 878, 175, 1, 0, 0, 0, 879, 880, 3, 140, 70, 0, 880, 881, 3, 166, 83, 0, 881, 882, 3, 140, 70, 0, 882, 177, 1, 0, 0, 0, 84, 189, 199, 227, 242, 248, 257, 263, 276, 280, 291, 307, 315, 319, 326, 332, 337, 346, 353, 359, 368, 375, 383, 391, 395, 399, 404, 415, 420, 424, 438, 449, 455, 462, 471, 494, 502, 505, 512, 523, 530, 538, 552, 561, 572, 586, 600, 609, 617, 622, 630, 632, 637, 644, 651, 660, 667, 676, 681, 686, 696, 702, 710, 712, 723, 730, 741, 746, 748, 755, 763, 766, 776, 793, 804, 815, 820, 826, 829, 834, 850, 855, 863, 872, 875]
\ No newline at end of file
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java
index 686b35f728cdd..6604a63ffe231 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser.java
@@ -75,12 +75,13 @@ public class EsqlBaseParser extends ParserConfig {
RULE_rerankCommand = 60, RULE_completionCommand = 61, RULE_lookupCommand = 62,
RULE_inlinestatsCommand = 63, RULE_insistCommand = 64, RULE_fuseCommand = 65,
RULE_booleanExpression = 66, RULE_regexBooleanExpression = 67, RULE_matchBooleanExpression = 68,
- RULE_valueExpression = 69, RULE_comparisonExpression = 70, RULE_operatorExpression = 71,
- RULE_primaryExpression = 72, RULE_functionExpression = 73, RULE_functionName = 74,
- RULE_mapExpression = 75, RULE_entryExpression = 76, RULE_mapValue = 77,
- RULE_constant = 78, RULE_booleanValue = 79, RULE_numericValue = 80, RULE_decimalValue = 81,
- RULE_integerValue = 82, RULE_string = 83, RULE_comparisonOperator = 84,
- RULE_joinCommand = 85, RULE_joinTarget = 86, RULE_joinCondition = 87;
+ RULE_valueExpression = 69, RULE_operatorExpression = 70, RULE_primaryExpression = 71,
+ RULE_functionExpression = 72, RULE_functionName = 73, RULE_mapExpression = 74,
+ RULE_entryExpression = 75, RULE_mapValue = 76, RULE_constant = 77, RULE_booleanValue = 78,
+ RULE_numericValue = 79, RULE_decimalValue = 80, RULE_integerValue = 81,
+ RULE_string = 82, RULE_comparisonOperator = 83, RULE_joinCommand = 84,
+ RULE_joinTarget = 85, RULE_joinCondition = 86, RULE_joinPredicate = 87,
+ RULE_joinPredicateExpression = 88;
private static String[] makeRuleNames() {
return new String[] {
"singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand",
@@ -99,11 +100,11 @@ private static String[] makeRuleNames() {
"forkSubQuery", "forkSubQueryCommand", "forkSubQueryProcessingCommand",
"rerankCommand", "completionCommand", "lookupCommand", "inlinestatsCommand",
"insistCommand", "fuseCommand", "booleanExpression", "regexBooleanExpression",
- "matchBooleanExpression", "valueExpression", "comparisonExpression",
- "operatorExpression", "primaryExpression", "functionExpression", "functionName",
- "mapExpression", "entryExpression", "mapValue", "constant", "booleanValue",
- "numericValue", "decimalValue", "integerValue", "string", "comparisonOperator",
- "joinCommand", "joinTarget", "joinCondition"
+ "matchBooleanExpression", "valueExpression", "operatorExpression", "primaryExpression",
+ "functionExpression", "functionName", "mapExpression", "entryExpression",
+ "mapValue", "constant", "booleanValue", "numericValue", "decimalValue",
+ "integerValue", "string", "comparisonOperator", "joinCommand", "joinTarget",
+ "joinCondition", "joinPredicate", "joinPredicateExpression"
};
}
public static final String[] ruleNames = makeRuleNames();
@@ -242,9 +243,9 @@ public final SingleStatementContext singleStatement() throws RecognitionExceptio
try {
enterOuterAlt(_localctx, 1);
{
- setState(176);
+ setState(178);
query(0);
- setState(177);
+ setState(179);
match(EOF);
}
}
@@ -340,11 +341,11 @@ private QueryContext query(int _p) throws RecognitionException {
_ctx = _localctx;
_prevctx = _localctx;
- setState(180);
+ setState(182);
sourceCommand();
}
_ctx.stop = _input.LT(-1);
- setState(187);
+ setState(189);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,0,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -355,16 +356,16 @@ private QueryContext query(int _p) throws RecognitionException {
{
_localctx = new CompositeQueryContext(new QueryContext(_parentctx, _parentState));
pushNewRecursionContext(_localctx, _startState, RULE_query);
- setState(182);
+ setState(184);
if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
- setState(183);
+ setState(185);
match(PIPE);
- setState(184);
+ setState(186);
processingCommand();
}
}
}
- setState(189);
+ setState(191);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,0,_ctx);
}
@@ -422,45 +423,45 @@ public final SourceCommandContext sourceCommand() throws RecognitionException {
SourceCommandContext _localctx = new SourceCommandContext(_ctx, getState());
enterRule(_localctx, 4, RULE_sourceCommand);
try {
- setState(197);
+ setState(199);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,1,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(190);
+ setState(192);
fromCommand();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(191);
+ setState(193);
rowCommand();
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
- setState(192);
+ setState(194);
showCommand();
}
break;
case 4:
enterOuterAlt(_localctx, 4);
{
- setState(193);
+ setState(195);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(194);
+ setState(196);
timeSeriesCommand();
}
break;
case 5:
enterOuterAlt(_localctx, 5);
{
- setState(195);
+ setState(197);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(196);
+ setState(198);
explainCommand();
}
break;
@@ -569,168 +570,168 @@ public final ProcessingCommandContext processingCommand() throws RecognitionExce
ProcessingCommandContext _localctx = new ProcessingCommandContext(_ctx, getState());
enterRule(_localctx, 6, RULE_processingCommand);
try {
- setState(225);
+ setState(227);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(199);
+ setState(201);
evalCommand();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(200);
+ setState(202);
whereCommand();
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
- setState(201);
+ setState(203);
keepCommand();
}
break;
case 4:
enterOuterAlt(_localctx, 4);
{
- setState(202);
+ setState(204);
limitCommand();
}
break;
case 5:
enterOuterAlt(_localctx, 5);
{
- setState(203);
+ setState(205);
statsCommand();
}
break;
case 6:
enterOuterAlt(_localctx, 6);
{
- setState(204);
+ setState(206);
sortCommand();
}
break;
case 7:
enterOuterAlt(_localctx, 7);
{
- setState(205);
+ setState(207);
dropCommand();
}
break;
case 8:
enterOuterAlt(_localctx, 8);
{
- setState(206);
+ setState(208);
renameCommand();
}
break;
case 9:
enterOuterAlt(_localctx, 9);
{
- setState(207);
+ setState(209);
dissectCommand();
}
break;
case 10:
enterOuterAlt(_localctx, 10);
{
- setState(208);
+ setState(210);
grokCommand();
}
break;
case 11:
enterOuterAlt(_localctx, 11);
{
- setState(209);
+ setState(211);
enrichCommand();
}
break;
case 12:
enterOuterAlt(_localctx, 12);
{
- setState(210);
+ setState(212);
mvExpandCommand();
}
break;
case 13:
enterOuterAlt(_localctx, 13);
{
- setState(211);
+ setState(213);
joinCommand();
}
break;
case 14:
enterOuterAlt(_localctx, 14);
{
- setState(212);
+ setState(214);
changePointCommand();
}
break;
case 15:
enterOuterAlt(_localctx, 15);
{
- setState(213);
+ setState(215);
completionCommand();
}
break;
case 16:
enterOuterAlt(_localctx, 16);
{
- setState(214);
+ setState(216);
sampleCommand();
}
break;
case 17:
enterOuterAlt(_localctx, 17);
{
- setState(215);
+ setState(217);
forkCommand();
}
break;
case 18:
enterOuterAlt(_localctx, 18);
{
- setState(216);
+ setState(218);
rerankCommand();
}
break;
case 19:
enterOuterAlt(_localctx, 19);
{
- setState(217);
+ setState(219);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(218);
+ setState(220);
inlinestatsCommand();
}
break;
case 20:
enterOuterAlt(_localctx, 20);
{
- setState(219);
+ setState(221);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(220);
+ setState(222);
lookupCommand();
}
break;
case 21:
enterOuterAlt(_localctx, 21);
{
- setState(221);
+ setState(223);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(222);
+ setState(224);
insistCommand();
}
break;
case 22:
enterOuterAlt(_localctx, 22);
{
- setState(223);
+ setState(225);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(224);
+ setState(226);
fuseCommand();
}
break;
@@ -779,9 +780,9 @@ public final WhereCommandContext whereCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(227);
+ setState(229);
match(WHERE);
- setState(228);
+ setState(230);
booleanExpression(0);
}
}
@@ -839,7 +840,7 @@ public final DataTypeContext dataType() throws RecognitionException {
_localctx = new ToDataTypeContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(230);
+ setState(232);
identifier();
}
}
@@ -886,9 +887,9 @@ public final RowCommandContext rowCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(232);
+ setState(234);
match(ROW);
- setState(233);
+ setState(235);
fields();
}
}
@@ -942,23 +943,23 @@ public final FieldsContext fields() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(235);
+ setState(237);
field();
- setState(240);
+ setState(242);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,3,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(236);
+ setState(238);
match(COMMA);
- setState(237);
+ setState(239);
field();
}
}
}
- setState(242);
+ setState(244);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,3,_ctx);
}
@@ -1010,19 +1011,19 @@ public final FieldContext field() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(246);
+ setState(248);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) {
case 1:
{
- setState(243);
+ setState(245);
qualifiedName();
- setState(244);
+ setState(246);
match(ASSIGN);
}
break;
}
- setState(248);
+ setState(250);
booleanExpression(0);
}
}
@@ -1076,23 +1077,23 @@ public final RerankFieldsContext rerankFields() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(250);
+ setState(252);
rerankField();
- setState(255);
+ setState(257);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,5,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(251);
+ setState(253);
match(COMMA);
- setState(252);
+ setState(254);
rerankField();
}
}
}
- setState(257);
+ setState(259);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,5,_ctx);
}
@@ -1144,16 +1145,16 @@ public final RerankFieldContext rerankField() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(258);
+ setState(260);
qualifiedName();
- setState(261);
+ setState(263);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) {
case 1:
{
- setState(259);
+ setState(261);
match(ASSIGN);
- setState(260);
+ setState(262);
booleanExpression(0);
}
break;
@@ -1203,9 +1204,9 @@ public final FromCommandContext fromCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(263);
+ setState(265);
match(FROM);
- setState(264);
+ setState(266);
indexPatternAndMetadataFields();
}
}
@@ -1252,9 +1253,9 @@ public final TimeSeriesCommandContext timeSeriesCommand() throws RecognitionExce
try {
enterOuterAlt(_localctx, 1);
{
- setState(266);
+ setState(268);
match(DEV_TIME_SERIES);
- setState(267);
+ setState(269);
indexPatternAndMetadataFields();
}
}
@@ -1311,32 +1312,32 @@ public final IndexPatternAndMetadataFieldsContext indexPatternAndMetadataFields(
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(269);
+ setState(271);
indexPattern();
- setState(274);
+ setState(276);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,7,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(270);
+ setState(272);
match(COMMA);
- setState(271);
+ setState(273);
indexPattern();
}
}
}
- setState(276);
+ setState(278);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,7,_ctx);
}
- setState(278);
+ setState(280);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,8,_ctx) ) {
case 1:
{
- setState(277);
+ setState(279);
metadata();
}
break;
@@ -1394,35 +1395,35 @@ public final IndexPatternContext indexPattern() throws RecognitionException {
IndexPatternContext _localctx = new IndexPatternContext(_ctx, getState());
enterRule(_localctx, 28, RULE_indexPattern);
try {
- setState(289);
+ setState(291);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,9,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(280);
+ setState(282);
clusterString();
- setState(281);
+ setState(283);
match(COLON);
- setState(282);
+ setState(284);
unquotedIndexString();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(284);
+ setState(286);
unquotedIndexString();
- setState(285);
+ setState(287);
match(CAST_OP);
- setState(286);
+ setState(288);
selectorString();
}
break;
case 3:
enterOuterAlt(_localctx, 3);
{
- setState(288);
+ setState(290);
indexString();
}
break;
@@ -1468,7 +1469,7 @@ public final ClusterStringContext clusterString() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(291);
+ setState(293);
match(UNQUOTED_SOURCE);
}
}
@@ -1512,7 +1513,7 @@ public final SelectorStringContext selectorString() throws RecognitionException
try {
enterOuterAlt(_localctx, 1);
{
- setState(293);
+ setState(295);
match(UNQUOTED_SOURCE);
}
}
@@ -1556,7 +1557,7 @@ public final UnquotedIndexStringContext unquotedIndexString() throws Recognition
try {
enterOuterAlt(_localctx, 1);
{
- setState(295);
+ setState(297);
match(UNQUOTED_SOURCE);
}
}
@@ -1602,7 +1603,7 @@ public final IndexStringContext indexString() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(297);
+ setState(299);
_la = _input.LA(1);
if ( !(_la==QUOTED_STRING || _la==UNQUOTED_SOURCE) ) {
_errHandler.recoverInline(this);
@@ -1663,25 +1664,25 @@ public final MetadataContext metadata() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(299);
+ setState(301);
match(METADATA);
- setState(300);
+ setState(302);
match(UNQUOTED_SOURCE);
- setState(305);
+ setState(307);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,10,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(301);
+ setState(303);
match(COMMA);
- setState(302);
+ setState(304);
match(UNQUOTED_SOURCE);
}
}
}
- setState(307);
+ setState(309);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,10,_ctx);
}
@@ -1730,9 +1731,9 @@ public final EvalCommandContext evalCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(308);
+ setState(310);
match(EVAL);
- setState(309);
+ setState(311);
fields();
}
}
@@ -1785,26 +1786,26 @@ public final StatsCommandContext statsCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(311);
- match(STATS);
setState(313);
+ match(STATS);
+ setState(315);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,11,_ctx) ) {
case 1:
{
- setState(312);
+ setState(314);
((StatsCommandContext)_localctx).stats = aggFields();
}
break;
}
- setState(317);
+ setState(319);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,12,_ctx) ) {
case 1:
{
- setState(315);
+ setState(317);
match(BY);
- setState(316);
+ setState(318);
((StatsCommandContext)_localctx).grouping = fields();
}
break;
@@ -1861,23 +1862,23 @@ public final AggFieldsContext aggFields() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(319);
+ setState(321);
aggField();
- setState(324);
+ setState(326);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,13,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(320);
+ setState(322);
match(COMMA);
- setState(321);
+ setState(323);
aggField();
}
}
}
- setState(326);
+ setState(328);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,13,_ctx);
}
@@ -1929,16 +1930,16 @@ public final AggFieldContext aggField() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(327);
+ setState(329);
field();
- setState(330);
+ setState(332);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,14,_ctx) ) {
case 1:
{
- setState(328);
+ setState(330);
match(WHERE);
- setState(329);
+ setState(331);
booleanExpression(0);
}
break;
@@ -1998,42 +1999,42 @@ public final QualifiedNameContext qualifiedName() throws RecognitionException {
enterRule(_localctx, 48, RULE_qualifiedName);
int _la;
try {
- setState(344);
+ setState(346);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,16,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(332);
+ setState(334);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(333);
- match(OPENING_BRACKET);
setState(335);
+ match(OPENING_BRACKET);
+ setState(337);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==UNQUOTED_IDENTIFIER) {
{
- setState(334);
+ setState(336);
((QualifiedNameContext)_localctx).qualifier = match(UNQUOTED_IDENTIFIER);
}
}
- setState(337);
+ setState(339);
match(CLOSING_BRACKET);
- setState(338);
+ setState(340);
match(DOT);
- setState(339);
+ setState(341);
match(OPENING_BRACKET);
- setState(340);
+ setState(342);
((QualifiedNameContext)_localctx).name = fieldName();
- setState(341);
+ setState(343);
match(CLOSING_BRACKET);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(343);
+ setState(345);
((QualifiedNameContext)_localctx).name = fieldName();
}
break;
@@ -2089,23 +2090,23 @@ public final FieldNameContext fieldName() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(346);
+ setState(348);
identifierOrParameter();
- setState(351);
+ setState(353);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,17,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(347);
+ setState(349);
match(DOT);
- setState(348);
+ setState(350);
identifierOrParameter();
}
}
}
- setState(353);
+ setState(355);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,17,_ctx);
}
@@ -2164,42 +2165,42 @@ public final QualifiedNamePatternContext qualifiedNamePattern() throws Recogniti
enterRule(_localctx, 52, RULE_qualifiedNamePattern);
int _la;
try {
- setState(366);
+ setState(368);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,19,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(354);
+ setState(356);
if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
- setState(355);
- match(OPENING_BRACKET);
setState(357);
+ match(OPENING_BRACKET);
+ setState(359);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==ID_PATTERN) {
{
- setState(356);
+ setState(358);
((QualifiedNamePatternContext)_localctx).qualifier = match(ID_PATTERN);
}
}
- setState(359);
+ setState(361);
match(CLOSING_BRACKET);
- setState(360);
+ setState(362);
match(DOT);
- setState(361);
+ setState(363);
match(OPENING_BRACKET);
- setState(362);
+ setState(364);
((QualifiedNamePatternContext)_localctx).name = fieldNamePattern();
- setState(363);
+ setState(365);
match(CLOSING_BRACKET);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(365);
+ setState(367);
((QualifiedNamePatternContext)_localctx).name = fieldNamePattern();
}
break;
@@ -2256,23 +2257,23 @@ public final FieldNamePatternContext fieldNamePattern() throws RecognitionExcept
enterOuterAlt(_localctx, 1);
{
{
- setState(368);
+ setState(370);
identifierPattern();
- setState(373);
+ setState(375);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,20,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(369);
+ setState(371);
match(DOT);
- setState(370);
+ setState(372);
identifierPattern();
}
}
}
- setState(375);
+ setState(377);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,20,_ctx);
}
@@ -2329,23 +2330,23 @@ public final QualifiedNamePatternsContext qualifiedNamePatterns() throws Recogni
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(376);
+ setState(378);
qualifiedNamePattern();
- setState(381);
+ setState(383);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,21,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(377);
+ setState(379);
match(COMMA);
- setState(378);
+ setState(380);
qualifiedNamePattern();
}
}
}
- setState(383);
+ setState(385);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,21,_ctx);
}
@@ -2393,7 +2394,7 @@ public final IdentifierContext identifier() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(384);
+ setState(386);
_la = _input.LA(1);
if ( !(_la==UNQUOTED_IDENTIFIER || _la==QUOTED_IDENTIFIER) ) {
_errHandler.recoverInline(this);
@@ -2449,13 +2450,13 @@ public final IdentifierPatternContext identifierPattern() throws RecognitionExce
IdentifierPatternContext _localctx = new IdentifierPatternContext(_ctx, getState());
enterRule(_localctx, 60, RULE_identifierPattern);
try {
- setState(389);
+ setState(391);
_errHandler.sync(this);
switch (_input.LA(1)) {
case ID_PATTERN:
enterOuterAlt(_localctx, 1);
{
- setState(386);
+ setState(388);
match(ID_PATTERN);
}
break;
@@ -2463,7 +2464,7 @@ public final IdentifierPatternContext identifierPattern() throws RecognitionExce
case NAMED_OR_POSITIONAL_PARAM:
enterOuterAlt(_localctx, 2);
{
- setState(387);
+ setState(389);
parameter();
}
break;
@@ -2471,7 +2472,7 @@ public final IdentifierPatternContext identifierPattern() throws RecognitionExce
case NAMED_OR_POSITIONAL_DOUBLE_PARAMS:
enterOuterAlt(_localctx, 3);
{
- setState(388);
+ setState(390);
doubleParameter();
}
break;
@@ -2547,14 +2548,14 @@ public final ParameterContext parameter() throws RecognitionException {
ParameterContext _localctx = new ParameterContext(_ctx, getState());
enterRule(_localctx, 62, RULE_parameter);
try {
- setState(393);
+ setState(395);
_errHandler.sync(this);
switch (_input.LA(1)) {
case PARAM:
_localctx = new InputParamContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(391);
+ setState(393);
match(PARAM);
}
break;
@@ -2562,7 +2563,7 @@ public final ParameterContext parameter() throws RecognitionException {
_localctx = new InputNamedOrPositionalParamContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(392);
+ setState(394);
match(NAMED_OR_POSITIONAL_PARAM);
}
break;
@@ -2638,14 +2639,14 @@ public final DoubleParameterContext doubleParameter() throws RecognitionExceptio
DoubleParameterContext _localctx = new DoubleParameterContext(_ctx, getState());
enterRule(_localctx, 64, RULE_doubleParameter);
try {
- setState(397);
+ setState(399);
_errHandler.sync(this);
switch (_input.LA(1)) {
case DOUBLE_PARAMS:
_localctx = new InputDoubleParamsContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(395);
+ setState(397);
match(DOUBLE_PARAMS);
}
break;
@@ -2653,7 +2654,7 @@ public final DoubleParameterContext doubleParameter() throws RecognitionExceptio
_localctx = new InputNamedOrPositionalDoubleParamsContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(396);
+ setState(398);
match(NAMED_OR_POSITIONAL_DOUBLE_PARAMS);
}
break;
@@ -2707,14 +2708,14 @@ public final IdentifierOrParameterContext identifierOrParameter() throws Recogni
IdentifierOrParameterContext _localctx = new IdentifierOrParameterContext(_ctx, getState());
enterRule(_localctx, 66, RULE_identifierOrParameter);
try {
- setState(402);
+ setState(404);
_errHandler.sync(this);
switch (_input.LA(1)) {
case UNQUOTED_IDENTIFIER:
case QUOTED_IDENTIFIER:
enterOuterAlt(_localctx, 1);
{
- setState(399);
+ setState(401);
identifier();
}
break;
@@ -2722,7 +2723,7 @@ public final IdentifierOrParameterContext identifierOrParameter() throws Recogni
case NAMED_OR_POSITIONAL_PARAM:
enterOuterAlt(_localctx, 2);
{
- setState(400);
+ setState(402);
parameter();
}
break;
@@ -2730,7 +2731,7 @@ public final IdentifierOrParameterContext identifierOrParameter() throws Recogni
case NAMED_OR_POSITIONAL_DOUBLE_PARAMS:
enterOuterAlt(_localctx, 3);
{
- setState(401);
+ setState(403);
doubleParameter();
}
break;
@@ -2781,9 +2782,9 @@ public final LimitCommandContext limitCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(404);
+ setState(406);
match(LIMIT);
- setState(405);
+ setState(407);
constant();
}
}
@@ -2838,25 +2839,25 @@ public final SortCommandContext sortCommand() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(407);
+ setState(409);
match(SORT);
- setState(408);
+ setState(410);
orderExpression();
- setState(413);
+ setState(415);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,26,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(409);
+ setState(411);
match(COMMA);
- setState(410);
+ setState(412);
orderExpression();
}
}
}
- setState(415);
+ setState(417);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,26,_ctx);
}
@@ -2912,14 +2913,14 @@ public final OrderExpressionContext orderExpression() throws RecognitionExceptio
try {
enterOuterAlt(_localctx, 1);
{
- setState(416);
- booleanExpression(0);
setState(418);
+ booleanExpression(0);
+ setState(420);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,27,_ctx) ) {
case 1:
{
- setState(417);
+ setState(419);
((OrderExpressionContext)_localctx).ordering = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==ASC || _la==DESC) ) {
@@ -2933,14 +2934,14 @@ public final OrderExpressionContext orderExpression() throws RecognitionExceptio
}
break;
}
- setState(422);
+ setState(424);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,28,_ctx) ) {
case 1:
{
- setState(420);
+ setState(422);
match(NULLS);
- setState(421);
+ setState(423);
((OrderExpressionContext)_localctx).nullOrdering = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==FIRST || _la==LAST) ) {
@@ -2999,9 +3000,9 @@ public final KeepCommandContext keepCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(424);
+ setState(426);
match(KEEP);
- setState(425);
+ setState(427);
qualifiedNamePatterns();
}
}
@@ -3048,9 +3049,9 @@ public final DropCommandContext dropCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(427);
+ setState(429);
match(DROP);
- setState(428);
+ setState(430);
qualifiedNamePatterns();
}
}
@@ -3105,25 +3106,25 @@ public final RenameCommandContext renameCommand() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(430);
+ setState(432);
match(RENAME);
- setState(431);
+ setState(433);
renameClause();
- setState(436);
+ setState(438);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,29,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(432);
+ setState(434);
match(COMMA);
- setState(433);
+ setState(435);
renameClause();
}
}
}
- setState(438);
+ setState(440);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,29,_ctx);
}
@@ -3176,28 +3177,28 @@ public final RenameClauseContext renameClause() throws RecognitionException {
RenameClauseContext _localctx = new RenameClauseContext(_ctx, getState());
enterRule(_localctx, 80, RULE_renameClause);
try {
- setState(447);
+ setState(449);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,30,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(439);
+ setState(441);
((RenameClauseContext)_localctx).oldName = qualifiedNamePattern();
- setState(440);
+ setState(442);
match(AS);
- setState(441);
+ setState(443);
((RenameClauseContext)_localctx).newName = qualifiedNamePattern();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(443);
+ setState(445);
((RenameClauseContext)_localctx).newName = qualifiedNamePattern();
- setState(444);
+ setState(446);
match(ASSIGN);
- setState(445);
+ setState(447);
((RenameClauseContext)_localctx).oldName = qualifiedNamePattern();
}
break;
@@ -3252,18 +3253,18 @@ public final DissectCommandContext dissectCommand() throws RecognitionException
try {
enterOuterAlt(_localctx, 1);
{
- setState(449);
+ setState(451);
match(DISSECT);
- setState(450);
+ setState(452);
primaryExpression(0);
- setState(451);
- string();
setState(453);
+ string();
+ setState(455);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,31,_ctx) ) {
case 1:
{
- setState(452);
+ setState(454);
dissectCommandOptions();
}
break;
@@ -3320,23 +3321,23 @@ public final DissectCommandOptionsContext dissectCommandOptions() throws Recogni
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(455);
+ setState(457);
dissectCommandOption();
- setState(460);
+ setState(462);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,32,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(456);
+ setState(458);
match(COMMA);
- setState(457);
+ setState(459);
dissectCommandOption();
}
}
}
- setState(462);
+ setState(464);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,32,_ctx);
}
@@ -3388,11 +3389,11 @@ public final DissectCommandOptionContext dissectCommandOption() throws Recogniti
try {
enterOuterAlt(_localctx, 1);
{
- setState(463);
+ setState(465);
identifier();
- setState(464);
+ setState(466);
match(ASSIGN);
- setState(465);
+ setState(467);
constant();
}
}
@@ -3439,14 +3440,14 @@ public final CommandNamedParametersContext commandNamedParameters() throws Recog
try {
enterOuterAlt(_localctx, 1);
{
- setState(469);
+ setState(471);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,33,_ctx) ) {
case 1:
{
- setState(467);
+ setState(469);
match(WITH);
- setState(468);
+ setState(470);
mapExpression();
}
break;
@@ -3499,11 +3500,11 @@ public final GrokCommandContext grokCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(471);
+ setState(473);
match(GROK);
- setState(472);
+ setState(474);
primaryExpression(0);
- setState(473);
+ setState(475);
string();
}
}
@@ -3550,9 +3551,9 @@ public final MvExpandCommandContext mvExpandCommand() throws RecognitionExceptio
try {
enterOuterAlt(_localctx, 1);
{
- setState(475);
+ setState(477);
match(MV_EXPAND);
- setState(476);
+ setState(478);
qualifiedName();
}
}
@@ -3599,9 +3600,9 @@ public final ExplainCommandContext explainCommand() throws RecognitionException
try {
enterOuterAlt(_localctx, 1);
{
- setState(478);
+ setState(480);
match(DEV_EXPLAIN);
- setState(479);
+ setState(481);
subqueryExpression();
}
}
@@ -3649,11 +3650,11 @@ public final SubqueryExpressionContext subqueryExpression() throws RecognitionEx
try {
enterOuterAlt(_localctx, 1);
{
- setState(481);
+ setState(483);
match(LP);
- setState(482);
+ setState(484);
query(0);
- setState(483);
+ setState(485);
match(RP);
}
}
@@ -3710,9 +3711,9 @@ public final ShowCommandContext showCommand() throws RecognitionException {
_localctx = new ShowInfoContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(485);
+ setState(487);
match(SHOW);
- setState(486);
+ setState(488);
match(INFO);
}
}
@@ -3777,46 +3778,46 @@ public final EnrichCommandContext enrichCommand() throws RecognitionException {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(488);
+ setState(490);
match(ENRICH);
- setState(489);
+ setState(491);
((EnrichCommandContext)_localctx).policyName = enrichPolicyName();
- setState(492);
+ setState(494);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,34,_ctx) ) {
case 1:
{
- setState(490);
+ setState(492);
match(ON);
- setState(491);
+ setState(493);
((EnrichCommandContext)_localctx).matchField = qualifiedNamePattern();
}
break;
}
- setState(503);
+ setState(505);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,36,_ctx) ) {
case 1:
{
- setState(494);
+ setState(496);
match(WITH);
- setState(495);
+ setState(497);
enrichWithClause();
- setState(500);
+ setState(502);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,35,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(496);
+ setState(498);
match(COMMA);
- setState(497);
+ setState(499);
enrichWithClause();
}
}
}
- setState(502);
+ setState(504);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,35,_ctx);
}
@@ -3867,7 +3868,7 @@ public final EnrichPolicyNameContext enrichPolicyName() throws RecognitionExcept
try {
enterOuterAlt(_localctx, 1);
{
- setState(505);
+ setState(507);
_la = _input.LA(1);
if ( !(_la==ENRICH_POLICY_NAME || _la==QUOTED_STRING) ) {
_errHandler.recoverInline(this);
@@ -3927,19 +3928,19 @@ public final EnrichWithClauseContext enrichWithClause() throws RecognitionExcept
try {
enterOuterAlt(_localctx, 1);
{
- setState(510);
+ setState(512);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,37,_ctx) ) {
case 1:
{
- setState(507);
+ setState(509);
((EnrichWithClauseContext)_localctx).newName = qualifiedNamePattern();
- setState(508);
+ setState(510);
match(ASSIGN);
}
break;
}
- setState(512);
+ setState(514);
((EnrichWithClauseContext)_localctx).enrichField = qualifiedNamePattern();
}
}
@@ -3987,9 +3988,9 @@ public final SampleCommandContext sampleCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(514);
+ setState(516);
match(SAMPLE);
- setState(515);
+ setState(517);
((SampleCommandContext)_localctx).probability = constant();
}
}
@@ -4046,34 +4047,34 @@ public final ChangePointCommandContext changePointCommand() throws RecognitionEx
try {
enterOuterAlt(_localctx, 1);
{
- setState(517);
+ setState(519);
match(CHANGE_POINT);
- setState(518);
+ setState(520);
((ChangePointCommandContext)_localctx).value = qualifiedName();
- setState(521);
+ setState(523);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,38,_ctx) ) {
case 1:
{
- setState(519);
+ setState(521);
match(ON);
- setState(520);
+ setState(522);
((ChangePointCommandContext)_localctx).key = qualifiedName();
}
break;
}
- setState(528);
+ setState(530);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,39,_ctx) ) {
case 1:
{
- setState(523);
+ setState(525);
match(AS);
- setState(524);
+ setState(526);
((ChangePointCommandContext)_localctx).targetType = qualifiedName();
- setState(525);
+ setState(527);
match(COMMA);
- setState(526);
+ setState(528);
((ChangePointCommandContext)_localctx).targetPvalue = qualifiedName();
}
break;
@@ -4123,9 +4124,9 @@ public final ForkCommandContext forkCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(530);
+ setState(532);
match(FORK);
- setState(531);
+ setState(533);
forkSubQueries();
}
}
@@ -4175,7 +4176,7 @@ public final ForkSubQueriesContext forkSubQueries() throws RecognitionException
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(534);
+ setState(536);
_errHandler.sync(this);
_alt = 1;
do {
@@ -4183,7 +4184,7 @@ public final ForkSubQueriesContext forkSubQueries() throws RecognitionException
case 1:
{
{
- setState(533);
+ setState(535);
forkSubQuery();
}
}
@@ -4191,7 +4192,7 @@ public final ForkSubQueriesContext forkSubQueries() throws RecognitionException
default:
throw new NoViableAltException(this);
}
- setState(536);
+ setState(538);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,40,_ctx);
} while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
@@ -4241,11 +4242,11 @@ public final ForkSubQueryContext forkSubQuery() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(538);
+ setState(540);
match(LP);
- setState(539);
+ setState(541);
forkSubQueryCommand(0);
- setState(540);
+ setState(542);
match(RP);
}
}
@@ -4341,11 +4342,11 @@ private ForkSubQueryCommandContext forkSubQueryCommand(int _p) throws Recognitio
_ctx = _localctx;
_prevctx = _localctx;
- setState(543);
+ setState(545);
forkSubQueryProcessingCommand();
}
_ctx.stop = _input.LT(-1);
- setState(550);
+ setState(552);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,41,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -4356,16 +4357,16 @@ private ForkSubQueryCommandContext forkSubQueryCommand(int _p) throws Recognitio
{
_localctx = new CompositeForkSubQueryContext(new ForkSubQueryCommandContext(_parentctx, _parentState));
pushNewRecursionContext(_localctx, _startState, RULE_forkSubQueryCommand);
- setState(545);
+ setState(547);
if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
- setState(546);
+ setState(548);
match(PIPE);
- setState(547);
+ setState(549);
forkSubQueryProcessingCommand();
}
}
}
- setState(552);
+ setState(554);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,41,_ctx);
}
@@ -4413,7 +4414,7 @@ public final ForkSubQueryProcessingCommandContext forkSubQueryProcessingCommand(
try {
enterOuterAlt(_localctx, 1);
{
- setState(553);
+ setState(555);
processingCommand();
}
}
@@ -4473,27 +4474,27 @@ public final RerankCommandContext rerankCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(555);
+ setState(557);
match(RERANK);
- setState(559);
+ setState(561);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,42,_ctx) ) {
case 1:
{
- setState(556);
+ setState(558);
((RerankCommandContext)_localctx).targetField = qualifiedName();
- setState(557);
+ setState(559);
match(ASSIGN);
}
break;
}
- setState(561);
+ setState(563);
((RerankCommandContext)_localctx).queryText = constant();
- setState(562);
+ setState(564);
match(ON);
- setState(563);
+ setState(565);
rerankFields();
- setState(564);
+ setState(566);
commandNamedParameters();
}
}
@@ -4549,23 +4550,23 @@ public final CompletionCommandContext completionCommand() throws RecognitionExce
try {
enterOuterAlt(_localctx, 1);
{
- setState(566);
+ setState(568);
match(COMPLETION);
- setState(570);
+ setState(572);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,43,_ctx) ) {
case 1:
{
- setState(567);
+ setState(569);
((CompletionCommandContext)_localctx).targetField = qualifiedName();
- setState(568);
+ setState(570);
match(ASSIGN);
}
break;
}
- setState(572);
+ setState(574);
((CompletionCommandContext)_localctx).prompt = primaryExpression(0);
- setState(573);
+ setState(575);
commandNamedParameters();
}
}
@@ -4618,13 +4619,13 @@ public final LookupCommandContext lookupCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(575);
+ setState(577);
match(DEV_LOOKUP);
- setState(576);
+ setState(578);
((LookupCommandContext)_localctx).tableName = indexPattern();
- setState(577);
+ setState(579);
match(ON);
- setState(578);
+ setState(580);
((LookupCommandContext)_localctx).matchFields = qualifiedNamePatterns();
}
}
@@ -4677,18 +4678,18 @@ public final InlinestatsCommandContext inlinestatsCommand() throws RecognitionEx
try {
enterOuterAlt(_localctx, 1);
{
- setState(580);
+ setState(582);
match(DEV_INLINESTATS);
- setState(581);
+ setState(583);
((InlinestatsCommandContext)_localctx).stats = aggFields();
- setState(584);
+ setState(586);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,44,_ctx) ) {
case 1:
{
- setState(582);
+ setState(584);
match(BY);
- setState(583);
+ setState(585);
((InlinestatsCommandContext)_localctx).grouping = fields();
}
break;
@@ -4738,9 +4739,9 @@ public final InsistCommandContext insistCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(586);
+ setState(588);
match(DEV_INSIST);
- setState(587);
+ setState(589);
qualifiedNamePatterns();
}
}
@@ -4784,7 +4785,7 @@ public final FuseCommandContext fuseCommand() throws RecognitionException {
try {
enterOuterAlt(_localctx, 1);
{
- setState(589);
+ setState(591);
match(DEV_FUSE);
}
}
@@ -5000,7 +5001,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(620);
+ setState(622);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,48,_ctx) ) {
case 1:
@@ -5009,9 +5010,9 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_ctx = _localctx;
_prevctx = _localctx;
- setState(592);
+ setState(594);
match(NOT);
- setState(593);
+ setState(595);
booleanExpression(8);
}
break;
@@ -5020,7 +5021,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new BooleanDefaultContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(594);
+ setState(596);
valueExpression();
}
break;
@@ -5029,7 +5030,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new RegexExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(595);
+ setState(597);
regexBooleanExpression();
}
break;
@@ -5038,41 +5039,41 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new LogicalInContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(596);
- valueExpression();
setState(598);
+ valueExpression();
+ setState(600);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(597);
+ setState(599);
match(NOT);
}
}
- setState(600);
+ setState(602);
match(IN);
- setState(601);
+ setState(603);
match(LP);
- setState(602);
+ setState(604);
valueExpression();
- setState(607);
+ setState(609);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(603);
+ setState(605);
match(COMMA);
- setState(604);
+ setState(606);
valueExpression();
}
}
- setState(609);
+ setState(611);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(610);
+ setState(612);
match(RP);
}
break;
@@ -5081,21 +5082,21 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new IsNullContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(612);
+ setState(614);
valueExpression();
- setState(613);
- match(IS);
setState(615);
+ match(IS);
+ setState(617);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(614);
+ setState(616);
match(NOT);
}
}
- setState(617);
+ setState(619);
match(NULL);
}
break;
@@ -5104,13 +5105,13 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new MatchExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(619);
+ setState(621);
matchBooleanExpression();
}
break;
}
_ctx.stop = _input.LT(-1);
- setState(630);
+ setState(632);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,50,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -5118,7 +5119,7 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
- setState(628);
+ setState(630);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,49,_ctx) ) {
case 1:
@@ -5126,11 +5127,11 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState));
((LogicalBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression);
- setState(622);
+ setState(624);
if (!(precpred(_ctx, 5))) throw new FailedPredicateException(this, "precpred(_ctx, 5)");
- setState(623);
+ setState(625);
((LogicalBinaryContext)_localctx).operator = match(AND);
- setState(624);
+ setState(626);
((LogicalBinaryContext)_localctx).right = booleanExpression(6);
}
break;
@@ -5139,18 +5140,18 @@ private BooleanExpressionContext booleanExpression(int _p) throws RecognitionExc
_localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState));
((LogicalBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_booleanExpression);
- setState(625);
+ setState(627);
if (!(precpred(_ctx, 4))) throw new FailedPredicateException(this, "precpred(_ctx, 4)");
- setState(626);
+ setState(628);
((LogicalBinaryContext)_localctx).operator = match(OR);
- setState(627);
+ setState(629);
((LogicalBinaryContext)_localctx).right = booleanExpression(5);
}
break;
}
}
}
- setState(632);
+ setState(634);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,50,_ctx);
}
@@ -5309,28 +5310,28 @@ public final RegexBooleanExpressionContext regexBooleanExpression() throws Recog
enterRule(_localctx, 134, RULE_regexBooleanExpression);
int _la;
try {
- setState(679);
+ setState(681);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,57,_ctx) ) {
case 1:
_localctx = new LikeExpressionContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(633);
- valueExpression();
setState(635);
+ valueExpression();
+ setState(637);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(634);
+ setState(636);
match(NOT);
}
}
- setState(637);
+ setState(639);
match(LIKE);
- setState(638);
+ setState(640);
string();
}
break;
@@ -5338,21 +5339,21 @@ public final RegexBooleanExpressionContext regexBooleanExpression() throws Recog
_localctx = new RlikeExpressionContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(640);
- valueExpression();
setState(642);
+ valueExpression();
+ setState(644);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(641);
+ setState(643);
match(NOT);
}
}
- setState(644);
+ setState(646);
match(RLIKE);
- setState(645);
+ setState(647);
string();
}
break;
@@ -5360,41 +5361,41 @@ public final RegexBooleanExpressionContext regexBooleanExpression() throws Recog
_localctx = new LikeListExpressionContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(647);
- valueExpression();
setState(649);
+ valueExpression();
+ setState(651);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(648);
+ setState(650);
match(NOT);
}
}
- setState(651);
+ setState(653);
match(LIKE);
- setState(652);
+ setState(654);
match(LP);
- setState(653);
+ setState(655);
string();
- setState(658);
+ setState(660);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(654);
+ setState(656);
match(COMMA);
- setState(655);
+ setState(657);
string();
}
}
- setState(660);
+ setState(662);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(661);
+ setState(663);
match(RP);
}
break;
@@ -5402,41 +5403,41 @@ public final RegexBooleanExpressionContext regexBooleanExpression() throws Recog
_localctx = new RlikeListExpressionContext(_localctx);
enterOuterAlt(_localctx, 4);
{
- setState(663);
- valueExpression();
setState(665);
+ valueExpression();
+ setState(667);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==NOT) {
{
- setState(664);
+ setState(666);
match(NOT);
}
}
- setState(667);
+ setState(669);
match(RLIKE);
- setState(668);
+ setState(670);
match(LP);
- setState(669);
+ setState(671);
string();
- setState(674);
+ setState(676);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(670);
+ setState(672);
match(COMMA);
- setState(671);
+ setState(673);
string();
}
}
- setState(676);
+ setState(678);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(677);
+ setState(679);
match(RP);
}
break;
@@ -5496,23 +5497,23 @@ public final MatchBooleanExpressionContext matchBooleanExpression() throws Recog
try {
enterOuterAlt(_localctx, 1);
{
- setState(681);
+ setState(683);
((MatchBooleanExpressionContext)_localctx).fieldExp = qualifiedName();
- setState(684);
+ setState(686);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==CAST_OP) {
{
- setState(682);
+ setState(684);
match(CAST_OP);
- setState(683);
+ setState(685);
((MatchBooleanExpressionContext)_localctx).fieldType = dataType();
}
}
- setState(686);
+ setState(688);
match(COLON);
- setState(687);
+ setState(689);
((MatchBooleanExpressionContext)_localctx).matchQuery = constant();
}
}
@@ -5564,8 +5565,16 @@ public T accept(ParseTreeVisitor extends T> visitor) {
}
@SuppressWarnings("CheckReturnValue")
public static class ComparisonContext extends ValueExpressionContext {
- public ComparisonExpressionContext comparisonExpression() {
- return getRuleContext(ComparisonExpressionContext.class,0);
+ public OperatorExpressionContext left;
+ public OperatorExpressionContext right;
+ public ComparisonOperatorContext comparisonOperator() {
+ return getRuleContext(ComparisonOperatorContext.class,0);
+ }
+ public List operatorExpression() {
+ return getRuleContexts(OperatorExpressionContext.class);
+ }
+ public OperatorExpressionContext operatorExpression(int i) {
+ return getRuleContext(OperatorExpressionContext.class,i);
}
@SuppressWarnings("this-escape")
public ComparisonContext(ValueExpressionContext ctx) { copyFrom(ctx); }
@@ -5588,14 +5597,14 @@ public final ValueExpressionContext valueExpression() throws RecognitionExceptio
ValueExpressionContext _localctx = new ValueExpressionContext(_ctx, getState());
enterRule(_localctx, 138, RULE_valueExpression);
try {
- setState(691);
+ setState(696);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,59,_ctx) ) {
case 1:
_localctx = new ValueExpressionDefaultContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(689);
+ setState(691);
operatorExpression(0);
}
break;
@@ -5603,8 +5612,12 @@ public final ValueExpressionContext valueExpression() throws RecognitionExceptio
_localctx = new ComparisonContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(690);
- comparisonExpression();
+ setState(692);
+ ((ComparisonContext)_localctx).left = operatorExpression(0);
+ setState(693);
+ comparisonOperator();
+ setState(694);
+ ((ComparisonContext)_localctx).right = operatorExpression(0);
}
break;
}
@@ -5620,64 +5633,6 @@ public final ValueExpressionContext valueExpression() throws RecognitionExceptio
return _localctx;
}
- @SuppressWarnings("CheckReturnValue")
- public static class ComparisonExpressionContext extends ParserRuleContext {
- public OperatorExpressionContext left;
- public OperatorExpressionContext right;
- public ComparisonOperatorContext comparisonOperator() {
- return getRuleContext(ComparisonOperatorContext.class,0);
- }
- public List operatorExpression() {
- return getRuleContexts(OperatorExpressionContext.class);
- }
- public OperatorExpressionContext operatorExpression(int i) {
- return getRuleContext(OperatorExpressionContext.class,i);
- }
- @SuppressWarnings("this-escape")
- public ComparisonExpressionContext(ParserRuleContext parent, int invokingState) {
- super(parent, invokingState);
- }
- @Override public int getRuleIndex() { return RULE_comparisonExpression; }
- @Override
- public void enterRule(ParseTreeListener listener) {
- if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterComparisonExpression(this);
- }
- @Override
- public void exitRule(ParseTreeListener listener) {
- if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).exitComparisonExpression(this);
- }
- @Override
- public T accept(ParseTreeVisitor extends T> visitor) {
- if ( visitor instanceof EsqlBaseParserVisitor ) return ((EsqlBaseParserVisitor extends T>)visitor).visitComparisonExpression(this);
- else return visitor.visitChildren(this);
- }
- }
-
- public final ComparisonExpressionContext comparisonExpression() throws RecognitionException {
- ComparisonExpressionContext _localctx = new ComparisonExpressionContext(_ctx, getState());
- enterRule(_localctx, 140, RULE_comparisonExpression);
- try {
- enterOuterAlt(_localctx, 1);
- {
- setState(693);
- ((ComparisonExpressionContext)_localctx).left = operatorExpression(0);
- setState(694);
- comparisonOperator();
- setState(695);
- ((ComparisonExpressionContext)_localctx).right = operatorExpression(0);
- }
- }
- catch (RecognitionException re) {
- _localctx.exception = re;
- _errHandler.reportError(this, re);
- _errHandler.recover(this, re);
- }
- finally {
- exitRule();
- }
- return _localctx;
- }
-
@SuppressWarnings("CheckReturnValue")
public static class OperatorExpressionContext extends ParserRuleContext {
@SuppressWarnings("this-escape")
@@ -5779,14 +5734,14 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
int _parentState = getState();
OperatorExpressionContext _localctx = new OperatorExpressionContext(_ctx, _parentState);
OperatorExpressionContext _prevctx = _localctx;
- int _startState = 142;
- enterRecursionRule(_localctx, 142, RULE_operatorExpression, _p);
+ int _startState = 140;
+ enterRecursionRule(_localctx, 140, RULE_operatorExpression, _p);
int _la;
try {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(701);
+ setState(702);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,60,_ctx) ) {
case 1:
@@ -5795,7 +5750,7 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_ctx = _localctx;
_prevctx = _localctx;
- setState(698);
+ setState(699);
primaryExpression(0);
}
break;
@@ -5804,7 +5759,7 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_localctx = new ArithmeticUnaryContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(699);
+ setState(700);
((ArithmeticUnaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
@@ -5815,13 +5770,13 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_errHandler.reportMatch(this);
consume();
}
- setState(700);
+ setState(701);
operatorExpression(3);
}
break;
}
_ctx.stop = _input.LT(-1);
- setState(711);
+ setState(712);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,62,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -5829,7 +5784,7 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
if ( _parseListeners!=null ) triggerExitRuleEvent();
_prevctx = _localctx;
{
- setState(709);
+ setState(710);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,61,_ctx) ) {
case 1:
@@ -5837,9 +5792,9 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState));
((ArithmeticBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_operatorExpression);
- setState(703);
- if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
setState(704);
+ if (!(precpred(_ctx, 2))) throw new FailedPredicateException(this, "precpred(_ctx, 2)");
+ setState(705);
((ArithmeticBinaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(((((_la - 85)) & ~0x3f) == 0 && ((1L << (_la - 85)) & 7L) != 0)) ) {
@@ -5850,7 +5805,7 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_errHandler.reportMatch(this);
consume();
}
- setState(705);
+ setState(706);
((ArithmeticBinaryContext)_localctx).right = operatorExpression(3);
}
break;
@@ -5859,9 +5814,9 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState));
((ArithmeticBinaryContext)_localctx).left = _prevctx;
pushNewRecursionContext(_localctx, _startState, RULE_operatorExpression);
- setState(706);
- if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
setState(707);
+ if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
+ setState(708);
((ArithmeticBinaryContext)_localctx).operator = _input.LT(1);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
@@ -5872,14 +5827,14 @@ private OperatorExpressionContext operatorExpression(int _p) throws RecognitionE
_errHandler.reportMatch(this);
consume();
}
- setState(708);
+ setState(709);
((ArithmeticBinaryContext)_localctx).right = operatorExpression(2);
}
break;
}
}
}
- setState(713);
+ setState(714);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,62,_ctx);
}
@@ -6031,13 +5986,13 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
int _parentState = getState();
PrimaryExpressionContext _localctx = new PrimaryExpressionContext(_ctx, _parentState);
PrimaryExpressionContext _prevctx = _localctx;
- int _startState = 144;
- enterRecursionRule(_localctx, 144, RULE_primaryExpression, _p);
+ int _startState = 142;
+ enterRecursionRule(_localctx, 142, RULE_primaryExpression, _p);
try {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(722);
+ setState(723);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,63,_ctx) ) {
case 1:
@@ -6046,7 +6001,7 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
_ctx = _localctx;
_prevctx = _localctx;
- setState(715);
+ setState(716);
constant();
}
break;
@@ -6055,7 +6010,7 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
_localctx = new DereferenceContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(716);
+ setState(717);
qualifiedName();
}
break;
@@ -6064,7 +6019,7 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
_localctx = new FunctionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(717);
+ setState(718);
functionExpression();
}
break;
@@ -6073,17 +6028,17 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
_localctx = new ParenthesizedExpressionContext(_localctx);
_ctx = _localctx;
_prevctx = _localctx;
- setState(718);
- match(LP);
setState(719);
- booleanExpression(0);
+ match(LP);
setState(720);
+ booleanExpression(0);
+ setState(721);
match(RP);
}
break;
}
_ctx.stop = _input.LT(-1);
- setState(729);
+ setState(730);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,64,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
@@ -6094,16 +6049,16 @@ private PrimaryExpressionContext primaryExpression(int _p) throws RecognitionExc
{
_localctx = new InlineCastContext(new PrimaryExpressionContext(_parentctx, _parentState));
pushNewRecursionContext(_localctx, _startState, RULE_primaryExpression);
- setState(724);
- if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
setState(725);
- match(CAST_OP);
+ if (!(precpred(_ctx, 1))) throw new FailedPredicateException(this, "precpred(_ctx, 1)");
setState(726);
+ match(CAST_OP);
+ setState(727);
dataType();
}
}
}
- setState(731);
+ setState(732);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,64,_ctx);
}
@@ -6163,56 +6118,56 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final FunctionExpressionContext functionExpression() throws RecognitionException {
FunctionExpressionContext _localctx = new FunctionExpressionContext(_ctx, getState());
- enterRule(_localctx, 146, RULE_functionExpression);
+ enterRule(_localctx, 144, RULE_functionExpression);
int _la;
try {
int _alt;
enterOuterAlt(_localctx, 1);
{
- setState(732);
- functionName();
setState(733);
+ functionName();
+ setState(734);
match(LP);
- setState(747);
+ setState(748);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,67,_ctx) ) {
case 1:
{
- setState(734);
+ setState(735);
match(ASTERISK);
}
break;
case 2:
{
{
- setState(735);
+ setState(736);
booleanExpression(0);
- setState(740);
+ setState(741);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,65,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(736);
- match(COMMA);
setState(737);
+ match(COMMA);
+ setState(738);
booleanExpression(0);
}
}
}
- setState(742);
+ setState(743);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,65,_ctx);
}
- setState(745);
+ setState(746);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==COMMA) {
{
- setState(743);
- match(COMMA);
setState(744);
+ match(COMMA);
+ setState(745);
mapExpression();
}
}
@@ -6221,7 +6176,7 @@ public final FunctionExpressionContext functionExpression() throws RecognitionEx
}
break;
}
- setState(749);
+ setState(750);
match(RP);
}
}
@@ -6265,9 +6220,9 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final FunctionNameContext functionName() throws RecognitionException {
FunctionNameContext _localctx = new FunctionNameContext(_ctx, getState());
- enterRule(_localctx, 148, RULE_functionName);
+ enterRule(_localctx, 146, RULE_functionName);
try {
- setState(754);
+ setState(755);
_errHandler.sync(this);
switch (_input.LA(1)) {
case PARAM:
@@ -6278,21 +6233,21 @@ public final FunctionNameContext functionName() throws RecognitionException {
case QUOTED_IDENTIFIER:
enterOuterAlt(_localctx, 1);
{
- setState(751);
+ setState(752);
identifierOrParameter();
}
break;
case FIRST:
enterOuterAlt(_localctx, 2);
{
- setState(752);
+ setState(753);
match(FIRST);
}
break;
case LAST:
enterOuterAlt(_localctx, 3);
{
- setState(753);
+ setState(754);
match(LAST);
}
break;
@@ -6347,40 +6302,40 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final MapExpressionContext mapExpression() throws RecognitionException {
MapExpressionContext _localctx = new MapExpressionContext(_ctx, getState());
- enterRule(_localctx, 150, RULE_mapExpression);
+ enterRule(_localctx, 148, RULE_mapExpression);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(756);
+ setState(757);
match(LEFT_BRACES);
- setState(765);
+ setState(766);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==QUOTED_STRING) {
{
- setState(757);
+ setState(758);
entryExpression();
- setState(762);
+ setState(763);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(758);
- match(COMMA);
setState(759);
+ match(COMMA);
+ setState(760);
entryExpression();
}
}
- setState(764);
+ setState(765);
_errHandler.sync(this);
_la = _input.LA(1);
}
}
}
- setState(767);
+ setState(768);
match(RIGHT_BRACES);
}
}
@@ -6428,15 +6383,15 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final EntryExpressionContext entryExpression() throws RecognitionException {
EntryExpressionContext _localctx = new EntryExpressionContext(_ctx, getState());
- enterRule(_localctx, 152, RULE_entryExpression);
+ enterRule(_localctx, 150, RULE_entryExpression);
try {
enterOuterAlt(_localctx, 1);
{
- setState(769);
- ((EntryExpressionContext)_localctx).key = string();
setState(770);
- match(COLON);
+ ((EntryExpressionContext)_localctx).key = string();
setState(771);
+ match(COLON);
+ setState(772);
((EntryExpressionContext)_localctx).value = mapValue();
}
}
@@ -6481,9 +6436,9 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final MapValueContext mapValue() throws RecognitionException {
MapValueContext _localctx = new MapValueContext(_ctx, getState());
- enterRule(_localctx, 154, RULE_mapValue);
+ enterRule(_localctx, 152, RULE_mapValue);
try {
- setState(775);
+ setState(776);
_errHandler.sync(this);
switch (_input.LA(1)) {
case QUOTED_STRING:
@@ -6499,14 +6454,14 @@ public final MapValueContext mapValue() throws RecognitionException {
case OPENING_BRACKET:
enterOuterAlt(_localctx, 1);
{
- setState(773);
+ setState(774);
constant();
}
break;
case LEFT_BRACES:
enterOuterAlt(_localctx, 2);
{
- setState(774);
+ setState(775);
mapExpression();
}
break;
@@ -6778,17 +6733,17 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final ConstantContext constant() throws RecognitionException {
ConstantContext _localctx = new ConstantContext(_ctx, getState());
- enterRule(_localctx, 156, RULE_constant);
+ enterRule(_localctx, 154, RULE_constant);
int _la;
try {
- setState(819);
+ setState(820);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,75,_ctx) ) {
case 1:
_localctx = new NullLiteralContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(777);
+ setState(778);
match(NULL);
}
break;
@@ -6796,9 +6751,9 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new QualifiedIntegerLiteralContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(778);
- integerValue();
setState(779);
+ integerValue();
+ setState(780);
match(UNQUOTED_IDENTIFIER);
}
break;
@@ -6806,7 +6761,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new DecimalLiteralContext(_localctx);
enterOuterAlt(_localctx, 3);
{
- setState(781);
+ setState(782);
decimalValue();
}
break;
@@ -6814,7 +6769,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new IntegerLiteralContext(_localctx);
enterOuterAlt(_localctx, 4);
{
- setState(782);
+ setState(783);
integerValue();
}
break;
@@ -6822,7 +6777,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new BooleanLiteralContext(_localctx);
enterOuterAlt(_localctx, 5);
{
- setState(783);
+ setState(784);
booleanValue();
}
break;
@@ -6830,7 +6785,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new InputParameterContext(_localctx);
enterOuterAlt(_localctx, 6);
{
- setState(784);
+ setState(785);
parameter();
}
break;
@@ -6838,7 +6793,7 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new StringLiteralContext(_localctx);
enterOuterAlt(_localctx, 7);
{
- setState(785);
+ setState(786);
string();
}
break;
@@ -6846,27 +6801,27 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new NumericArrayLiteralContext(_localctx);
enterOuterAlt(_localctx, 8);
{
- setState(786);
- match(OPENING_BRACKET);
setState(787);
+ match(OPENING_BRACKET);
+ setState(788);
numericValue();
- setState(792);
+ setState(793);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(788);
- match(COMMA);
setState(789);
+ match(COMMA);
+ setState(790);
numericValue();
}
}
- setState(794);
+ setState(795);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(795);
+ setState(796);
match(CLOSING_BRACKET);
}
break;
@@ -6874,27 +6829,27 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new BooleanArrayLiteralContext(_localctx);
enterOuterAlt(_localctx, 9);
{
- setState(797);
- match(OPENING_BRACKET);
setState(798);
+ match(OPENING_BRACKET);
+ setState(799);
booleanValue();
- setState(803);
+ setState(804);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(799);
- match(COMMA);
setState(800);
+ match(COMMA);
+ setState(801);
booleanValue();
}
}
- setState(805);
+ setState(806);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(806);
+ setState(807);
match(CLOSING_BRACKET);
}
break;
@@ -6902,27 +6857,27 @@ public final ConstantContext constant() throws RecognitionException {
_localctx = new StringArrayLiteralContext(_localctx);
enterOuterAlt(_localctx, 10);
{
- setState(808);
- match(OPENING_BRACKET);
setState(809);
+ match(OPENING_BRACKET);
+ setState(810);
string();
- setState(814);
+ setState(815);
_errHandler.sync(this);
_la = _input.LA(1);
while (_la==COMMA) {
{
{
- setState(810);
- match(COMMA);
setState(811);
+ match(COMMA);
+ setState(812);
string();
}
}
- setState(816);
+ setState(817);
_errHandler.sync(this);
_la = _input.LA(1);
}
- setState(817);
+ setState(818);
match(CLOSING_BRACKET);
}
break;
@@ -6965,12 +6920,12 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final BooleanValueContext booleanValue() throws RecognitionException {
BooleanValueContext _localctx = new BooleanValueContext(_ctx, getState());
- enterRule(_localctx, 158, RULE_booleanValue);
+ enterRule(_localctx, 156, RULE_booleanValue);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(821);
+ setState(822);
_la = _input.LA(1);
if ( !(_la==FALSE || _la==TRUE) ) {
_errHandler.recoverInline(this);
@@ -7023,22 +6978,22 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final NumericValueContext numericValue() throws RecognitionException {
NumericValueContext _localctx = new NumericValueContext(_ctx, getState());
- enterRule(_localctx, 160, RULE_numericValue);
+ enterRule(_localctx, 158, RULE_numericValue);
try {
- setState(825);
+ setState(826);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,76,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(823);
+ setState(824);
decimalValue();
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(824);
+ setState(825);
integerValue();
}
break;
@@ -7082,17 +7037,17 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final DecimalValueContext decimalValue() throws RecognitionException {
DecimalValueContext _localctx = new DecimalValueContext(_ctx, getState());
- enterRule(_localctx, 162, RULE_decimalValue);
+ enterRule(_localctx, 160, RULE_decimalValue);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(828);
+ setState(829);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
- setState(827);
+ setState(828);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
_errHandler.recoverInline(this);
@@ -7105,7 +7060,7 @@ public final DecimalValueContext decimalValue() throws RecognitionException {
}
}
- setState(830);
+ setState(831);
match(DECIMAL_LITERAL);
}
}
@@ -7147,17 +7102,17 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final IntegerValueContext integerValue() throws RecognitionException {
IntegerValueContext _localctx = new IntegerValueContext(_ctx, getState());
- enterRule(_localctx, 164, RULE_integerValue);
+ enterRule(_localctx, 162, RULE_integerValue);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(833);
+ setState(834);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==PLUS || _la==MINUS) {
{
- setState(832);
+ setState(833);
_la = _input.LA(1);
if ( !(_la==PLUS || _la==MINUS) ) {
_errHandler.recoverInline(this);
@@ -7170,7 +7125,7 @@ public final IntegerValueContext integerValue() throws RecognitionException {
}
}
- setState(835);
+ setState(836);
match(INTEGER_LITERAL);
}
}
@@ -7210,11 +7165,11 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final StringContext string() throws RecognitionException {
StringContext _localctx = new StringContext(_ctx, getState());
- enterRule(_localctx, 166, RULE_string);
+ enterRule(_localctx, 164, RULE_string);
try {
enterOuterAlt(_localctx, 1);
{
- setState(837);
+ setState(838);
match(QUOTED_STRING);
}
}
@@ -7259,12 +7214,12 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final ComparisonOperatorContext comparisonOperator() throws RecognitionException {
ComparisonOperatorContext _localctx = new ComparisonOperatorContext(_ctx, getState());
- enterRule(_localctx, 168, RULE_comparisonOperator);
+ enterRule(_localctx, 166, RULE_comparisonOperator);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(839);
+ setState(840);
_la = _input.LA(1);
if ( !(((((_la - 76)) & ~0x3f) == 0 && ((1L << (_la - 76)) & 125L) != 0)) ) {
_errHandler.recoverInline(this);
@@ -7322,12 +7277,12 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final JoinCommandContext joinCommand() throws RecognitionException {
JoinCommandContext _localctx = new JoinCommandContext(_ctx, getState());
- enterRule(_localctx, 170, RULE_joinCommand);
+ enterRule(_localctx, 168, RULE_joinCommand);
int _la;
try {
enterOuterAlt(_localctx, 1);
{
- setState(841);
+ setState(842);
((JoinCommandContext)_localctx).type = _input.LT(1);
_la = _input.LA(1);
if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 109051904L) != 0)) ) {
@@ -7338,11 +7293,11 @@ public final JoinCommandContext joinCommand() throws RecognitionException {
_errHandler.reportMatch(this);
consume();
}
- setState(842);
- match(JOIN);
setState(843);
- joinTarget();
+ match(JOIN);
setState(844);
+ joinTarget();
+ setState(845);
joinCondition();
}
}
@@ -7388,37 +7343,37 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final JoinTargetContext joinTarget() throws RecognitionException {
JoinTargetContext _localctx = new JoinTargetContext(_ctx, getState());
- enterRule(_localctx, 172, RULE_joinTarget);
+ enterRule(_localctx, 170, RULE_joinTarget);
int _la;
try {
- setState(854);
+ setState(855);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,80,_ctx) ) {
case 1:
enterOuterAlt(_localctx, 1);
{
- setState(846);
- if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
setState(847);
+ if (!(this.isDevVersion())) throw new FailedPredicateException(this, "this.isDevVersion()");
+ setState(848);
((JoinTargetContext)_localctx).index = indexPattern();
- setState(849);
+ setState(850);
_errHandler.sync(this);
_la = _input.LA(1);
if (_la==AS) {
{
- setState(848);
+ setState(849);
match(AS);
}
}
- setState(851);
+ setState(852);
((JoinTargetContext)_localctx).qualifier = match(UNQUOTED_SOURCE);
}
break;
case 2:
enterOuterAlt(_localctx, 2);
{
- setState(853);
+ setState(854);
((JoinTargetContext)_localctx).index = indexPattern();
}
break;
@@ -7481,11 +7436,11 @@ public T accept(ParseTreeVisitor extends T> visitor) {
@SuppressWarnings("CheckReturnValue")
public static class ExpressionBasedLookupJoinContext extends JoinConditionContext {
public TerminalNode ON() { return getToken(EsqlBaseParser.ON, 0); }
- public List comparisonExpression() {
- return getRuleContexts(ComparisonExpressionContext.class);
+ public List joinPredicateExpression() {
+ return getRuleContexts(JoinPredicateExpressionContext.class);
}
- public ComparisonExpressionContext comparisonExpression(int i) {
- return getRuleContext(ComparisonExpressionContext.class,i);
+ public JoinPredicateExpressionContext joinPredicateExpression(int i) {
+ return getRuleContext(JoinPredicateExpressionContext.class,i);
}
public List AND() { return getTokens(EsqlBaseParser.AND); }
public TerminalNode AND(int i) {
@@ -7510,35 +7465,35 @@ public T accept(ParseTreeVisitor extends T> visitor) {
public final JoinConditionContext joinCondition() throws RecognitionException {
JoinConditionContext _localctx = new JoinConditionContext(_ctx, getState());
- enterRule(_localctx, 174, RULE_joinCondition);
+ enterRule(_localctx, 172, RULE_joinCondition);
try {
int _alt;
- setState(874);
+ setState(875);
_errHandler.sync(this);
switch ( getInterpreter().adaptivePredict(_input,83,_ctx) ) {
case 1:
_localctx = new FieldBasedLookupJoinContext(_localctx);
enterOuterAlt(_localctx, 1);
{
- setState(856);
- match(ON);
setState(857);
+ match(ON);
+ setState(858);
qualifiedName();
- setState(862);
+ setState(863);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,81,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(858);
- match(COMMA);
setState(859);
+ match(COMMA);
+ setState(860);
qualifiedName();
}
}
}
- setState(864);
+ setState(865);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,81,_ctx);
}
@@ -7548,25 +7503,25 @@ public final JoinConditionContext joinCondition() throws RecognitionException {
_localctx = new ExpressionBasedLookupJoinContext(_localctx);
enterOuterAlt(_localctx, 2);
{
- setState(865);
- match(ON);
setState(866);
- comparisonExpression();
- setState(871);
+ match(ON);
+ setState(867);
+ joinPredicateExpression();
+ setState(872);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,82,_ctx);
while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
if ( _alt==1 ) {
{
{
- setState(867);
- match(AND);
setState(868);
- comparisonExpression();
+ match(AND);
+ setState(869);
+ joinPredicateExpression();
}
}
}
- setState(873);
+ setState(874);
_errHandler.sync(this);
_alt = getInterpreter().adaptivePredict(_input,82,_ctx);
}
@@ -7585,6 +7540,110 @@ public final JoinConditionContext joinCondition() throws RecognitionException {
return _localctx;
}
+ @SuppressWarnings("CheckReturnValue")
+ public static class JoinPredicateContext extends ParserRuleContext {
+ public ValueExpressionContext valueExpression() {
+ return getRuleContext(ValueExpressionContext.class,0);
+ }
+ @SuppressWarnings("this-escape")
+ public JoinPredicateContext(ParserRuleContext parent, int invokingState) {
+ super(parent, invokingState);
+ }
+ @Override public int getRuleIndex() { return RULE_joinPredicate; }
+ @Override
+ public void enterRule(ParseTreeListener listener) {
+ if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterJoinPredicate(this);
+ }
+ @Override
+ public void exitRule(ParseTreeListener listener) {
+ if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).exitJoinPredicate(this);
+ }
+ @Override
+ public T accept(ParseTreeVisitor extends T> visitor) {
+ if ( visitor instanceof EsqlBaseParserVisitor ) return ((EsqlBaseParserVisitor extends T>)visitor).visitJoinPredicate(this);
+ else return visitor.visitChildren(this);
+ }
+ }
+
+ public final JoinPredicateContext joinPredicate() throws RecognitionException {
+ JoinPredicateContext _localctx = new JoinPredicateContext(_ctx, getState());
+ enterRule(_localctx, 174, RULE_joinPredicate);
+ try {
+ enterOuterAlt(_localctx, 1);
+ {
+ setState(877);
+ valueExpression();
+ }
+ }
+ catch (RecognitionException re) {
+ _localctx.exception = re;
+ _errHandler.reportError(this, re);
+ _errHandler.recover(this, re);
+ }
+ finally {
+ exitRule();
+ }
+ return _localctx;
+ }
+
+ @SuppressWarnings("CheckReturnValue")
+ public static class JoinPredicateExpressionContext extends ParserRuleContext {
+ public OperatorExpressionContext left;
+ public OperatorExpressionContext right;
+ public ComparisonOperatorContext comparisonOperator() {
+ return getRuleContext(ComparisonOperatorContext.class,0);
+ }
+ public List operatorExpression() {
+ return getRuleContexts(OperatorExpressionContext.class);
+ }
+ public OperatorExpressionContext operatorExpression(int i) {
+ return getRuleContext(OperatorExpressionContext.class,i);
+ }
+ @SuppressWarnings("this-escape")
+ public JoinPredicateExpressionContext(ParserRuleContext parent, int invokingState) {
+ super(parent, invokingState);
+ }
+ @Override public int getRuleIndex() { return RULE_joinPredicateExpression; }
+ @Override
+ public void enterRule(ParseTreeListener listener) {
+ if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).enterJoinPredicateExpression(this);
+ }
+ @Override
+ public void exitRule(ParseTreeListener listener) {
+ if ( listener instanceof EsqlBaseParserListener ) ((EsqlBaseParserListener)listener).exitJoinPredicateExpression(this);
+ }
+ @Override
+ public T accept(ParseTreeVisitor extends T> visitor) {
+ if ( visitor instanceof EsqlBaseParserVisitor ) return ((EsqlBaseParserVisitor extends T>)visitor).visitJoinPredicateExpression(this);
+ else return visitor.visitChildren(this);
+ }
+ }
+
+ public final JoinPredicateExpressionContext joinPredicateExpression() throws RecognitionException {
+ JoinPredicateExpressionContext _localctx = new JoinPredicateExpressionContext(_ctx, getState());
+ enterRule(_localctx, 176, RULE_joinPredicateExpression);
+ try {
+ enterOuterAlt(_localctx, 1);
+ {
+ setState(879);
+ ((JoinPredicateExpressionContext)_localctx).left = operatorExpression(0);
+ setState(880);
+ comparisonOperator();
+ setState(881);
+ ((JoinPredicateExpressionContext)_localctx).right = operatorExpression(0);
+ }
+ }
+ catch (RecognitionException re) {
+ _localctx.exception = re;
+ _errHandler.reportError(this, re);
+ _errHandler.recover(this, re);
+ }
+ finally {
+ exitRule();
+ }
+ return _localctx;
+ }
+
public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
switch (ruleIndex) {
case 1:
@@ -7601,11 +7660,11 @@ public boolean sempred(RuleContext _localctx, int ruleIndex, int predIndex) {
return forkSubQueryCommand_sempred((ForkSubQueryCommandContext)_localctx, predIndex);
case 66:
return booleanExpression_sempred((BooleanExpressionContext)_localctx, predIndex);
- case 71:
+ case 70:
return operatorExpression_sempred((OperatorExpressionContext)_localctx, predIndex);
- case 72:
+ case 71:
return primaryExpression_sempred((PrimaryExpressionContext)_localctx, predIndex);
- case 86:
+ case 85:
return joinTarget_sempred((JoinTargetContext)_localctx, predIndex);
}
return true;
@@ -7694,7 +7753,7 @@ private boolean joinTarget_sempred(JoinTargetContext _localctx, int predIndex) {
}
public static final String _serializedATN =
- "\u0004\u0001\u0087\u036d\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001"+
+ "\u0004\u0001\u0087\u0374\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001"+
"\u0002\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004"+
"\u0002\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007"+
"\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b"+
@@ -7715,528 +7774,531 @@ private boolean joinTarget_sempred(JoinTargetContext _localctx, int predIndex) {
"E\u0002F\u0007F\u0002G\u0007G\u0002H\u0007H\u0002I\u0007I\u0002J\u0007"+
"J\u0002K\u0007K\u0002L\u0007L\u0002M\u0007M\u0002N\u0007N\u0002O\u0007"+
"O\u0002P\u0007P\u0002Q\u0007Q\u0002R\u0007R\u0002S\u0007S\u0002T\u0007"+
- "T\u0002U\u0007U\u0002V\u0007V\u0002W\u0007W\u0001\u0000\u0001\u0000\u0001"+
- "\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+
- "\u0001\u0005\u0001\u00ba\b\u0001\n\u0001\f\u0001\u00bd\t\u0001\u0001\u0002"+
- "\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002"+
- "\u0003\u0002\u00c6\b\u0002\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003"+
- "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003"+
- "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003"+
- "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003"+
- "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0003\u0003\u00e2\b\u0003"+
- "\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005\u0001\u0006"+
- "\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\u0007\u0005\u0007"+
- "\u00ef\b\u0007\n\u0007\f\u0007\u00f2\t\u0007\u0001\b\u0001\b\u0001\b\u0003"+
- "\b\u00f7\b\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001\t\u0005\t\u00fe\b\t"+
- "\n\t\f\t\u0101\t\t\u0001\n\u0001\n\u0001\n\u0003\n\u0106\b\n\u0001\u000b"+
- "\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001"+
- "\r\u0005\r\u0111\b\r\n\r\f\r\u0114\t\r\u0001\r\u0003\r\u0117\b\r\u0001"+
- "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001"+
- "\u000e\u0001\u000e\u0001\u000e\u0003\u000e\u0122\b\u000e\u0001\u000f\u0001"+
- "\u000f\u0001\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001\u0012\u0001"+
- "\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0005\u0013\u0130"+
- "\b\u0013\n\u0013\f\u0013\u0133\t\u0013\u0001\u0014\u0001\u0014\u0001\u0014"+
- "\u0001\u0015\u0001\u0015\u0003\u0015\u013a\b\u0015\u0001\u0015\u0001\u0015"+
- "\u0003\u0015\u013e\b\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0005\u0016"+
- "\u0143\b\u0016\n\u0016\f\u0016\u0146\t\u0016\u0001\u0017\u0001\u0017\u0001"+
- "\u0017\u0003\u0017\u014b\b\u0017\u0001\u0018\u0001\u0018\u0001\u0018\u0003"+
- "\u0018\u0150\b\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001"+
- "\u0018\u0001\u0018\u0001\u0018\u0003\u0018\u0159\b\u0018\u0001\u0019\u0001"+
- "\u0019\u0001\u0019\u0005\u0019\u015e\b\u0019\n\u0019\f\u0019\u0161\t\u0019"+
- "\u0001\u001a\u0001\u001a\u0001\u001a\u0003\u001a\u0166\b\u001a\u0001\u001a"+
- "\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a"+
- "\u0003\u001a\u016f\b\u001a\u0001\u001b\u0001\u001b\u0001\u001b\u0005\u001b"+
- "\u0174\b\u001b\n\u001b\f\u001b\u0177\t\u001b\u0001\u001c\u0001\u001c\u0001"+
- "\u001c\u0005\u001c\u017c\b\u001c\n\u001c\f\u001c\u017f\t\u001c\u0001\u001d"+
- "\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e\u0003\u001e\u0186\b\u001e"+
- "\u0001\u001f\u0001\u001f\u0003\u001f\u018a\b\u001f\u0001 \u0001 \u0003"+
- " \u018e\b \u0001!\u0001!\u0001!\u0003!\u0193\b!\u0001\"\u0001\"\u0001"+
- "\"\u0001#\u0001#\u0001#\u0001#\u0005#\u019c\b#\n#\f#\u019f\t#\u0001$\u0001"+
- "$\u0003$\u01a3\b$\u0001$\u0001$\u0003$\u01a7\b$\u0001%\u0001%\u0001%\u0001"+
- "&\u0001&\u0001&\u0001\'\u0001\'\u0001\'\u0001\'\u0005\'\u01b3\b\'\n\'"+
- "\f\'\u01b6\t\'\u0001(\u0001(\u0001(\u0001(\u0001(\u0001(\u0001(\u0001"+
- "(\u0003(\u01c0\b(\u0001)\u0001)\u0001)\u0001)\u0003)\u01c6\b)\u0001*\u0001"+
- "*\u0001*\u0005*\u01cb\b*\n*\f*\u01ce\t*\u0001+\u0001+\u0001+\u0001+\u0001"+
- ",\u0001,\u0003,\u01d6\b,\u0001-\u0001-\u0001-\u0001-\u0001.\u0001.\u0001"+
- ".\u0001/\u0001/\u0001/\u00010\u00010\u00010\u00010\u00011\u00011\u0001"+
- "1\u00012\u00012\u00012\u00012\u00032\u01ed\b2\u00012\u00012\u00012\u0001"+
- "2\u00052\u01f3\b2\n2\f2\u01f6\t2\u00032\u01f8\b2\u00013\u00013\u00014"+
- "\u00014\u00014\u00034\u01ff\b4\u00014\u00014\u00015\u00015\u00015\u0001"+
- "6\u00016\u00016\u00016\u00036\u020a\b6\u00016\u00016\u00016\u00016\u0001"+
- "6\u00036\u0211\b6\u00017\u00017\u00017\u00018\u00048\u0217\b8\u000b8\f"+
- "8\u0218\u00019\u00019\u00019\u00019\u0001:\u0001:\u0001:\u0001:\u0001"+
- ":\u0001:\u0005:\u0225\b:\n:\f:\u0228\t:\u0001;\u0001;\u0001<\u0001<\u0001"+
- "<\u0001<\u0003<\u0230\b<\u0001<\u0001<\u0001<\u0001<\u0001<\u0001=\u0001"+
- "=\u0001=\u0001=\u0003=\u023b\b=\u0001=\u0001=\u0001=\u0001>\u0001>\u0001"+
- ">\u0001>\u0001>\u0001?\u0001?\u0001?\u0001?\u0003?\u0249\b?\u0001@\u0001"+
- "@\u0001@\u0001A\u0001A\u0001B\u0001B\u0001B\u0001B\u0001B\u0001B\u0001"+
- "B\u0003B\u0257\bB\u0001B\u0001B\u0001B\u0001B\u0001B\u0005B\u025e\bB\n"+
- "B\fB\u0261\tB\u0001B\u0001B\u0001B\u0001B\u0001B\u0003B\u0268\bB\u0001"+
- "B\u0001B\u0001B\u0003B\u026d\bB\u0001B\u0001B\u0001B\u0001B\u0001B\u0001"+
- "B\u0005B\u0275\bB\nB\fB\u0278\tB\u0001C\u0001C\u0003C\u027c\bC\u0001C"+
- "\u0001C\u0001C\u0001C\u0001C\u0003C\u0283\bC\u0001C\u0001C\u0001C\u0001"+
- "C\u0001C\u0003C\u028a\bC\u0001C\u0001C\u0001C\u0001C\u0001C\u0005C\u0291"+
- "\bC\nC\fC\u0294\tC\u0001C\u0001C\u0001C\u0001C\u0003C\u029a\bC\u0001C"+
- "\u0001C\u0001C\u0001C\u0001C\u0005C\u02a1\bC\nC\fC\u02a4\tC\u0001C\u0001"+
- "C\u0003C\u02a8\bC\u0001D\u0001D\u0001D\u0003D\u02ad\bD\u0001D\u0001D\u0001"+
- "D\u0001E\u0001E\u0003E\u02b4\bE\u0001F\u0001F\u0001F\u0001F\u0001G\u0001"+
- "G\u0001G\u0001G\u0003G\u02be\bG\u0001G\u0001G\u0001G\u0001G\u0001G\u0001"+
- "G\u0005G\u02c6\bG\nG\fG\u02c9\tG\u0001H\u0001H\u0001H\u0001H\u0001H\u0001"+
- "H\u0001H\u0001H\u0003H\u02d3\bH\u0001H\u0001H\u0001H\u0005H\u02d8\bH\n"+
- "H\fH\u02db\tH\u0001I\u0001I\u0001I\u0001I\u0001I\u0001I\u0005I\u02e3\b"+
- "I\nI\fI\u02e6\tI\u0001I\u0001I\u0003I\u02ea\bI\u0003I\u02ec\bI\u0001I"+
- "\u0001I\u0001J\u0001J\u0001J\u0003J\u02f3\bJ\u0001K\u0001K\u0001K\u0001"+
- "K\u0005K\u02f9\bK\nK\fK\u02fc\tK\u0003K\u02fe\bK\u0001K\u0001K\u0001L"+
- "\u0001L\u0001L\u0001L\u0001M\u0001M\u0003M\u0308\bM\u0001N\u0001N\u0001"+
- "N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001"+
- "N\u0005N\u0317\bN\nN\fN\u031a\tN\u0001N\u0001N\u0001N\u0001N\u0001N\u0001"+
- "N\u0005N\u0322\bN\nN\fN\u0325\tN\u0001N\u0001N\u0001N\u0001N\u0001N\u0001"+
- "N\u0005N\u032d\bN\nN\fN\u0330\tN\u0001N\u0001N\u0003N\u0334\bN\u0001O"+
- "\u0001O\u0001P\u0001P\u0003P\u033a\bP\u0001Q\u0003Q\u033d\bQ\u0001Q\u0001"+
- "Q\u0001R\u0003R\u0342\bR\u0001R\u0001R\u0001S\u0001S\u0001T\u0001T\u0001"+
- "U\u0001U\u0001U\u0001U\u0001U\u0001V\u0001V\u0001V\u0003V\u0352\bV\u0001"+
- "V\u0001V\u0001V\u0003V\u0357\bV\u0001W\u0001W\u0001W\u0001W\u0005W\u035d"+
- "\bW\nW\fW\u0360\tW\u0001W\u0001W\u0001W\u0001W\u0005W\u0366\bW\nW\fW\u0369"+
- "\tW\u0003W\u036b\bW\u0001W\u0000\u0005\u0002t\u0084\u008e\u0090X\u0000"+
- "\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u001a\u001c"+
- "\u001e \"$&(*,.02468:<>@BDFHJLNPRTVXZ\\^`bdfhjlnprtvxz|~\u0080\u0082\u0084"+
- "\u0086\u0088\u008a\u008c\u008e\u0090\u0092\u0094\u0096\u0098\u009a\u009c"+
- "\u009e\u00a0\u00a2\u00a4\u00a6\u00a8\u00aa\u00ac\u00ae\u0000\n\u0002\u0000"+
- "11gg\u0001\u0000ab\u0002\u000055;;\u0002\u0000>>AA\u0002\u0000&&11\u0001"+
- "\u0000ST\u0001\u0000UW\u0002\u0000==JJ\u0002\u0000LLNR\u0002\u0000\u0017"+
- "\u0017\u0019\u001a\u0394\u0000\u00b0\u0001\u0000\u0000\u0000\u0002\u00b3"+
- "\u0001\u0000\u0000\u0000\u0004\u00c5\u0001\u0000\u0000\u0000\u0006\u00e1"+
- "\u0001\u0000\u0000\u0000\b\u00e3\u0001\u0000\u0000\u0000\n\u00e6\u0001"+
- "\u0000\u0000\u0000\f\u00e8\u0001\u0000\u0000\u0000\u000e\u00eb\u0001\u0000"+
- "\u0000\u0000\u0010\u00f6\u0001\u0000\u0000\u0000\u0012\u00fa\u0001\u0000"+
- "\u0000\u0000\u0014\u0102\u0001\u0000\u0000\u0000\u0016\u0107\u0001\u0000"+
- "\u0000\u0000\u0018\u010a\u0001\u0000\u0000\u0000\u001a\u010d\u0001\u0000"+
- "\u0000\u0000\u001c\u0121\u0001\u0000\u0000\u0000\u001e\u0123\u0001\u0000"+
- "\u0000\u0000 \u0125\u0001\u0000\u0000\u0000\"\u0127\u0001\u0000\u0000"+
- "\u0000$\u0129\u0001\u0000\u0000\u0000&\u012b\u0001\u0000\u0000\u0000("+
- "\u0134\u0001\u0000\u0000\u0000*\u0137\u0001\u0000\u0000\u0000,\u013f\u0001"+
- "\u0000\u0000\u0000.\u0147\u0001\u0000\u0000\u00000\u0158\u0001\u0000\u0000"+
- "\u00002\u015a\u0001\u0000\u0000\u00004\u016e\u0001\u0000\u0000\u00006"+
- "\u0170\u0001\u0000\u0000\u00008\u0178\u0001\u0000\u0000\u0000:\u0180\u0001"+
- "\u0000\u0000\u0000<\u0185\u0001\u0000\u0000\u0000>\u0189\u0001\u0000\u0000"+
- "\u0000@\u018d\u0001\u0000\u0000\u0000B\u0192\u0001\u0000\u0000\u0000D"+
- "\u0194\u0001\u0000\u0000\u0000F\u0197\u0001\u0000\u0000\u0000H\u01a0\u0001"+
- "\u0000\u0000\u0000J\u01a8\u0001\u0000\u0000\u0000L\u01ab\u0001\u0000\u0000"+
- "\u0000N\u01ae\u0001\u0000\u0000\u0000P\u01bf\u0001\u0000\u0000\u0000R"+
- "\u01c1\u0001\u0000\u0000\u0000T\u01c7\u0001\u0000\u0000\u0000V\u01cf\u0001"+
- "\u0000\u0000\u0000X\u01d5\u0001\u0000\u0000\u0000Z\u01d7\u0001\u0000\u0000"+
- "\u0000\\\u01db\u0001\u0000\u0000\u0000^\u01de\u0001\u0000\u0000\u0000"+
- "`\u01e1\u0001\u0000\u0000\u0000b\u01e5\u0001\u0000\u0000\u0000d\u01e8"+
- "\u0001\u0000\u0000\u0000f\u01f9\u0001\u0000\u0000\u0000h\u01fe\u0001\u0000"+
- "\u0000\u0000j\u0202\u0001\u0000\u0000\u0000l\u0205\u0001\u0000\u0000\u0000"+
- "n\u0212\u0001\u0000\u0000\u0000p\u0216\u0001\u0000\u0000\u0000r\u021a"+
- "\u0001\u0000\u0000\u0000t\u021e\u0001\u0000\u0000\u0000v\u0229\u0001\u0000"+
- "\u0000\u0000x\u022b\u0001\u0000\u0000\u0000z\u0236\u0001\u0000\u0000\u0000"+
- "|\u023f\u0001\u0000\u0000\u0000~\u0244\u0001\u0000\u0000\u0000\u0080\u024a"+
- "\u0001\u0000\u0000\u0000\u0082\u024d\u0001\u0000\u0000\u0000\u0084\u026c"+
- "\u0001\u0000\u0000\u0000\u0086\u02a7\u0001\u0000\u0000\u0000\u0088\u02a9"+
- "\u0001\u0000\u0000\u0000\u008a\u02b3\u0001\u0000\u0000\u0000\u008c\u02b5"+
- "\u0001\u0000\u0000\u0000\u008e\u02bd\u0001\u0000\u0000\u0000\u0090\u02d2"+
- "\u0001\u0000\u0000\u0000\u0092\u02dc\u0001\u0000\u0000\u0000\u0094\u02f2"+
- "\u0001\u0000\u0000\u0000\u0096\u02f4\u0001\u0000\u0000\u0000\u0098\u0301"+
- "\u0001\u0000\u0000\u0000\u009a\u0307\u0001\u0000\u0000\u0000\u009c\u0333"+
- "\u0001\u0000\u0000\u0000\u009e\u0335\u0001\u0000\u0000\u0000\u00a0\u0339"+
- "\u0001\u0000\u0000\u0000\u00a2\u033c\u0001\u0000\u0000\u0000\u00a4\u0341"+
- "\u0001\u0000\u0000\u0000\u00a6\u0345\u0001\u0000\u0000\u0000\u00a8\u0347"+
- "\u0001\u0000\u0000\u0000\u00aa\u0349\u0001\u0000\u0000\u0000\u00ac\u0356"+
- "\u0001\u0000\u0000\u0000\u00ae\u036a\u0001\u0000\u0000\u0000\u00b0\u00b1"+
- "\u0003\u0002\u0001\u0000\u00b1\u00b2\u0005\u0000\u0000\u0001\u00b2\u0001"+
- "\u0001\u0000\u0000\u0000\u00b3\u00b4\u0006\u0001\uffff\uffff\u0000\u00b4"+
- "\u00b5\u0003\u0004\u0002\u0000\u00b5\u00bb\u0001\u0000\u0000\u0000\u00b6"+
- "\u00b7\n\u0001\u0000\u0000\u00b7\u00b8\u00050\u0000\u0000\u00b8\u00ba"+
- "\u0003\u0006\u0003\u0000\u00b9\u00b6\u0001\u0000\u0000\u0000\u00ba\u00bd"+
- "\u0001\u0000\u0000\u0000\u00bb\u00b9\u0001\u0000\u0000\u0000\u00bb\u00bc"+
- "\u0001\u0000\u0000\u0000\u00bc\u0003\u0001\u0000\u0000\u0000\u00bd\u00bb"+
- "\u0001\u0000\u0000\u0000\u00be\u00c6\u0003\u0016\u000b\u0000\u00bf\u00c6"+
- "\u0003\f\u0006\u0000\u00c0\u00c6\u0003b1\u0000\u00c1\u00c2\u0004\u0002"+
- "\u0001\u0000\u00c2\u00c6\u0003\u0018\f\u0000\u00c3\u00c4\u0004\u0002\u0002"+
- "\u0000\u00c4\u00c6\u0003^/\u0000\u00c5\u00be\u0001\u0000\u0000\u0000\u00c5"+
- "\u00bf\u0001\u0000\u0000\u0000\u00c5\u00c0\u0001\u0000\u0000\u0000\u00c5"+
- "\u00c1\u0001\u0000\u0000\u0000\u00c5\u00c3\u0001\u0000\u0000\u0000\u00c6"+
- "\u0005\u0001\u0000\u0000\u0000\u00c7\u00e2\u0003(\u0014\u0000\u00c8\u00e2"+
- "\u0003\b\u0004\u0000\u00c9\u00e2\u0003J%\u0000\u00ca\u00e2\u0003D\"\u0000"+
- "\u00cb\u00e2\u0003*\u0015\u0000\u00cc\u00e2\u0003F#\u0000\u00cd\u00e2"+
- "\u0003L&\u0000\u00ce\u00e2\u0003N\'\u0000\u00cf\u00e2\u0003R)\u0000\u00d0"+
- "\u00e2\u0003Z-\u0000\u00d1\u00e2\u0003d2\u0000\u00d2\u00e2\u0003\\.\u0000"+
- "\u00d3\u00e2\u0003\u00aaU\u0000\u00d4\u00e2\u0003l6\u0000\u00d5\u00e2"+
- "\u0003z=\u0000\u00d6\u00e2\u0003j5\u0000\u00d7\u00e2\u0003n7\u0000\u00d8"+
- "\u00e2\u0003x<\u0000\u00d9\u00da\u0004\u0003\u0003\u0000\u00da\u00e2\u0003"+
- "~?\u0000\u00db\u00dc\u0004\u0003\u0004\u0000\u00dc\u00e2\u0003|>\u0000"+
- "\u00dd\u00de\u0004\u0003\u0005\u0000\u00de\u00e2\u0003\u0080@\u0000\u00df"+
- "\u00e0\u0004\u0003\u0006\u0000\u00e0\u00e2\u0003\u0082A\u0000\u00e1\u00c7"+
- "\u0001\u0000\u0000\u0000\u00e1\u00c8\u0001\u0000\u0000\u0000\u00e1\u00c9"+
- "\u0001\u0000\u0000\u0000\u00e1\u00ca\u0001\u0000\u0000\u0000\u00e1\u00cb"+
- "\u0001\u0000\u0000\u0000\u00e1\u00cc\u0001\u0000\u0000\u0000\u00e1\u00cd"+
- "\u0001\u0000\u0000\u0000\u00e1\u00ce\u0001\u0000\u0000\u0000\u00e1\u00cf"+
- "\u0001\u0000\u0000\u0000\u00e1\u00d0\u0001\u0000\u0000\u0000\u00e1\u00d1"+
- "\u0001\u0000\u0000\u0000\u00e1\u00d2\u0001\u0000\u0000\u0000\u00e1\u00d3"+
- "\u0001\u0000\u0000\u0000\u00e1\u00d4\u0001\u0000\u0000\u0000\u00e1\u00d5"+
- "\u0001\u0000\u0000\u0000\u00e1\u00d6\u0001\u0000\u0000\u0000\u00e1\u00d7"+
- "\u0001\u0000\u0000\u0000\u00e1\u00d8\u0001\u0000\u0000\u0000\u00e1\u00d9"+
- "\u0001\u0000\u0000\u0000\u00e1\u00db\u0001\u0000\u0000\u0000\u00e1\u00dd"+
- "\u0001\u0000\u0000\u0000\u00e1\u00df\u0001\u0000\u0000\u0000\u00e2\u0007"+
- "\u0001\u0000\u0000\u0000\u00e3\u00e4\u0005\u0011\u0000\u0000\u00e4\u00e5"+
- "\u0003\u0084B\u0000\u00e5\t\u0001\u0000\u0000\u0000\u00e6\u00e7\u0003"+
- ":\u001d\u0000\u00e7\u000b\u0001\u0000\u0000\u0000\u00e8\u00e9\u0005\r"+
- "\u0000\u0000\u00e9\u00ea\u0003\u000e\u0007\u0000\u00ea\r\u0001\u0000\u0000"+
- "\u0000\u00eb\u00f0\u0003\u0010\b\u0000\u00ec\u00ed\u0005:\u0000\u0000"+
- "\u00ed\u00ef\u0003\u0010\b\u0000\u00ee\u00ec\u0001\u0000\u0000\u0000\u00ef"+
- "\u00f2\u0001\u0000\u0000\u0000\u00f0\u00ee\u0001\u0000\u0000\u0000\u00f0"+
- "\u00f1\u0001\u0000\u0000\u0000\u00f1\u000f\u0001\u0000\u0000\u0000\u00f2"+
- "\u00f0\u0001\u0000\u0000\u0000\u00f3\u00f4\u00030\u0018\u0000\u00f4\u00f5"+
- "\u00056\u0000\u0000\u00f5\u00f7\u0001\u0000\u0000\u0000\u00f6\u00f3\u0001"+
- "\u0000\u0000\u0000\u00f6\u00f7\u0001\u0000\u0000\u0000\u00f7\u00f8\u0001"+
- "\u0000\u0000\u0000\u00f8\u00f9\u0003\u0084B\u0000\u00f9\u0011\u0001\u0000"+
- "\u0000\u0000\u00fa\u00ff\u0003\u0014\n\u0000\u00fb\u00fc\u0005:\u0000"+
- "\u0000\u00fc\u00fe\u0003\u0014\n\u0000\u00fd\u00fb\u0001\u0000\u0000\u0000"+
- "\u00fe\u0101\u0001\u0000\u0000\u0000\u00ff\u00fd\u0001\u0000\u0000\u0000"+
- "\u00ff\u0100\u0001\u0000\u0000\u0000\u0100\u0013\u0001\u0000\u0000\u0000"+
- "\u0101\u00ff\u0001\u0000\u0000\u0000\u0102\u0105\u00030\u0018\u0000\u0103"+
- "\u0104\u00056\u0000\u0000\u0104\u0106\u0003\u0084B\u0000\u0105\u0103\u0001"+
- "\u0000\u0000\u0000\u0105\u0106\u0001\u0000\u0000\u0000\u0106\u0015\u0001"+
- "\u0000\u0000\u0000\u0107\u0108\u0005\u0013\u0000\u0000\u0108\u0109\u0003"+
- "\u001a\r\u0000\u0109\u0017\u0001\u0000\u0000\u0000\u010a\u010b\u0005\u0014"+
- "\u0000\u0000\u010b\u010c\u0003\u001a\r\u0000\u010c\u0019\u0001\u0000\u0000"+
- "\u0000\u010d\u0112\u0003\u001c\u000e\u0000\u010e\u010f\u0005:\u0000\u0000"+
- "\u010f\u0111\u0003\u001c\u000e\u0000\u0110\u010e\u0001\u0000\u0000\u0000"+
- "\u0111\u0114\u0001\u0000\u0000\u0000\u0112\u0110\u0001\u0000\u0000\u0000"+
- "\u0112\u0113\u0001\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000"+
- "\u0114\u0112\u0001\u0000\u0000\u0000\u0115\u0117\u0003&\u0013\u0000\u0116"+
- "\u0115\u0001\u0000\u0000\u0000\u0116\u0117\u0001\u0000\u0000\u0000\u0117"+
- "\u001b\u0001\u0000\u0000\u0000\u0118\u0119\u0003\u001e\u000f\u0000\u0119"+
- "\u011a\u00059\u0000\u0000\u011a\u011b\u0003\"\u0011\u0000\u011b\u0122"+
- "\u0001\u0000\u0000\u0000\u011c\u011d\u0003\"\u0011\u0000\u011d\u011e\u0005"+
- "8\u0000\u0000\u011e\u011f\u0003 \u0010\u0000\u011f\u0122\u0001\u0000\u0000"+
- "\u0000\u0120\u0122\u0003$\u0012\u0000\u0121\u0118\u0001\u0000\u0000\u0000"+
- "\u0121\u011c\u0001\u0000\u0000\u0000\u0121\u0120\u0001\u0000\u0000\u0000"+
- "\u0122\u001d\u0001\u0000\u0000\u0000\u0123\u0124\u0005g\u0000\u0000\u0124"+
- "\u001f\u0001\u0000\u0000\u0000\u0125\u0126\u0005g\u0000\u0000\u0126!\u0001"+
- "\u0000\u0000\u0000\u0127\u0128\u0005g\u0000\u0000\u0128#\u0001\u0000\u0000"+
- "\u0000\u0129\u012a\u0007\u0000\u0000\u0000\u012a%\u0001\u0000\u0000\u0000"+
- "\u012b\u012c\u0005f\u0000\u0000\u012c\u0131\u0005g\u0000\u0000\u012d\u012e"+
- "\u0005:\u0000\u0000\u012e\u0130\u0005g\u0000\u0000\u012f\u012d\u0001\u0000"+
- "\u0000\u0000\u0130\u0133\u0001\u0000\u0000\u0000\u0131\u012f\u0001\u0000"+
- "\u0000\u0000\u0131\u0132\u0001\u0000\u0000\u0000\u0132\'\u0001\u0000\u0000"+
- "\u0000\u0133\u0131\u0001\u0000\u0000\u0000\u0134\u0135\u0005\t\u0000\u0000"+
- "\u0135\u0136\u0003\u000e\u0007\u0000\u0136)\u0001\u0000\u0000\u0000\u0137"+
- "\u0139\u0005\u0010\u0000\u0000\u0138\u013a\u0003,\u0016\u0000\u0139\u0138"+
- "\u0001\u0000\u0000\u0000\u0139\u013a\u0001\u0000\u0000\u0000\u013a\u013d"+
- "\u0001\u0000\u0000\u0000\u013b\u013c\u00057\u0000\u0000\u013c\u013e\u0003"+
- "\u000e\u0007\u0000\u013d\u013b\u0001\u0000\u0000\u0000\u013d\u013e\u0001"+
- "\u0000\u0000\u0000\u013e+\u0001\u0000\u0000\u0000\u013f\u0144\u0003.\u0017"+
- "\u0000\u0140\u0141\u0005:\u0000\u0000\u0141\u0143\u0003.\u0017\u0000\u0142"+
- "\u0140\u0001\u0000\u0000\u0000\u0143\u0146\u0001\u0000\u0000\u0000\u0144"+
- "\u0142\u0001\u0000\u0000\u0000\u0144\u0145\u0001\u0000\u0000\u0000\u0145"+
- "-\u0001\u0000\u0000\u0000\u0146\u0144\u0001\u0000\u0000\u0000\u0147\u014a"+
- "\u0003\u0010\b\u0000\u0148\u0149\u0005\u0011\u0000\u0000\u0149\u014b\u0003"+
- "\u0084B\u0000\u014a\u0148\u0001\u0000\u0000\u0000\u014a\u014b\u0001\u0000"+
- "\u0000\u0000\u014b/\u0001\u0000\u0000\u0000\u014c\u014d\u0004\u0018\u0007"+
- "\u0000\u014d\u014f\u0005]\u0000\u0000\u014e\u0150\u0005a\u0000\u0000\u014f"+
- "\u014e\u0001\u0000\u0000\u0000\u014f\u0150\u0001\u0000\u0000\u0000\u0150"+
- "\u0151\u0001\u0000\u0000\u0000\u0151\u0152\u0005^\u0000\u0000\u0152\u0153"+
- "\u0005<\u0000\u0000\u0153\u0154\u0005]\u0000\u0000\u0154\u0155\u00032"+
- "\u0019\u0000\u0155\u0156\u0005^\u0000\u0000\u0156\u0159\u0001\u0000\u0000"+
- "\u0000\u0157\u0159\u00032\u0019\u0000\u0158\u014c\u0001\u0000\u0000\u0000"+
- "\u0158\u0157\u0001\u0000\u0000\u0000\u01591\u0001\u0000\u0000\u0000\u015a"+
- "\u015f\u0003B!\u0000\u015b\u015c\u0005<\u0000\u0000\u015c\u015e\u0003"+
- "B!\u0000\u015d\u015b\u0001\u0000\u0000\u0000\u015e\u0161\u0001\u0000\u0000"+
- "\u0000\u015f\u015d\u0001\u0000\u0000\u0000\u015f\u0160\u0001\u0000\u0000"+
- "\u0000\u01603\u0001\u0000\u0000\u0000\u0161\u015f\u0001\u0000\u0000\u0000"+
- "\u0162\u0163\u0004\u001a\b\u0000\u0163\u0165\u0005]\u0000\u0000\u0164"+
- "\u0166\u0005|\u0000\u0000\u0165\u0164\u0001\u0000\u0000\u0000\u0165\u0166"+
- "\u0001\u0000\u0000\u0000\u0166\u0167\u0001\u0000\u0000\u0000\u0167\u0168"+
- "\u0005^\u0000\u0000\u0168\u0169\u0005<\u0000\u0000\u0169\u016a\u0005]"+
- "\u0000\u0000\u016a\u016b\u00036\u001b\u0000\u016b\u016c\u0005^\u0000\u0000"+
- "\u016c\u016f\u0001\u0000\u0000\u0000\u016d\u016f\u00036\u001b\u0000\u016e"+
- "\u0162\u0001\u0000\u0000\u0000\u016e\u016d\u0001\u0000\u0000\u0000\u016f"+
- "5\u0001\u0000\u0000\u0000\u0170\u0175\u0003<\u001e\u0000\u0171\u0172\u0005"+
- "<\u0000\u0000\u0172\u0174\u0003<\u001e\u0000\u0173\u0171\u0001\u0000\u0000"+
- "\u0000\u0174\u0177\u0001\u0000\u0000\u0000\u0175\u0173\u0001\u0000\u0000"+
- "\u0000\u0175\u0176\u0001\u0000\u0000\u0000\u01767\u0001\u0000\u0000\u0000"+
- "\u0177\u0175\u0001\u0000\u0000\u0000\u0178\u017d\u00034\u001a\u0000\u0179"+
- "\u017a\u0005:\u0000\u0000\u017a\u017c\u00034\u001a\u0000\u017b\u0179\u0001"+
- "\u0000\u0000\u0000\u017c\u017f\u0001\u0000\u0000\u0000\u017d\u017b\u0001"+
- "\u0000\u0000\u0000\u017d\u017e\u0001\u0000\u0000\u0000\u017e9\u0001\u0000"+
- "\u0000\u0000\u017f\u017d\u0001\u0000\u0000\u0000\u0180\u0181\u0007\u0001"+
- "\u0000\u0000\u0181;\u0001\u0000\u0000\u0000\u0182\u0186\u0005|\u0000\u0000"+
- "\u0183\u0186\u0003>\u001f\u0000\u0184\u0186\u0003@ \u0000\u0185\u0182"+
- "\u0001\u0000\u0000\u0000\u0185\u0183\u0001\u0000\u0000\u0000\u0185\u0184"+
- "\u0001\u0000\u0000\u0000\u0186=\u0001\u0000\u0000\u0000\u0187\u018a\u0005"+
- "H\u0000\u0000\u0188\u018a\u0005[\u0000\u0000\u0189\u0187\u0001\u0000\u0000"+
- "\u0000\u0189\u0188\u0001\u0000\u0000\u0000\u018a?\u0001\u0000\u0000\u0000"+
- "\u018b\u018e\u0005Z\u0000\u0000\u018c\u018e\u0005\\\u0000\u0000\u018d"+
- "\u018b\u0001\u0000\u0000\u0000\u018d\u018c\u0001\u0000\u0000\u0000\u018e"+
- "A\u0001\u0000\u0000\u0000\u018f\u0193\u0003:\u001d\u0000\u0190\u0193\u0003"+
- ">\u001f\u0000\u0191\u0193\u0003@ \u0000\u0192\u018f\u0001\u0000\u0000"+
- "\u0000\u0192\u0190\u0001\u0000\u0000\u0000\u0192\u0191\u0001\u0000\u0000"+
- "\u0000\u0193C\u0001\u0000\u0000\u0000\u0194\u0195\u0005\u000b\u0000\u0000"+
- "\u0195\u0196\u0003\u009cN\u0000\u0196E\u0001\u0000\u0000\u0000\u0197\u0198"+
- "\u0005\u000f\u0000\u0000\u0198\u019d\u0003H$\u0000\u0199\u019a\u0005:"+
- "\u0000\u0000\u019a\u019c\u0003H$\u0000\u019b\u0199\u0001\u0000\u0000\u0000"+
- "\u019c\u019f\u0001\u0000\u0000\u0000\u019d\u019b\u0001\u0000\u0000\u0000"+
- "\u019d\u019e\u0001\u0000\u0000\u0000\u019eG\u0001\u0000\u0000\u0000\u019f"+
- "\u019d\u0001\u0000\u0000\u0000\u01a0\u01a2\u0003\u0084B\u0000\u01a1\u01a3"+
- "\u0007\u0002\u0000\u0000\u01a2\u01a1\u0001\u0000\u0000\u0000\u01a2\u01a3"+
- "\u0001\u0000\u0000\u0000\u01a3\u01a6\u0001\u0000\u0000\u0000\u01a4\u01a5"+
- "\u0005E\u0000\u0000\u01a5\u01a7\u0007\u0003\u0000\u0000\u01a6\u01a4\u0001"+
- "\u0000\u0000\u0000\u01a6\u01a7\u0001\u0000\u0000\u0000\u01a7I\u0001\u0000"+
- "\u0000\u0000\u01a8\u01a9\u0005\u001e\u0000\u0000\u01a9\u01aa\u00038\u001c"+
- "\u0000\u01aaK\u0001\u0000\u0000\u0000\u01ab\u01ac\u0005\u001d\u0000\u0000"+
- "\u01ac\u01ad\u00038\u001c\u0000\u01adM\u0001\u0000\u0000\u0000\u01ae\u01af"+
- "\u0005 \u0000\u0000\u01af\u01b4\u0003P(\u0000\u01b0\u01b1\u0005:\u0000"+
- "\u0000\u01b1\u01b3\u0003P(\u0000\u01b2\u01b0\u0001\u0000\u0000\u0000\u01b3"+
- "\u01b6\u0001\u0000\u0000\u0000\u01b4\u01b2\u0001\u0000\u0000\u0000\u01b4"+
- "\u01b5\u0001\u0000\u0000\u0000\u01b5O\u0001\u0000\u0000\u0000\u01b6\u01b4"+
- "\u0001\u0000\u0000\u0000\u01b7\u01b8\u00034\u001a\u0000\u01b8\u01b9\u0005"+
- "\u0080\u0000\u0000\u01b9\u01ba\u00034\u001a\u0000\u01ba\u01c0\u0001\u0000"+
- "\u0000\u0000\u01bb\u01bc\u00034\u001a\u0000\u01bc\u01bd\u00056\u0000\u0000"+
- "\u01bd\u01be\u00034\u001a\u0000\u01be\u01c0\u0001\u0000\u0000\u0000\u01bf"+
- "\u01b7\u0001\u0000\u0000\u0000\u01bf\u01bb\u0001\u0000\u0000\u0000\u01c0"+
- "Q\u0001\u0000\u0000\u0000\u01c1\u01c2\u0005\b\u0000\u0000\u01c2\u01c3"+
- "\u0003\u0090H\u0000\u01c3\u01c5\u0003\u00a6S\u0000\u01c4\u01c6\u0003T"+
- "*\u0000\u01c5\u01c4\u0001\u0000\u0000\u0000\u01c5\u01c6\u0001\u0000\u0000"+
- "\u0000\u01c6S\u0001\u0000\u0000\u0000\u01c7\u01cc\u0003V+\u0000\u01c8"+
- "\u01c9\u0005:\u0000\u0000\u01c9\u01cb\u0003V+\u0000\u01ca\u01c8\u0001"+
- "\u0000\u0000\u0000\u01cb\u01ce\u0001\u0000\u0000\u0000\u01cc\u01ca\u0001"+
- "\u0000\u0000\u0000\u01cc\u01cd\u0001\u0000\u0000\u0000\u01cdU\u0001\u0000"+
- "\u0000\u0000\u01ce\u01cc\u0001\u0000\u0000\u0000\u01cf\u01d0\u0003:\u001d"+
- "\u0000\u01d0\u01d1\u00056\u0000\u0000\u01d1\u01d2\u0003\u009cN\u0000\u01d2"+
- "W\u0001\u0000\u0000\u0000\u01d3\u01d4\u0005K\u0000\u0000\u01d4\u01d6\u0003"+
- "\u0096K\u0000\u01d5\u01d3\u0001\u0000\u0000\u0000\u01d5\u01d6\u0001\u0000"+
- "\u0000\u0000\u01d6Y\u0001\u0000\u0000\u0000\u01d7\u01d8\u0005\n\u0000"+
- "\u0000\u01d8\u01d9\u0003\u0090H\u0000\u01d9\u01da\u0003\u00a6S\u0000\u01da"+
- "[\u0001\u0000\u0000\u0000\u01db\u01dc\u0005\u001c\u0000\u0000\u01dc\u01dd"+
- "\u00030\u0018\u0000\u01dd]\u0001\u0000\u0000\u0000\u01de\u01df\u0005\u0006"+
- "\u0000\u0000\u01df\u01e0\u0003`0\u0000\u01e0_\u0001\u0000\u0000\u0000"+
- "\u01e1\u01e2\u0005_\u0000\u0000\u01e2\u01e3\u0003\u0002\u0001\u0000\u01e3"+
- "\u01e4\u0005`\u0000\u0000\u01e4a\u0001\u0000\u0000\u0000\u01e5\u01e6\u0005"+
- "!\u0000\u0000\u01e6\u01e7\u0005\u0084\u0000\u0000\u01e7c\u0001\u0000\u0000"+
- "\u0000\u01e8\u01e9\u0005\u0005\u0000\u0000\u01e9\u01ec\u0003f3\u0000\u01ea"+
- "\u01eb\u0005F\u0000\u0000\u01eb\u01ed\u00034\u001a\u0000\u01ec\u01ea\u0001"+
- "\u0000\u0000\u0000\u01ec\u01ed\u0001\u0000\u0000\u0000\u01ed\u01f7\u0001"+
- "\u0000\u0000\u0000\u01ee\u01ef\u0005K\u0000\u0000\u01ef\u01f4\u0003h4"+
- "\u0000\u01f0\u01f1\u0005:\u0000\u0000\u01f1\u01f3\u0003h4\u0000\u01f2"+
- "\u01f0\u0001\u0000\u0000\u0000\u01f3\u01f6\u0001\u0000\u0000\u0000\u01f4"+
- "\u01f2\u0001\u0000\u0000\u0000\u01f4\u01f5\u0001\u0000\u0000\u0000\u01f5"+
- "\u01f8\u0001\u0000\u0000\u0000\u01f6\u01f4\u0001\u0000\u0000\u0000\u01f7"+
- "\u01ee\u0001\u0000\u0000\u0000\u01f7\u01f8\u0001\u0000\u0000\u0000\u01f8"+
- "e\u0001\u0000\u0000\u0000\u01f9\u01fa\u0007\u0004\u0000\u0000\u01fag\u0001"+
- "\u0000\u0000\u0000\u01fb\u01fc\u00034\u001a\u0000\u01fc\u01fd\u00056\u0000"+
- "\u0000\u01fd\u01ff\u0001\u0000\u0000\u0000\u01fe\u01fb\u0001\u0000\u0000"+
- "\u0000\u01fe\u01ff\u0001\u0000\u0000\u0000\u01ff\u0200\u0001\u0000\u0000"+
- "\u0000\u0200\u0201\u00034\u001a\u0000\u0201i\u0001\u0000\u0000\u0000\u0202"+
- "\u0203\u0005\u000e\u0000\u0000\u0203\u0204\u0003\u009cN\u0000\u0204k\u0001"+
- "\u0000\u0000\u0000\u0205\u0206\u0005\u0004\u0000\u0000\u0206\u0209\u0003"+
- "0\u0018\u0000\u0207\u0208\u0005F\u0000\u0000\u0208\u020a\u00030\u0018"+
- "\u0000\u0209\u0207\u0001\u0000\u0000\u0000\u0209\u020a\u0001\u0000\u0000"+
- "\u0000\u020a\u0210\u0001\u0000\u0000\u0000\u020b\u020c\u0005\u0080\u0000"+
- "\u0000\u020c\u020d\u00030\u0018\u0000\u020d\u020e\u0005:\u0000\u0000\u020e"+
- "\u020f\u00030\u0018\u0000\u020f\u0211\u0001\u0000\u0000\u0000\u0210\u020b"+
- "\u0001\u0000\u0000\u0000\u0210\u0211\u0001\u0000\u0000\u0000\u0211m\u0001"+
- "\u0000\u0000\u0000\u0212\u0213\u0005\u0015\u0000\u0000\u0213\u0214\u0003"+
- "p8\u0000\u0214o\u0001\u0000\u0000\u0000\u0215\u0217\u0003r9\u0000\u0216"+
- "\u0215\u0001\u0000\u0000\u0000\u0217\u0218\u0001\u0000\u0000\u0000\u0218"+
- "\u0216\u0001\u0000\u0000\u0000\u0218\u0219\u0001\u0000\u0000\u0000\u0219"+
- "q\u0001\u0000\u0000\u0000\u021a\u021b\u0005_\u0000\u0000\u021b\u021c\u0003"+
- "t:\u0000\u021c\u021d\u0005`\u0000\u0000\u021ds\u0001\u0000\u0000\u0000"+
- "\u021e\u021f\u0006:\uffff\uffff\u0000\u021f\u0220\u0003v;\u0000\u0220"+
- "\u0226\u0001\u0000\u0000\u0000\u0221\u0222\n\u0001\u0000\u0000\u0222\u0223"+
- "\u00050\u0000\u0000\u0223\u0225\u0003v;\u0000\u0224\u0221\u0001\u0000"+
- "\u0000\u0000\u0225\u0228\u0001\u0000\u0000\u0000\u0226\u0224\u0001\u0000"+
- "\u0000\u0000\u0226\u0227\u0001\u0000\u0000\u0000\u0227u\u0001\u0000\u0000"+
- "\u0000\u0228\u0226\u0001\u0000\u0000\u0000\u0229\u022a\u0003\u0006\u0003"+
- "\u0000\u022aw\u0001\u0000\u0000\u0000\u022b\u022f\u0005\f\u0000\u0000"+
- "\u022c\u022d\u00030\u0018\u0000\u022d\u022e\u00056\u0000\u0000\u022e\u0230"+
- "\u0001\u0000\u0000\u0000\u022f\u022c\u0001\u0000\u0000\u0000\u022f\u0230"+
- "\u0001\u0000\u0000\u0000\u0230\u0231\u0001\u0000\u0000\u0000\u0231\u0232"+
- "\u0003\u009cN\u0000\u0232\u0233\u0005F\u0000\u0000\u0233\u0234\u0003\u0012"+
- "\t\u0000\u0234\u0235\u0003X,\u0000\u0235y\u0001\u0000\u0000\u0000\u0236"+
- "\u023a\u0005\u0007\u0000\u0000\u0237\u0238\u00030\u0018\u0000\u0238\u0239"+
- "\u00056\u0000\u0000\u0239\u023b\u0001\u0000\u0000\u0000\u023a\u0237\u0001"+
- "\u0000\u0000\u0000\u023a\u023b\u0001\u0000\u0000\u0000\u023b\u023c\u0001"+
- "\u0000\u0000\u0000\u023c\u023d\u0003\u0090H\u0000\u023d\u023e\u0003X,"+
- "\u0000\u023e{\u0001\u0000\u0000\u0000\u023f\u0240\u0005\u001b\u0000\u0000"+
- "\u0240\u0241\u0003\u001c\u000e\u0000\u0241\u0242\u0005F\u0000\u0000\u0242"+
- "\u0243\u00038\u001c\u0000\u0243}\u0001\u0000\u0000\u0000\u0244\u0245\u0005"+
- "\u0012\u0000\u0000\u0245\u0248\u0003,\u0016\u0000\u0246\u0247\u00057\u0000"+
- "\u0000\u0247\u0249\u0003\u000e\u0007\u0000\u0248\u0246\u0001\u0000\u0000"+
- "\u0000\u0248\u0249\u0001\u0000\u0000\u0000\u0249\u007f\u0001\u0000\u0000"+
- "\u0000\u024a\u024b\u0005\u001f\u0000\u0000\u024b\u024c\u00038\u001c\u0000"+
- "\u024c\u0081\u0001\u0000\u0000\u0000\u024d\u024e\u0005\u0016\u0000\u0000"+
- "\u024e\u0083\u0001\u0000\u0000\u0000\u024f\u0250\u0006B\uffff\uffff\u0000"+
- "\u0250\u0251\u0005C\u0000\u0000\u0251\u026d\u0003\u0084B\b\u0252\u026d"+
- "\u0003\u008aE\u0000\u0253\u026d\u0003\u0086C\u0000\u0254\u0256\u0003\u008a"+
- "E\u0000\u0255\u0257\u0005C\u0000\u0000\u0256\u0255\u0001\u0000\u0000\u0000"+
- "\u0256\u0257\u0001\u0000\u0000\u0000\u0257\u0258\u0001\u0000\u0000\u0000"+
- "\u0258\u0259\u0005?\u0000\u0000\u0259\u025a\u0005_\u0000\u0000\u025a\u025f"+
- "\u0003\u008aE\u0000\u025b\u025c\u0005:\u0000\u0000\u025c\u025e\u0003\u008a"+
- "E\u0000\u025d\u025b\u0001\u0000\u0000\u0000\u025e\u0261\u0001\u0000\u0000"+
- "\u0000\u025f\u025d\u0001\u0000\u0000\u0000\u025f\u0260\u0001\u0000\u0000"+
- "\u0000\u0260\u0262\u0001\u0000\u0000\u0000\u0261\u025f\u0001\u0000\u0000"+
- "\u0000\u0262\u0263\u0005`\u0000\u0000\u0263\u026d\u0001\u0000\u0000\u0000"+
- "\u0264\u0265\u0003\u008aE\u0000\u0265\u0267\u0005@\u0000\u0000\u0266\u0268"+
- "\u0005C\u0000\u0000\u0267\u0266\u0001\u0000\u0000\u0000\u0267\u0268\u0001"+
- "\u0000\u0000\u0000\u0268\u0269\u0001\u0000\u0000\u0000\u0269\u026a\u0005"+
- "D\u0000\u0000\u026a\u026d\u0001\u0000\u0000\u0000\u026b\u026d\u0003\u0088"+
- "D\u0000\u026c\u024f\u0001\u0000\u0000\u0000\u026c\u0252\u0001\u0000\u0000"+
- "\u0000\u026c\u0253\u0001\u0000\u0000\u0000\u026c\u0254\u0001\u0000\u0000"+
- "\u0000\u026c\u0264\u0001\u0000\u0000\u0000\u026c\u026b\u0001\u0000\u0000"+
- "\u0000\u026d\u0276\u0001\u0000\u0000\u0000\u026e\u026f\n\u0005\u0000\u0000"+
- "\u026f\u0270\u00054\u0000\u0000\u0270\u0275\u0003\u0084B\u0006\u0271\u0272"+
- "\n\u0004\u0000\u0000\u0272\u0273\u0005G\u0000\u0000\u0273\u0275\u0003"+
- "\u0084B\u0005\u0274\u026e\u0001\u0000\u0000\u0000\u0274\u0271\u0001\u0000"+
- "\u0000\u0000\u0275\u0278\u0001\u0000\u0000\u0000\u0276\u0274\u0001\u0000"+
- "\u0000\u0000\u0276\u0277\u0001\u0000\u0000\u0000\u0277\u0085\u0001\u0000"+
- "\u0000\u0000\u0278\u0276\u0001\u0000\u0000\u0000\u0279\u027b\u0003\u008a"+
- "E\u0000\u027a\u027c\u0005C\u0000\u0000\u027b\u027a\u0001\u0000\u0000\u0000"+
- "\u027b\u027c\u0001\u0000\u0000\u0000\u027c\u027d\u0001\u0000\u0000\u0000"+
- "\u027d\u027e\u0005B\u0000\u0000\u027e\u027f\u0003\u00a6S\u0000\u027f\u02a8"+
- "\u0001\u0000\u0000\u0000\u0280\u0282\u0003\u008aE\u0000\u0281\u0283\u0005"+
- "C\u0000\u0000\u0282\u0281\u0001\u0000\u0000\u0000\u0282\u0283\u0001\u0000"+
- "\u0000\u0000\u0283\u0284\u0001\u0000\u0000\u0000\u0284\u0285\u0005I\u0000"+
- "\u0000\u0285\u0286\u0003\u00a6S\u0000\u0286\u02a8\u0001\u0000\u0000\u0000"+
- "\u0287\u0289\u0003\u008aE\u0000\u0288\u028a\u0005C\u0000\u0000\u0289\u0288"+
- "\u0001\u0000\u0000\u0000\u0289\u028a\u0001\u0000\u0000\u0000\u028a\u028b"+
- "\u0001\u0000\u0000\u0000\u028b\u028c\u0005B\u0000\u0000\u028c\u028d\u0005"+
- "_\u0000\u0000\u028d\u0292\u0003\u00a6S\u0000\u028e\u028f\u0005:\u0000"+
- "\u0000\u028f\u0291\u0003\u00a6S\u0000\u0290\u028e\u0001\u0000\u0000\u0000"+
- "\u0291\u0294\u0001\u0000\u0000\u0000\u0292\u0290\u0001\u0000\u0000\u0000"+
- "\u0292\u0293\u0001\u0000\u0000\u0000\u0293\u0295\u0001\u0000\u0000\u0000"+
- "\u0294\u0292\u0001\u0000\u0000\u0000\u0295\u0296\u0005`\u0000\u0000\u0296"+
- "\u02a8\u0001\u0000\u0000\u0000\u0297\u0299\u0003\u008aE\u0000\u0298\u029a"+
- "\u0005C\u0000\u0000\u0299\u0298\u0001\u0000\u0000\u0000\u0299\u029a\u0001"+
- "\u0000\u0000\u0000\u029a\u029b\u0001\u0000\u0000\u0000\u029b\u029c\u0005"+
- "I\u0000\u0000\u029c\u029d\u0005_\u0000\u0000\u029d\u02a2\u0003\u00a6S"+
- "\u0000\u029e\u029f\u0005:\u0000\u0000\u029f\u02a1\u0003\u00a6S\u0000\u02a0"+
- "\u029e\u0001\u0000\u0000\u0000\u02a1\u02a4\u0001\u0000\u0000\u0000\u02a2"+
- "\u02a0\u0001\u0000\u0000\u0000\u02a2\u02a3\u0001\u0000\u0000\u0000\u02a3"+
- "\u02a5\u0001\u0000\u0000\u0000\u02a4\u02a2\u0001\u0000\u0000\u0000\u02a5"+
- "\u02a6\u0005`\u0000\u0000\u02a6\u02a8\u0001\u0000\u0000\u0000\u02a7\u0279"+
- "\u0001\u0000\u0000\u0000\u02a7\u0280\u0001\u0000\u0000\u0000\u02a7\u0287"+
- "\u0001\u0000\u0000\u0000\u02a7\u0297\u0001\u0000\u0000\u0000\u02a8\u0087"+
- "\u0001\u0000\u0000\u0000\u02a9\u02ac\u00030\u0018\u0000\u02aa\u02ab\u0005"+
- "8\u0000\u0000\u02ab\u02ad\u0003\n\u0005\u0000\u02ac\u02aa\u0001\u0000"+
- "\u0000\u0000\u02ac\u02ad\u0001\u0000\u0000\u0000\u02ad\u02ae\u0001\u0000"+
- "\u0000\u0000\u02ae\u02af\u00059\u0000\u0000\u02af\u02b0\u0003\u009cN\u0000"+
- "\u02b0\u0089\u0001\u0000\u0000\u0000\u02b1\u02b4\u0003\u008eG\u0000\u02b2"+
- "\u02b4\u0003\u008cF\u0000\u02b3\u02b1\u0001\u0000\u0000\u0000\u02b3\u02b2"+
- "\u0001\u0000\u0000\u0000\u02b4\u008b\u0001\u0000\u0000\u0000\u02b5\u02b6"+
- "\u0003\u008eG\u0000\u02b6\u02b7\u0003\u00a8T\u0000\u02b7\u02b8\u0003\u008e"+
- "G\u0000\u02b8\u008d\u0001\u0000\u0000\u0000\u02b9\u02ba\u0006G\uffff\uffff"+
- "\u0000\u02ba\u02be\u0003\u0090H\u0000\u02bb\u02bc\u0007\u0005\u0000\u0000"+
- "\u02bc\u02be\u0003\u008eG\u0003\u02bd\u02b9\u0001\u0000\u0000\u0000\u02bd"+
- "\u02bb\u0001\u0000\u0000\u0000\u02be\u02c7\u0001\u0000\u0000\u0000\u02bf"+
- "\u02c0\n\u0002\u0000\u0000\u02c0\u02c1\u0007\u0006\u0000\u0000\u02c1\u02c6"+
- "\u0003\u008eG\u0003\u02c2\u02c3\n\u0001\u0000\u0000\u02c3\u02c4\u0007"+
- "\u0005\u0000\u0000\u02c4\u02c6\u0003\u008eG\u0002\u02c5\u02bf\u0001\u0000"+
- "\u0000\u0000\u02c5\u02c2\u0001\u0000\u0000\u0000\u02c6\u02c9\u0001\u0000"+
- "\u0000\u0000\u02c7\u02c5\u0001\u0000\u0000\u0000\u02c7\u02c8\u0001\u0000"+
- "\u0000\u0000\u02c8\u008f\u0001\u0000\u0000\u0000\u02c9\u02c7\u0001\u0000"+
- "\u0000\u0000\u02ca\u02cb\u0006H\uffff\uffff\u0000\u02cb\u02d3\u0003\u009c"+
- "N\u0000\u02cc\u02d3\u00030\u0018\u0000\u02cd\u02d3\u0003\u0092I\u0000"+
- "\u02ce\u02cf\u0005_\u0000\u0000\u02cf\u02d0\u0003\u0084B\u0000\u02d0\u02d1"+
- "\u0005`\u0000\u0000\u02d1\u02d3\u0001\u0000\u0000\u0000\u02d2\u02ca\u0001"+
- "\u0000\u0000\u0000\u02d2\u02cc\u0001\u0000\u0000\u0000\u02d2\u02cd\u0001"+
- "\u0000\u0000\u0000\u02d2\u02ce\u0001\u0000\u0000\u0000\u02d3\u02d9\u0001"+
- "\u0000\u0000\u0000\u02d4\u02d5\n\u0001\u0000\u0000\u02d5\u02d6\u00058"+
- "\u0000\u0000\u02d6\u02d8\u0003\n\u0005\u0000\u02d7\u02d4\u0001\u0000\u0000"+
- "\u0000\u02d8\u02db\u0001\u0000\u0000\u0000\u02d9\u02d7\u0001\u0000\u0000"+
- "\u0000\u02d9\u02da\u0001\u0000\u0000\u0000\u02da\u0091\u0001\u0000\u0000"+
- "\u0000\u02db\u02d9\u0001\u0000\u0000\u0000\u02dc\u02dd\u0003\u0094J\u0000"+
- "\u02dd\u02eb\u0005_\u0000\u0000\u02de\u02ec\u0005U\u0000\u0000\u02df\u02e4"+
- "\u0003\u0084B\u0000\u02e0\u02e1\u0005:\u0000\u0000\u02e1\u02e3\u0003\u0084"+
- "B\u0000\u02e2\u02e0\u0001\u0000\u0000\u0000\u02e3\u02e6\u0001\u0000\u0000"+
- "\u0000\u02e4\u02e2\u0001\u0000\u0000\u0000\u02e4\u02e5\u0001\u0000\u0000"+
- "\u0000\u02e5\u02e9\u0001\u0000\u0000\u0000\u02e6\u02e4\u0001\u0000\u0000"+
- "\u0000\u02e7\u02e8\u0005:\u0000\u0000\u02e8\u02ea\u0003\u0096K\u0000\u02e9"+
- "\u02e7\u0001\u0000\u0000\u0000\u02e9\u02ea\u0001\u0000\u0000\u0000\u02ea"+
- "\u02ec\u0001\u0000\u0000\u0000\u02eb\u02de\u0001\u0000\u0000\u0000\u02eb"+
- "\u02df\u0001\u0000\u0000\u0000\u02eb\u02ec\u0001\u0000\u0000\u0000\u02ec"+
- "\u02ed\u0001\u0000\u0000\u0000\u02ed\u02ee\u0005`\u0000\u0000\u02ee\u0093"+
- "\u0001\u0000\u0000\u0000\u02ef\u02f3\u0003B!\u0000\u02f0\u02f3\u0005>"+
- "\u0000\u0000\u02f1\u02f3\u0005A\u0000\u0000\u02f2\u02ef\u0001\u0000\u0000"+
- "\u0000\u02f2\u02f0\u0001\u0000\u0000\u0000\u02f2\u02f1\u0001\u0000\u0000"+
- "\u0000\u02f3\u0095\u0001\u0000\u0000\u0000\u02f4\u02fd\u0005X\u0000\u0000"+
- "\u02f5\u02fa\u0003\u0098L\u0000\u02f6\u02f7\u0005:\u0000\u0000\u02f7\u02f9"+
- "\u0003\u0098L\u0000\u02f8\u02f6\u0001\u0000\u0000\u0000\u02f9\u02fc\u0001"+
- "\u0000\u0000\u0000\u02fa\u02f8\u0001\u0000\u0000\u0000\u02fa\u02fb\u0001"+
- "\u0000\u0000\u0000\u02fb\u02fe\u0001\u0000\u0000\u0000\u02fc\u02fa\u0001"+
- "\u0000\u0000\u0000\u02fd\u02f5\u0001\u0000\u0000\u0000\u02fd\u02fe\u0001"+
- "\u0000\u0000\u0000\u02fe\u02ff\u0001\u0000\u0000\u0000\u02ff\u0300\u0005"+
- "Y\u0000\u0000\u0300\u0097\u0001\u0000\u0000\u0000\u0301\u0302\u0003\u00a6"+
- "S\u0000\u0302\u0303\u00059\u0000\u0000\u0303\u0304\u0003\u009aM\u0000"+
- "\u0304\u0099\u0001\u0000\u0000\u0000\u0305\u0308\u0003\u009cN\u0000\u0306"+
- "\u0308\u0003\u0096K\u0000\u0307\u0305\u0001\u0000\u0000\u0000\u0307\u0306"+
- "\u0001\u0000\u0000\u0000\u0308\u009b\u0001\u0000\u0000\u0000\u0309\u0334"+
- "\u0005D\u0000\u0000\u030a\u030b\u0003\u00a4R\u0000\u030b\u030c\u0005a"+
- "\u0000\u0000\u030c\u0334\u0001\u0000\u0000\u0000\u030d\u0334\u0003\u00a2"+
- "Q\u0000\u030e\u0334\u0003\u00a4R\u0000\u030f\u0334\u0003\u009eO\u0000"+
- "\u0310\u0334\u0003>\u001f\u0000\u0311\u0334\u0003\u00a6S\u0000\u0312\u0313"+
- "\u0005]\u0000\u0000\u0313\u0318\u0003\u00a0P\u0000\u0314\u0315\u0005:"+
- "\u0000\u0000\u0315\u0317\u0003\u00a0P\u0000\u0316\u0314\u0001\u0000\u0000"+
- "\u0000\u0317\u031a\u0001\u0000\u0000\u0000\u0318\u0316\u0001\u0000\u0000"+
- "\u0000\u0318\u0319\u0001\u0000\u0000\u0000\u0319\u031b\u0001\u0000\u0000"+
- "\u0000\u031a\u0318\u0001\u0000\u0000\u0000\u031b\u031c\u0005^\u0000\u0000"+
- "\u031c\u0334\u0001\u0000\u0000\u0000\u031d\u031e\u0005]\u0000\u0000\u031e"+
- "\u0323\u0003\u009eO\u0000\u031f\u0320\u0005:\u0000\u0000\u0320\u0322\u0003"+
- "\u009eO\u0000\u0321\u031f\u0001\u0000\u0000\u0000\u0322\u0325\u0001\u0000"+
- "\u0000\u0000\u0323\u0321\u0001\u0000\u0000\u0000\u0323\u0324\u0001\u0000"+
- "\u0000\u0000\u0324\u0326\u0001\u0000\u0000\u0000\u0325\u0323\u0001\u0000"+
- "\u0000\u0000\u0326\u0327\u0005^\u0000\u0000\u0327\u0334\u0001\u0000\u0000"+
- "\u0000\u0328\u0329\u0005]\u0000\u0000\u0329\u032e\u0003\u00a6S\u0000\u032a"+
- "\u032b\u0005:\u0000\u0000\u032b\u032d\u0003\u00a6S\u0000\u032c\u032a\u0001"+
- "\u0000\u0000\u0000\u032d\u0330\u0001\u0000\u0000\u0000\u032e\u032c\u0001"+
- "\u0000\u0000\u0000\u032e\u032f\u0001\u0000\u0000\u0000\u032f\u0331\u0001"+
- "\u0000\u0000\u0000\u0330\u032e\u0001\u0000\u0000\u0000\u0331\u0332\u0005"+
- "^\u0000\u0000\u0332\u0334\u0001\u0000\u0000\u0000\u0333\u0309\u0001\u0000"+
- "\u0000\u0000\u0333\u030a\u0001\u0000\u0000\u0000\u0333\u030d\u0001\u0000"+
- "\u0000\u0000\u0333\u030e\u0001\u0000\u0000\u0000\u0333\u030f\u0001\u0000"+
- "\u0000\u0000\u0333\u0310\u0001\u0000\u0000\u0000\u0333\u0311\u0001\u0000"+
- "\u0000\u0000\u0333\u0312\u0001\u0000\u0000\u0000\u0333\u031d\u0001\u0000"+
- "\u0000\u0000\u0333\u0328\u0001\u0000\u0000\u0000\u0334\u009d\u0001\u0000"+
- "\u0000\u0000\u0335\u0336\u0007\u0007\u0000\u0000\u0336\u009f\u0001\u0000"+
- "\u0000\u0000\u0337\u033a\u0003\u00a2Q\u0000\u0338\u033a\u0003\u00a4R\u0000"+
- "\u0339\u0337\u0001\u0000\u0000\u0000\u0339\u0338\u0001\u0000\u0000\u0000"+
- "\u033a\u00a1\u0001\u0000\u0000\u0000\u033b\u033d\u0007\u0005\u0000\u0000"+
- "\u033c\u033b\u0001\u0000\u0000\u0000\u033c\u033d\u0001\u0000\u0000\u0000"+
- "\u033d\u033e\u0001\u0000\u0000\u0000\u033e\u033f\u00053\u0000\u0000\u033f"+
- "\u00a3\u0001\u0000\u0000\u0000\u0340\u0342\u0007\u0005\u0000\u0000\u0341"+
- "\u0340\u0001\u0000\u0000\u0000\u0341\u0342\u0001\u0000\u0000\u0000\u0342"+
- "\u0343\u0001\u0000\u0000\u0000\u0343\u0344\u00052\u0000\u0000\u0344\u00a5"+
- "\u0001\u0000\u0000\u0000\u0345\u0346\u00051\u0000\u0000\u0346\u00a7\u0001"+
- "\u0000\u0000\u0000\u0347\u0348\u0007\b\u0000\u0000\u0348\u00a9\u0001\u0000"+
- "\u0000\u0000\u0349\u034a\u0007\t\u0000\u0000\u034a\u034b\u0005n\u0000"+
- "\u0000\u034b\u034c\u0003\u00acV\u0000\u034c\u034d\u0003\u00aeW\u0000\u034d"+
- "\u00ab\u0001\u0000\u0000\u0000\u034e\u034f\u0004V\u000f\u0000\u034f\u0351"+
- "\u0003\u001c\u000e\u0000\u0350\u0352\u0005\u0080\u0000\u0000\u0351\u0350"+
- "\u0001\u0000\u0000\u0000\u0351\u0352\u0001\u0000\u0000\u0000\u0352\u0353"+
- "\u0001\u0000\u0000\u0000\u0353\u0354\u0005g\u0000\u0000\u0354\u0357\u0001"+
- "\u0000\u0000\u0000\u0355\u0357\u0003\u001c\u000e\u0000\u0356\u034e\u0001"+
- "\u0000\u0000\u0000\u0356\u0355\u0001\u0000\u0000\u0000\u0357\u00ad\u0001"+
- "\u0000\u0000\u0000\u0358\u0359\u0005F\u0000\u0000\u0359\u035e\u00030\u0018"+
- "\u0000\u035a\u035b\u0005:\u0000\u0000\u035b\u035d\u00030\u0018\u0000\u035c"+
- "\u035a\u0001\u0000\u0000\u0000\u035d\u0360\u0001\u0000\u0000\u0000\u035e"+
- "\u035c\u0001\u0000\u0000\u0000\u035e\u035f\u0001\u0000\u0000\u0000\u035f"+
- "\u036b\u0001\u0000\u0000\u0000\u0360\u035e\u0001\u0000\u0000\u0000\u0361"+
- "\u0362\u0005F\u0000\u0000\u0362\u0367\u0003\u008cF\u0000\u0363\u0364\u0005"+
- "4\u0000\u0000\u0364\u0366\u0003\u008cF\u0000\u0365\u0363\u0001\u0000\u0000"+
- "\u0000\u0366\u0369\u0001\u0000\u0000\u0000\u0367\u0365\u0001\u0000\u0000"+
- "\u0000\u0367\u0368\u0001\u0000\u0000\u0000\u0368\u036b\u0001\u0000\u0000"+
- "\u0000\u0369\u0367\u0001\u0000\u0000\u0000\u036a\u0358\u0001\u0000\u0000"+
- "\u0000\u036a\u0361\u0001\u0000\u0000\u0000\u036b\u00af\u0001\u0000\u0000"+
- "\u0000T\u00bb\u00c5\u00e1\u00f0\u00f6\u00ff\u0105\u0112\u0116\u0121\u0131"+
- "\u0139\u013d\u0144\u014a\u014f\u0158\u015f\u0165\u016e\u0175\u017d\u0185"+
- "\u0189\u018d\u0192\u019d\u01a2\u01a6\u01b4\u01bf\u01c5\u01cc\u01d5\u01ec"+
- "\u01f4\u01f7\u01fe\u0209\u0210\u0218\u0226\u022f\u023a\u0248\u0256\u025f"+
- "\u0267\u026c\u0274\u0276\u027b\u0282\u0289\u0292\u0299\u02a2\u02a7\u02ac"+
- "\u02b3\u02bd\u02c5\u02c7\u02d2\u02d9\u02e4\u02e9\u02eb\u02f2\u02fa\u02fd"+
- "\u0307\u0318\u0323\u032e\u0333\u0339\u033c\u0341\u0351\u0356\u035e\u0367"+
- "\u036a";
+ "T\u0002U\u0007U\u0002V\u0007V\u0002W\u0007W\u0002X\u0007X\u0001\u0000"+
+ "\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001"+
+ "\u0001\u0001\u0001\u0001\u0005\u0001\u00bc\b\u0001\n\u0001\f\u0001\u00bf"+
+ "\t\u0001\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001"+
+ "\u0002\u0001\u0002\u0003\u0002\u00c8\b\u0002\u0001\u0003\u0001\u0003\u0001"+
+ "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+
+ "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+
+ "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001"+
+ "\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0003"+
+ "\u0003\u00e4\b\u0003\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001"+
+ "\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001"+
+ "\u0007\u0005\u0007\u00f1\b\u0007\n\u0007\f\u0007\u00f4\t\u0007\u0001\b"+
+ "\u0001\b\u0001\b\u0003\b\u00f9\b\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+
+ "\t\u0005\t\u0100\b\t\n\t\f\t\u0103\t\t\u0001\n\u0001\n\u0001\n\u0003\n"+
+ "\u0108\b\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\f\u0001\f\u0001\f"+
+ "\u0001\r\u0001\r\u0001\r\u0005\r\u0113\b\r\n\r\f\r\u0116\t\r\u0001\r\u0003"+
+ "\r\u0119\b\r\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e"+
+ "\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0003\u000e\u0124\b\u000e"+
+ "\u0001\u000f\u0001\u000f\u0001\u0010\u0001\u0010\u0001\u0011\u0001\u0011"+
+ "\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013"+
+ "\u0005\u0013\u0132\b\u0013\n\u0013\f\u0013\u0135\t\u0013\u0001\u0014\u0001"+
+ "\u0014\u0001\u0014\u0001\u0015\u0001\u0015\u0003\u0015\u013c\b\u0015\u0001"+
+ "\u0015\u0001\u0015\u0003\u0015\u0140\b\u0015\u0001\u0016\u0001\u0016\u0001"+
+ "\u0016\u0005\u0016\u0145\b\u0016\n\u0016\f\u0016\u0148\t\u0016\u0001\u0017"+
+ "\u0001\u0017\u0001\u0017\u0003\u0017\u014d\b\u0017\u0001\u0018\u0001\u0018"+
+ "\u0001\u0018\u0003\u0018\u0152\b\u0018\u0001\u0018\u0001\u0018\u0001\u0018"+
+ "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0003\u0018\u015b\b\u0018"+
+ "\u0001\u0019\u0001\u0019\u0001\u0019\u0005\u0019\u0160\b\u0019\n\u0019"+
+ "\f\u0019\u0163\t\u0019\u0001\u001a\u0001\u001a\u0001\u001a\u0003\u001a"+
+ "\u0168\b\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a"+
+ "\u0001\u001a\u0001\u001a\u0003\u001a\u0171\b\u001a\u0001\u001b\u0001\u001b"+
+ "\u0001\u001b\u0005\u001b\u0176\b\u001b\n\u001b\f\u001b\u0179\t\u001b\u0001"+
+ "\u001c\u0001\u001c\u0001\u001c\u0005\u001c\u017e\b\u001c\n\u001c\f\u001c"+
+ "\u0181\t\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001\u001e"+
+ "\u0003\u001e\u0188\b\u001e\u0001\u001f\u0001\u001f\u0003\u001f\u018c\b"+
+ "\u001f\u0001 \u0001 \u0003 \u0190\b \u0001!\u0001!\u0001!\u0003!\u0195"+
+ "\b!\u0001\"\u0001\"\u0001\"\u0001#\u0001#\u0001#\u0001#\u0005#\u019e\b"+
+ "#\n#\f#\u01a1\t#\u0001$\u0001$\u0003$\u01a5\b$\u0001$\u0001$\u0003$\u01a9"+
+ "\b$\u0001%\u0001%\u0001%\u0001&\u0001&\u0001&\u0001\'\u0001\'\u0001\'"+
+ "\u0001\'\u0005\'\u01b5\b\'\n\'\f\'\u01b8\t\'\u0001(\u0001(\u0001(\u0001"+
+ "(\u0001(\u0001(\u0001(\u0001(\u0003(\u01c2\b(\u0001)\u0001)\u0001)\u0001"+
+ ")\u0003)\u01c8\b)\u0001*\u0001*\u0001*\u0005*\u01cd\b*\n*\f*\u01d0\t*"+
+ "\u0001+\u0001+\u0001+\u0001+\u0001,\u0001,\u0003,\u01d8\b,\u0001-\u0001"+
+ "-\u0001-\u0001-\u0001.\u0001.\u0001.\u0001/\u0001/\u0001/\u00010\u0001"+
+ "0\u00010\u00010\u00011\u00011\u00011\u00012\u00012\u00012\u00012\u0003"+
+ "2\u01ef\b2\u00012\u00012\u00012\u00012\u00052\u01f5\b2\n2\f2\u01f8\t2"+
+ "\u00032\u01fa\b2\u00013\u00013\u00014\u00014\u00014\u00034\u0201\b4\u0001"+
+ "4\u00014\u00015\u00015\u00015\u00016\u00016\u00016\u00016\u00036\u020c"+
+ "\b6\u00016\u00016\u00016\u00016\u00016\u00036\u0213\b6\u00017\u00017\u0001"+
+ "7\u00018\u00048\u0219\b8\u000b8\f8\u021a\u00019\u00019\u00019\u00019\u0001"+
+ ":\u0001:\u0001:\u0001:\u0001:\u0001:\u0005:\u0227\b:\n:\f:\u022a\t:\u0001"+
+ ";\u0001;\u0001<\u0001<\u0001<\u0001<\u0003<\u0232\b<\u0001<\u0001<\u0001"+
+ "<\u0001<\u0001<\u0001=\u0001=\u0001=\u0001=\u0003=\u023d\b=\u0001=\u0001"+
+ "=\u0001=\u0001>\u0001>\u0001>\u0001>\u0001>\u0001?\u0001?\u0001?\u0001"+
+ "?\u0003?\u024b\b?\u0001@\u0001@\u0001@\u0001A\u0001A\u0001B\u0001B\u0001"+
+ "B\u0001B\u0001B\u0001B\u0001B\u0003B\u0259\bB\u0001B\u0001B\u0001B\u0001"+
+ "B\u0001B\u0005B\u0260\bB\nB\fB\u0263\tB\u0001B\u0001B\u0001B\u0001B\u0001"+
+ "B\u0003B\u026a\bB\u0001B\u0001B\u0001B\u0003B\u026f\bB\u0001B\u0001B\u0001"+
+ "B\u0001B\u0001B\u0001B\u0005B\u0277\bB\nB\fB\u027a\tB\u0001C\u0001C\u0003"+
+ "C\u027e\bC\u0001C\u0001C\u0001C\u0001C\u0001C\u0003C\u0285\bC\u0001C\u0001"+
+ "C\u0001C\u0001C\u0001C\u0003C\u028c\bC\u0001C\u0001C\u0001C\u0001C\u0001"+
+ "C\u0005C\u0293\bC\nC\fC\u0296\tC\u0001C\u0001C\u0001C\u0001C\u0003C\u029c"+
+ "\bC\u0001C\u0001C\u0001C\u0001C\u0001C\u0005C\u02a3\bC\nC\fC\u02a6\tC"+
+ "\u0001C\u0001C\u0003C\u02aa\bC\u0001D\u0001D\u0001D\u0003D\u02af\bD\u0001"+
+ "D\u0001D\u0001D\u0001E\u0001E\u0001E\u0001E\u0001E\u0003E\u02b9\bE\u0001"+
+ "F\u0001F\u0001F\u0001F\u0003F\u02bf\bF\u0001F\u0001F\u0001F\u0001F\u0001"+
+ "F\u0001F\u0005F\u02c7\bF\nF\fF\u02ca\tF\u0001G\u0001G\u0001G\u0001G\u0001"+
+ "G\u0001G\u0001G\u0001G\u0003G\u02d4\bG\u0001G\u0001G\u0001G\u0005G\u02d9"+
+ "\bG\nG\fG\u02dc\tG\u0001H\u0001H\u0001H\u0001H\u0001H\u0001H\u0005H\u02e4"+
+ "\bH\nH\fH\u02e7\tH\u0001H\u0001H\u0003H\u02eb\bH\u0003H\u02ed\bH\u0001"+
+ "H\u0001H\u0001I\u0001I\u0001I\u0003I\u02f4\bI\u0001J\u0001J\u0001J\u0001"+
+ "J\u0005J\u02fa\bJ\nJ\fJ\u02fd\tJ\u0003J\u02ff\bJ\u0001J\u0001J\u0001K"+
+ "\u0001K\u0001K\u0001K\u0001L\u0001L\u0003L\u0309\bL\u0001M\u0001M\u0001"+
+ "M\u0001M\u0001M\u0001M\u0001M\u0001M\u0001M\u0001M\u0001M\u0001M\u0001"+
+ "M\u0005M\u0318\bM\nM\fM\u031b\tM\u0001M\u0001M\u0001M\u0001M\u0001M\u0001"+
+ "M\u0005M\u0323\bM\nM\fM\u0326\tM\u0001M\u0001M\u0001M\u0001M\u0001M\u0001"+
+ "M\u0005M\u032e\bM\nM\fM\u0331\tM\u0001M\u0001M\u0003M\u0335\bM\u0001N"+
+ "\u0001N\u0001O\u0001O\u0003O\u033b\bO\u0001P\u0003P\u033e\bP\u0001P\u0001"+
+ "P\u0001Q\u0003Q\u0343\bQ\u0001Q\u0001Q\u0001R\u0001R\u0001S\u0001S\u0001"+
+ "T\u0001T\u0001T\u0001T\u0001T\u0001U\u0001U\u0001U\u0003U\u0353\bU\u0001"+
+ "U\u0001U\u0001U\u0003U\u0358\bU\u0001V\u0001V\u0001V\u0001V\u0005V\u035e"+
+ "\bV\nV\fV\u0361\tV\u0001V\u0001V\u0001V\u0001V\u0005V\u0367\bV\nV\fV\u036a"+
+ "\tV\u0003V\u036c\bV\u0001W\u0001W\u0001X\u0001X\u0001X\u0001X\u0001X\u0000"+
+ "\u0005\u0002t\u0084\u008c\u008eY\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010"+
+ "\u0012\u0014\u0016\u0018\u001a\u001c\u001e \"$&(*,.02468:<>@BDFHJLNPR"+
+ "TVXZ\\^`bdfhjlnprtvxz|~\u0080\u0082\u0084\u0086\u0088\u008a\u008c\u008e"+
+ "\u0090\u0092\u0094\u0096\u0098\u009a\u009c\u009e\u00a0\u00a2\u00a4\u00a6"+
+ "\u00a8\u00aa\u00ac\u00ae\u00b0\u0000\n\u0002\u000011gg\u0001\u0000ab\u0002"+
+ "\u000055;;\u0002\u0000>>AA\u0002\u0000&&11\u0001\u0000ST\u0001\u0000U"+
+ "W\u0002\u0000==JJ\u0002\u0000LLNR\u0002\u0000\u0017\u0017\u0019\u001a"+
+ "\u039a\u0000\u00b2\u0001\u0000\u0000\u0000\u0002\u00b5\u0001\u0000\u0000"+
+ "\u0000\u0004\u00c7\u0001\u0000\u0000\u0000\u0006\u00e3\u0001\u0000\u0000"+
+ "\u0000\b\u00e5\u0001\u0000\u0000\u0000\n\u00e8\u0001\u0000\u0000\u0000"+
+ "\f\u00ea\u0001\u0000\u0000\u0000\u000e\u00ed\u0001\u0000\u0000\u0000\u0010"+
+ "\u00f8\u0001\u0000\u0000\u0000\u0012\u00fc\u0001\u0000\u0000\u0000\u0014"+
+ "\u0104\u0001\u0000\u0000\u0000\u0016\u0109\u0001\u0000\u0000\u0000\u0018"+
+ "\u010c\u0001\u0000\u0000\u0000\u001a\u010f\u0001\u0000\u0000\u0000\u001c"+
+ "\u0123\u0001\u0000\u0000\u0000\u001e\u0125\u0001\u0000\u0000\u0000 \u0127"+
+ "\u0001\u0000\u0000\u0000\"\u0129\u0001\u0000\u0000\u0000$\u012b\u0001"+
+ "\u0000\u0000\u0000&\u012d\u0001\u0000\u0000\u0000(\u0136\u0001\u0000\u0000"+
+ "\u0000*\u0139\u0001\u0000\u0000\u0000,\u0141\u0001\u0000\u0000\u0000."+
+ "\u0149\u0001\u0000\u0000\u00000\u015a\u0001\u0000\u0000\u00002\u015c\u0001"+
+ "\u0000\u0000\u00004\u0170\u0001\u0000\u0000\u00006\u0172\u0001\u0000\u0000"+
+ "\u00008\u017a\u0001\u0000\u0000\u0000:\u0182\u0001\u0000\u0000\u0000<"+
+ "\u0187\u0001\u0000\u0000\u0000>\u018b\u0001\u0000\u0000\u0000@\u018f\u0001"+
+ "\u0000\u0000\u0000B\u0194\u0001\u0000\u0000\u0000D\u0196\u0001\u0000\u0000"+
+ "\u0000F\u0199\u0001\u0000\u0000\u0000H\u01a2\u0001\u0000\u0000\u0000J"+
+ "\u01aa\u0001\u0000\u0000\u0000L\u01ad\u0001\u0000\u0000\u0000N\u01b0\u0001"+
+ "\u0000\u0000\u0000P\u01c1\u0001\u0000\u0000\u0000R\u01c3\u0001\u0000\u0000"+
+ "\u0000T\u01c9\u0001\u0000\u0000\u0000V\u01d1\u0001\u0000\u0000\u0000X"+
+ "\u01d7\u0001\u0000\u0000\u0000Z\u01d9\u0001\u0000\u0000\u0000\\\u01dd"+
+ "\u0001\u0000\u0000\u0000^\u01e0\u0001\u0000\u0000\u0000`\u01e3\u0001\u0000"+
+ "\u0000\u0000b\u01e7\u0001\u0000\u0000\u0000d\u01ea\u0001\u0000\u0000\u0000"+
+ "f\u01fb\u0001\u0000\u0000\u0000h\u0200\u0001\u0000\u0000\u0000j\u0204"+
+ "\u0001\u0000\u0000\u0000l\u0207\u0001\u0000\u0000\u0000n\u0214\u0001\u0000"+
+ "\u0000\u0000p\u0218\u0001\u0000\u0000\u0000r\u021c\u0001\u0000\u0000\u0000"+
+ "t\u0220\u0001\u0000\u0000\u0000v\u022b\u0001\u0000\u0000\u0000x\u022d"+
+ "\u0001\u0000\u0000\u0000z\u0238\u0001\u0000\u0000\u0000|\u0241\u0001\u0000"+
+ "\u0000\u0000~\u0246\u0001\u0000\u0000\u0000\u0080\u024c\u0001\u0000\u0000"+
+ "\u0000\u0082\u024f\u0001\u0000\u0000\u0000\u0084\u026e\u0001\u0000\u0000"+
+ "\u0000\u0086\u02a9\u0001\u0000\u0000\u0000\u0088\u02ab\u0001\u0000\u0000"+
+ "\u0000\u008a\u02b8\u0001\u0000\u0000\u0000\u008c\u02be\u0001\u0000\u0000"+
+ "\u0000\u008e\u02d3\u0001\u0000\u0000\u0000\u0090\u02dd\u0001\u0000\u0000"+
+ "\u0000\u0092\u02f3\u0001\u0000\u0000\u0000\u0094\u02f5\u0001\u0000\u0000"+
+ "\u0000\u0096\u0302\u0001\u0000\u0000\u0000\u0098\u0308\u0001\u0000\u0000"+
+ "\u0000\u009a\u0334\u0001\u0000\u0000\u0000\u009c\u0336\u0001\u0000\u0000"+
+ "\u0000\u009e\u033a\u0001\u0000\u0000\u0000\u00a0\u033d\u0001\u0000\u0000"+
+ "\u0000\u00a2\u0342\u0001\u0000\u0000\u0000\u00a4\u0346\u0001\u0000\u0000"+
+ "\u0000\u00a6\u0348\u0001\u0000\u0000\u0000\u00a8\u034a\u0001\u0000\u0000"+
+ "\u0000\u00aa\u0357\u0001\u0000\u0000\u0000\u00ac\u036b\u0001\u0000\u0000"+
+ "\u0000\u00ae\u036d\u0001\u0000\u0000\u0000\u00b0\u036f\u0001\u0000\u0000"+
+ "\u0000\u00b2\u00b3\u0003\u0002\u0001\u0000\u00b3\u00b4\u0005\u0000\u0000"+
+ "\u0001\u00b4\u0001\u0001\u0000\u0000\u0000\u00b5\u00b6\u0006\u0001\uffff"+
+ "\uffff\u0000\u00b6\u00b7\u0003\u0004\u0002\u0000\u00b7\u00bd\u0001\u0000"+
+ "\u0000\u0000\u00b8\u00b9\n\u0001\u0000\u0000\u00b9\u00ba\u00050\u0000"+
+ "\u0000\u00ba\u00bc\u0003\u0006\u0003\u0000\u00bb\u00b8\u0001\u0000\u0000"+
+ "\u0000\u00bc\u00bf\u0001\u0000\u0000\u0000\u00bd\u00bb\u0001\u0000\u0000"+
+ "\u0000\u00bd\u00be\u0001\u0000\u0000\u0000\u00be\u0003\u0001\u0000\u0000"+
+ "\u0000\u00bf\u00bd\u0001\u0000\u0000\u0000\u00c0\u00c8\u0003\u0016\u000b"+
+ "\u0000\u00c1\u00c8\u0003\f\u0006\u0000\u00c2\u00c8\u0003b1\u0000\u00c3"+
+ "\u00c4\u0004\u0002\u0001\u0000\u00c4\u00c8\u0003\u0018\f\u0000\u00c5\u00c6"+
+ "\u0004\u0002\u0002\u0000\u00c6\u00c8\u0003^/\u0000\u00c7\u00c0\u0001\u0000"+
+ "\u0000\u0000\u00c7\u00c1\u0001\u0000\u0000\u0000\u00c7\u00c2\u0001\u0000"+
+ "\u0000\u0000\u00c7\u00c3\u0001\u0000\u0000\u0000\u00c7\u00c5\u0001\u0000"+
+ "\u0000\u0000\u00c8\u0005\u0001\u0000\u0000\u0000\u00c9\u00e4\u0003(\u0014"+
+ "\u0000\u00ca\u00e4\u0003\b\u0004\u0000\u00cb\u00e4\u0003J%\u0000\u00cc"+
+ "\u00e4\u0003D\"\u0000\u00cd\u00e4\u0003*\u0015\u0000\u00ce\u00e4\u0003"+
+ "F#\u0000\u00cf\u00e4\u0003L&\u0000\u00d0\u00e4\u0003N\'\u0000\u00d1\u00e4"+
+ "\u0003R)\u0000\u00d2\u00e4\u0003Z-\u0000\u00d3\u00e4\u0003d2\u0000\u00d4"+
+ "\u00e4\u0003\\.\u0000\u00d5\u00e4\u0003\u00a8T\u0000\u00d6\u00e4\u0003"+
+ "l6\u0000\u00d7\u00e4\u0003z=\u0000\u00d8\u00e4\u0003j5\u0000\u00d9\u00e4"+
+ "\u0003n7\u0000\u00da\u00e4\u0003x<\u0000\u00db\u00dc\u0004\u0003\u0003"+
+ "\u0000\u00dc\u00e4\u0003~?\u0000\u00dd\u00de\u0004\u0003\u0004\u0000\u00de"+
+ "\u00e4\u0003|>\u0000\u00df\u00e0\u0004\u0003\u0005\u0000\u00e0\u00e4\u0003"+
+ "\u0080@\u0000\u00e1\u00e2\u0004\u0003\u0006\u0000\u00e2\u00e4\u0003\u0082"+
+ "A\u0000\u00e3\u00c9\u0001\u0000\u0000\u0000\u00e3\u00ca\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00cb\u0001\u0000\u0000\u0000\u00e3\u00cc\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00cd\u0001\u0000\u0000\u0000\u00e3\u00ce\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00cf\u0001\u0000\u0000\u0000\u00e3\u00d0\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00d1\u0001\u0000\u0000\u0000\u00e3\u00d2\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00d3\u0001\u0000\u0000\u0000\u00e3\u00d4\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00d5\u0001\u0000\u0000\u0000\u00e3\u00d6\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00d7\u0001\u0000\u0000\u0000\u00e3\u00d8\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00d9\u0001\u0000\u0000\u0000\u00e3\u00da\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00db\u0001\u0000\u0000\u0000\u00e3\u00dd\u0001\u0000\u0000"+
+ "\u0000\u00e3\u00df\u0001\u0000\u0000\u0000\u00e3\u00e1\u0001\u0000\u0000"+
+ "\u0000\u00e4\u0007\u0001\u0000\u0000\u0000\u00e5\u00e6\u0005\u0011\u0000"+
+ "\u0000\u00e6\u00e7\u0003\u0084B\u0000\u00e7\t\u0001\u0000\u0000\u0000"+
+ "\u00e8\u00e9\u0003:\u001d\u0000\u00e9\u000b\u0001\u0000\u0000\u0000\u00ea"+
+ "\u00eb\u0005\r\u0000\u0000\u00eb\u00ec\u0003\u000e\u0007\u0000\u00ec\r"+
+ "\u0001\u0000\u0000\u0000\u00ed\u00f2\u0003\u0010\b\u0000\u00ee\u00ef\u0005"+
+ ":\u0000\u0000\u00ef\u00f1\u0003\u0010\b\u0000\u00f0\u00ee\u0001\u0000"+
+ "\u0000\u0000\u00f1\u00f4\u0001\u0000\u0000\u0000\u00f2\u00f0\u0001\u0000"+
+ "\u0000\u0000\u00f2\u00f3\u0001\u0000\u0000\u0000\u00f3\u000f\u0001\u0000"+
+ "\u0000\u0000\u00f4\u00f2\u0001\u0000\u0000\u0000\u00f5\u00f6\u00030\u0018"+
+ "\u0000\u00f6\u00f7\u00056\u0000\u0000\u00f7\u00f9\u0001\u0000\u0000\u0000"+
+ "\u00f8\u00f5\u0001\u0000\u0000\u0000\u00f8\u00f9\u0001\u0000\u0000\u0000"+
+ "\u00f9\u00fa\u0001\u0000\u0000\u0000\u00fa\u00fb\u0003\u0084B\u0000\u00fb"+
+ "\u0011\u0001\u0000\u0000\u0000\u00fc\u0101\u0003\u0014\n\u0000\u00fd\u00fe"+
+ "\u0005:\u0000\u0000\u00fe\u0100\u0003\u0014\n\u0000\u00ff\u00fd\u0001"+
+ "\u0000\u0000\u0000\u0100\u0103\u0001\u0000\u0000\u0000\u0101\u00ff\u0001"+
+ "\u0000\u0000\u0000\u0101\u0102\u0001\u0000\u0000\u0000\u0102\u0013\u0001"+
+ "\u0000\u0000\u0000\u0103\u0101\u0001\u0000\u0000\u0000\u0104\u0107\u0003"+
+ "0\u0018\u0000\u0105\u0106\u00056\u0000\u0000\u0106\u0108\u0003\u0084B"+
+ "\u0000\u0107\u0105\u0001\u0000\u0000\u0000\u0107\u0108\u0001\u0000\u0000"+
+ "\u0000\u0108\u0015\u0001\u0000\u0000\u0000\u0109\u010a\u0005\u0013\u0000"+
+ "\u0000\u010a\u010b\u0003\u001a\r\u0000\u010b\u0017\u0001\u0000\u0000\u0000"+
+ "\u010c\u010d\u0005\u0014\u0000\u0000\u010d\u010e\u0003\u001a\r\u0000\u010e"+
+ "\u0019\u0001\u0000\u0000\u0000\u010f\u0114\u0003\u001c\u000e\u0000\u0110"+
+ "\u0111\u0005:\u0000\u0000\u0111\u0113\u0003\u001c\u000e\u0000\u0112\u0110"+
+ "\u0001\u0000\u0000\u0000\u0113\u0116\u0001\u0000\u0000\u0000\u0114\u0112"+
+ "\u0001\u0000\u0000\u0000\u0114\u0115\u0001\u0000\u0000\u0000\u0115\u0118"+
+ "\u0001\u0000\u0000\u0000\u0116\u0114\u0001\u0000\u0000\u0000\u0117\u0119"+
+ "\u0003&\u0013\u0000\u0118\u0117\u0001\u0000\u0000\u0000\u0118\u0119\u0001"+
+ "\u0000\u0000\u0000\u0119\u001b\u0001\u0000\u0000\u0000\u011a\u011b\u0003"+
+ "\u001e\u000f\u0000\u011b\u011c\u00059\u0000\u0000\u011c\u011d\u0003\""+
+ "\u0011\u0000\u011d\u0124\u0001\u0000\u0000\u0000\u011e\u011f\u0003\"\u0011"+
+ "\u0000\u011f\u0120\u00058\u0000\u0000\u0120\u0121\u0003 \u0010\u0000\u0121"+
+ "\u0124\u0001\u0000\u0000\u0000\u0122\u0124\u0003$\u0012\u0000\u0123\u011a"+
+ "\u0001\u0000\u0000\u0000\u0123\u011e\u0001\u0000\u0000\u0000\u0123\u0122"+
+ "\u0001\u0000\u0000\u0000\u0124\u001d\u0001\u0000\u0000\u0000\u0125\u0126"+
+ "\u0005g\u0000\u0000\u0126\u001f\u0001\u0000\u0000\u0000\u0127\u0128\u0005"+
+ "g\u0000\u0000\u0128!\u0001\u0000\u0000\u0000\u0129\u012a\u0005g\u0000"+
+ "\u0000\u012a#\u0001\u0000\u0000\u0000\u012b\u012c\u0007\u0000\u0000\u0000"+
+ "\u012c%\u0001\u0000\u0000\u0000\u012d\u012e\u0005f\u0000\u0000\u012e\u0133"+
+ "\u0005g\u0000\u0000\u012f\u0130\u0005:\u0000\u0000\u0130\u0132\u0005g"+
+ "\u0000\u0000\u0131\u012f\u0001\u0000\u0000\u0000\u0132\u0135\u0001\u0000"+
+ "\u0000\u0000\u0133\u0131\u0001\u0000\u0000\u0000\u0133\u0134\u0001\u0000"+
+ "\u0000\u0000\u0134\'\u0001\u0000\u0000\u0000\u0135\u0133\u0001\u0000\u0000"+
+ "\u0000\u0136\u0137\u0005\t\u0000\u0000\u0137\u0138\u0003\u000e\u0007\u0000"+
+ "\u0138)\u0001\u0000\u0000\u0000\u0139\u013b\u0005\u0010\u0000\u0000\u013a"+
+ "\u013c\u0003,\u0016\u0000\u013b\u013a\u0001\u0000\u0000\u0000\u013b\u013c"+
+ "\u0001\u0000\u0000\u0000\u013c\u013f\u0001\u0000\u0000\u0000\u013d\u013e"+
+ "\u00057\u0000\u0000\u013e\u0140\u0003\u000e\u0007\u0000\u013f\u013d\u0001"+
+ "\u0000\u0000\u0000\u013f\u0140\u0001\u0000\u0000\u0000\u0140+\u0001\u0000"+
+ "\u0000\u0000\u0141\u0146\u0003.\u0017\u0000\u0142\u0143\u0005:\u0000\u0000"+
+ "\u0143\u0145\u0003.\u0017\u0000\u0144\u0142\u0001\u0000\u0000\u0000\u0145"+
+ "\u0148\u0001\u0000\u0000\u0000\u0146\u0144\u0001\u0000\u0000\u0000\u0146"+
+ "\u0147\u0001\u0000\u0000\u0000\u0147-\u0001\u0000\u0000\u0000\u0148\u0146"+
+ "\u0001\u0000\u0000\u0000\u0149\u014c\u0003\u0010\b\u0000\u014a\u014b\u0005"+
+ "\u0011\u0000\u0000\u014b\u014d\u0003\u0084B\u0000\u014c\u014a\u0001\u0000"+
+ "\u0000\u0000\u014c\u014d\u0001\u0000\u0000\u0000\u014d/\u0001\u0000\u0000"+
+ "\u0000\u014e\u014f\u0004\u0018\u0007\u0000\u014f\u0151\u0005]\u0000\u0000"+
+ "\u0150\u0152\u0005a\u0000\u0000\u0151\u0150\u0001\u0000\u0000\u0000\u0151"+
+ "\u0152\u0001\u0000\u0000\u0000\u0152\u0153\u0001\u0000\u0000\u0000\u0153"+
+ "\u0154\u0005^\u0000\u0000\u0154\u0155\u0005<\u0000\u0000\u0155\u0156\u0005"+
+ "]\u0000\u0000\u0156\u0157\u00032\u0019\u0000\u0157\u0158\u0005^\u0000"+
+ "\u0000\u0158\u015b\u0001\u0000\u0000\u0000\u0159\u015b\u00032\u0019\u0000"+
+ "\u015a\u014e\u0001\u0000\u0000\u0000\u015a\u0159\u0001\u0000\u0000\u0000"+
+ "\u015b1\u0001\u0000\u0000\u0000\u015c\u0161\u0003B!\u0000\u015d\u015e"+
+ "\u0005<\u0000\u0000\u015e\u0160\u0003B!\u0000\u015f\u015d\u0001\u0000"+
+ "\u0000\u0000\u0160\u0163\u0001\u0000\u0000\u0000\u0161\u015f\u0001\u0000"+
+ "\u0000\u0000\u0161\u0162\u0001\u0000\u0000\u0000\u01623\u0001\u0000\u0000"+
+ "\u0000\u0163\u0161\u0001\u0000\u0000\u0000\u0164\u0165\u0004\u001a\b\u0000"+
+ "\u0165\u0167\u0005]\u0000\u0000\u0166\u0168\u0005|\u0000\u0000\u0167\u0166"+
+ "\u0001\u0000\u0000\u0000\u0167\u0168\u0001\u0000\u0000\u0000\u0168\u0169"+
+ "\u0001\u0000\u0000\u0000\u0169\u016a\u0005^\u0000\u0000\u016a\u016b\u0005"+
+ "<\u0000\u0000\u016b\u016c\u0005]\u0000\u0000\u016c\u016d\u00036\u001b"+
+ "\u0000\u016d\u016e\u0005^\u0000\u0000\u016e\u0171\u0001\u0000\u0000\u0000"+
+ "\u016f\u0171\u00036\u001b\u0000\u0170\u0164\u0001\u0000\u0000\u0000\u0170"+
+ "\u016f\u0001\u0000\u0000\u0000\u01715\u0001\u0000\u0000\u0000\u0172\u0177"+
+ "\u0003<\u001e\u0000\u0173\u0174\u0005<\u0000\u0000\u0174\u0176\u0003<"+
+ "\u001e\u0000\u0175\u0173\u0001\u0000\u0000\u0000\u0176\u0179\u0001\u0000"+
+ "\u0000\u0000\u0177\u0175\u0001\u0000\u0000\u0000\u0177\u0178\u0001\u0000"+
+ "\u0000\u0000\u01787\u0001\u0000\u0000\u0000\u0179\u0177\u0001\u0000\u0000"+
+ "\u0000\u017a\u017f\u00034\u001a\u0000\u017b\u017c\u0005:\u0000\u0000\u017c"+
+ "\u017e\u00034\u001a\u0000\u017d\u017b\u0001\u0000\u0000\u0000\u017e\u0181"+
+ "\u0001\u0000\u0000\u0000\u017f\u017d\u0001\u0000\u0000\u0000\u017f\u0180"+
+ "\u0001\u0000\u0000\u0000\u01809\u0001\u0000\u0000\u0000\u0181\u017f\u0001"+
+ "\u0000\u0000\u0000\u0182\u0183\u0007\u0001\u0000\u0000\u0183;\u0001\u0000"+
+ "\u0000\u0000\u0184\u0188\u0005|\u0000\u0000\u0185\u0188\u0003>\u001f\u0000"+
+ "\u0186\u0188\u0003@ \u0000\u0187\u0184\u0001\u0000\u0000\u0000\u0187\u0185"+
+ "\u0001\u0000\u0000\u0000\u0187\u0186\u0001\u0000\u0000\u0000\u0188=\u0001"+
+ "\u0000\u0000\u0000\u0189\u018c\u0005H\u0000\u0000\u018a\u018c\u0005[\u0000"+
+ "\u0000\u018b\u0189\u0001\u0000\u0000\u0000\u018b\u018a\u0001\u0000\u0000"+
+ "\u0000\u018c?\u0001\u0000\u0000\u0000\u018d\u0190\u0005Z\u0000\u0000\u018e"+
+ "\u0190\u0005\\\u0000\u0000\u018f\u018d\u0001\u0000\u0000\u0000\u018f\u018e"+
+ "\u0001\u0000\u0000\u0000\u0190A\u0001\u0000\u0000\u0000\u0191\u0195\u0003"+
+ ":\u001d\u0000\u0192\u0195\u0003>\u001f\u0000\u0193\u0195\u0003@ \u0000"+
+ "\u0194\u0191\u0001\u0000\u0000\u0000\u0194\u0192\u0001\u0000\u0000\u0000"+
+ "\u0194\u0193\u0001\u0000\u0000\u0000\u0195C\u0001\u0000\u0000\u0000\u0196"+
+ "\u0197\u0005\u000b\u0000\u0000\u0197\u0198\u0003\u009aM\u0000\u0198E\u0001"+
+ "\u0000\u0000\u0000\u0199\u019a\u0005\u000f\u0000\u0000\u019a\u019f\u0003"+
+ "H$\u0000\u019b\u019c\u0005:\u0000\u0000\u019c\u019e\u0003H$\u0000\u019d"+
+ "\u019b\u0001\u0000\u0000\u0000\u019e\u01a1\u0001\u0000\u0000\u0000\u019f"+
+ "\u019d\u0001\u0000\u0000\u0000\u019f\u01a0\u0001\u0000\u0000\u0000\u01a0"+
+ "G\u0001\u0000\u0000\u0000\u01a1\u019f\u0001\u0000\u0000\u0000\u01a2\u01a4"+
+ "\u0003\u0084B\u0000\u01a3\u01a5\u0007\u0002\u0000\u0000\u01a4\u01a3\u0001"+
+ "\u0000\u0000\u0000\u01a4\u01a5\u0001\u0000\u0000\u0000\u01a5\u01a8\u0001"+
+ "\u0000\u0000\u0000\u01a6\u01a7\u0005E\u0000\u0000\u01a7\u01a9\u0007\u0003"+
+ "\u0000\u0000\u01a8\u01a6\u0001\u0000\u0000\u0000\u01a8\u01a9\u0001\u0000"+
+ "\u0000\u0000\u01a9I\u0001\u0000\u0000\u0000\u01aa\u01ab\u0005\u001e\u0000"+
+ "\u0000\u01ab\u01ac\u00038\u001c\u0000\u01acK\u0001\u0000\u0000\u0000\u01ad"+
+ "\u01ae\u0005\u001d\u0000\u0000\u01ae\u01af\u00038\u001c\u0000\u01afM\u0001"+
+ "\u0000\u0000\u0000\u01b0\u01b1\u0005 \u0000\u0000\u01b1\u01b6\u0003P("+
+ "\u0000\u01b2\u01b3\u0005:\u0000\u0000\u01b3\u01b5\u0003P(\u0000\u01b4"+
+ "\u01b2\u0001\u0000\u0000\u0000\u01b5\u01b8\u0001\u0000\u0000\u0000\u01b6"+
+ "\u01b4\u0001\u0000\u0000\u0000\u01b6\u01b7\u0001\u0000\u0000\u0000\u01b7"+
+ "O\u0001\u0000\u0000\u0000\u01b8\u01b6\u0001\u0000\u0000\u0000\u01b9\u01ba"+
+ "\u00034\u001a\u0000\u01ba\u01bb\u0005\u0080\u0000\u0000\u01bb\u01bc\u0003"+
+ "4\u001a\u0000\u01bc\u01c2\u0001\u0000\u0000\u0000\u01bd\u01be\u00034\u001a"+
+ "\u0000\u01be\u01bf\u00056\u0000\u0000\u01bf\u01c0\u00034\u001a\u0000\u01c0"+
+ "\u01c2\u0001\u0000\u0000\u0000\u01c1\u01b9\u0001\u0000\u0000\u0000\u01c1"+
+ "\u01bd\u0001\u0000\u0000\u0000\u01c2Q\u0001\u0000\u0000\u0000\u01c3\u01c4"+
+ "\u0005\b\u0000\u0000\u01c4\u01c5\u0003\u008eG\u0000\u01c5\u01c7\u0003"+
+ "\u00a4R\u0000\u01c6\u01c8\u0003T*\u0000\u01c7\u01c6\u0001\u0000\u0000"+
+ "\u0000\u01c7\u01c8\u0001\u0000\u0000\u0000\u01c8S\u0001\u0000\u0000\u0000"+
+ "\u01c9\u01ce\u0003V+\u0000\u01ca\u01cb\u0005:\u0000\u0000\u01cb\u01cd"+
+ "\u0003V+\u0000\u01cc\u01ca\u0001\u0000\u0000\u0000\u01cd\u01d0\u0001\u0000"+
+ "\u0000\u0000\u01ce\u01cc\u0001\u0000\u0000\u0000\u01ce\u01cf\u0001\u0000"+
+ "\u0000\u0000\u01cfU\u0001\u0000\u0000\u0000\u01d0\u01ce\u0001\u0000\u0000"+
+ "\u0000\u01d1\u01d2\u0003:\u001d\u0000\u01d2\u01d3\u00056\u0000\u0000\u01d3"+
+ "\u01d4\u0003\u009aM\u0000\u01d4W\u0001\u0000\u0000\u0000\u01d5\u01d6\u0005"+
+ "K\u0000\u0000\u01d6\u01d8\u0003\u0094J\u0000\u01d7\u01d5\u0001\u0000\u0000"+
+ "\u0000\u01d7\u01d8\u0001\u0000\u0000\u0000\u01d8Y\u0001\u0000\u0000\u0000"+
+ "\u01d9\u01da\u0005\n\u0000\u0000\u01da\u01db\u0003\u008eG\u0000\u01db"+
+ "\u01dc\u0003\u00a4R\u0000\u01dc[\u0001\u0000\u0000\u0000\u01dd\u01de\u0005"+
+ "\u001c\u0000\u0000\u01de\u01df\u00030\u0018\u0000\u01df]\u0001\u0000\u0000"+
+ "\u0000\u01e0\u01e1\u0005\u0006\u0000\u0000\u01e1\u01e2\u0003`0\u0000\u01e2"+
+ "_\u0001\u0000\u0000\u0000\u01e3\u01e4\u0005_\u0000\u0000\u01e4\u01e5\u0003"+
+ "\u0002\u0001\u0000\u01e5\u01e6\u0005`\u0000\u0000\u01e6a\u0001\u0000\u0000"+
+ "\u0000\u01e7\u01e8\u0005!\u0000\u0000\u01e8\u01e9\u0005\u0084\u0000\u0000"+
+ "\u01e9c\u0001\u0000\u0000\u0000\u01ea\u01eb\u0005\u0005\u0000\u0000\u01eb"+
+ "\u01ee\u0003f3\u0000\u01ec\u01ed\u0005F\u0000\u0000\u01ed\u01ef\u0003"+
+ "4\u001a\u0000\u01ee\u01ec\u0001\u0000\u0000\u0000\u01ee\u01ef\u0001\u0000"+
+ "\u0000\u0000\u01ef\u01f9\u0001\u0000\u0000\u0000\u01f0\u01f1\u0005K\u0000"+
+ "\u0000\u01f1\u01f6\u0003h4\u0000\u01f2\u01f3\u0005:\u0000\u0000\u01f3"+
+ "\u01f5\u0003h4\u0000\u01f4\u01f2\u0001\u0000\u0000\u0000\u01f5\u01f8\u0001"+
+ "\u0000\u0000\u0000\u01f6\u01f4\u0001\u0000\u0000\u0000\u01f6\u01f7\u0001"+
+ "\u0000\u0000\u0000\u01f7\u01fa\u0001\u0000\u0000\u0000\u01f8\u01f6\u0001"+
+ "\u0000\u0000\u0000\u01f9\u01f0\u0001\u0000\u0000\u0000\u01f9\u01fa\u0001"+
+ "\u0000\u0000\u0000\u01fae\u0001\u0000\u0000\u0000\u01fb\u01fc\u0007\u0004"+
+ "\u0000\u0000\u01fcg\u0001\u0000\u0000\u0000\u01fd\u01fe\u00034\u001a\u0000"+
+ "\u01fe\u01ff\u00056\u0000\u0000\u01ff\u0201\u0001\u0000\u0000\u0000\u0200"+
+ "\u01fd\u0001\u0000\u0000\u0000\u0200\u0201\u0001\u0000\u0000\u0000\u0201"+
+ "\u0202\u0001\u0000\u0000\u0000\u0202\u0203\u00034\u001a\u0000\u0203i\u0001"+
+ "\u0000\u0000\u0000\u0204\u0205\u0005\u000e\u0000\u0000\u0205\u0206\u0003"+
+ "\u009aM\u0000\u0206k\u0001\u0000\u0000\u0000\u0207\u0208\u0005\u0004\u0000"+
+ "\u0000\u0208\u020b\u00030\u0018\u0000\u0209\u020a\u0005F\u0000\u0000\u020a"+
+ "\u020c\u00030\u0018\u0000\u020b\u0209\u0001\u0000\u0000\u0000\u020b\u020c"+
+ "\u0001\u0000\u0000\u0000\u020c\u0212\u0001\u0000\u0000\u0000\u020d\u020e"+
+ "\u0005\u0080\u0000\u0000\u020e\u020f\u00030\u0018\u0000\u020f\u0210\u0005"+
+ ":\u0000\u0000\u0210\u0211\u00030\u0018\u0000\u0211\u0213\u0001\u0000\u0000"+
+ "\u0000\u0212\u020d\u0001\u0000\u0000\u0000\u0212\u0213\u0001\u0000\u0000"+
+ "\u0000\u0213m\u0001\u0000\u0000\u0000\u0214\u0215\u0005\u0015\u0000\u0000"+
+ "\u0215\u0216\u0003p8\u0000\u0216o\u0001\u0000\u0000\u0000\u0217\u0219"+
+ "\u0003r9\u0000\u0218\u0217\u0001\u0000\u0000\u0000\u0219\u021a\u0001\u0000"+
+ "\u0000\u0000\u021a\u0218\u0001\u0000\u0000\u0000\u021a\u021b\u0001\u0000"+
+ "\u0000\u0000\u021bq\u0001\u0000\u0000\u0000\u021c\u021d\u0005_\u0000\u0000"+
+ "\u021d\u021e\u0003t:\u0000\u021e\u021f\u0005`\u0000\u0000\u021fs\u0001"+
+ "\u0000\u0000\u0000\u0220\u0221\u0006:\uffff\uffff\u0000\u0221\u0222\u0003"+
+ "v;\u0000\u0222\u0228\u0001\u0000\u0000\u0000\u0223\u0224\n\u0001\u0000"+
+ "\u0000\u0224\u0225\u00050\u0000\u0000\u0225\u0227\u0003v;\u0000\u0226"+
+ "\u0223\u0001\u0000\u0000\u0000\u0227\u022a\u0001\u0000\u0000\u0000\u0228"+
+ "\u0226\u0001\u0000\u0000\u0000\u0228\u0229\u0001\u0000\u0000\u0000\u0229"+
+ "u\u0001\u0000\u0000\u0000\u022a\u0228\u0001\u0000\u0000\u0000\u022b\u022c"+
+ "\u0003\u0006\u0003\u0000\u022cw\u0001\u0000\u0000\u0000\u022d\u0231\u0005"+
+ "\f\u0000\u0000\u022e\u022f\u00030\u0018\u0000\u022f\u0230\u00056\u0000"+
+ "\u0000\u0230\u0232\u0001\u0000\u0000\u0000\u0231\u022e\u0001\u0000\u0000"+
+ "\u0000\u0231\u0232\u0001\u0000\u0000\u0000\u0232\u0233\u0001\u0000\u0000"+
+ "\u0000\u0233\u0234\u0003\u009aM\u0000\u0234\u0235\u0005F\u0000\u0000\u0235"+
+ "\u0236\u0003\u0012\t\u0000\u0236\u0237\u0003X,\u0000\u0237y\u0001\u0000"+
+ "\u0000\u0000\u0238\u023c\u0005\u0007\u0000\u0000\u0239\u023a\u00030\u0018"+
+ "\u0000\u023a\u023b\u00056\u0000\u0000\u023b\u023d\u0001\u0000\u0000\u0000"+
+ "\u023c\u0239\u0001\u0000\u0000\u0000\u023c\u023d\u0001\u0000\u0000\u0000"+
+ "\u023d\u023e\u0001\u0000\u0000\u0000\u023e\u023f\u0003\u008eG\u0000\u023f"+
+ "\u0240\u0003X,\u0000\u0240{\u0001\u0000\u0000\u0000\u0241\u0242\u0005"+
+ "\u001b\u0000\u0000\u0242\u0243\u0003\u001c\u000e\u0000\u0243\u0244\u0005"+
+ "F\u0000\u0000\u0244\u0245\u00038\u001c\u0000\u0245}\u0001\u0000\u0000"+
+ "\u0000\u0246\u0247\u0005\u0012\u0000\u0000\u0247\u024a\u0003,\u0016\u0000"+
+ "\u0248\u0249\u00057\u0000\u0000\u0249\u024b\u0003\u000e\u0007\u0000\u024a"+
+ "\u0248\u0001\u0000\u0000\u0000\u024a\u024b\u0001\u0000\u0000\u0000\u024b"+
+ "\u007f\u0001\u0000\u0000\u0000\u024c\u024d\u0005\u001f\u0000\u0000\u024d"+
+ "\u024e\u00038\u001c\u0000\u024e\u0081\u0001\u0000\u0000\u0000\u024f\u0250"+
+ "\u0005\u0016\u0000\u0000\u0250\u0083\u0001\u0000\u0000\u0000\u0251\u0252"+
+ "\u0006B\uffff\uffff\u0000\u0252\u0253\u0005C\u0000\u0000\u0253\u026f\u0003"+
+ "\u0084B\b\u0254\u026f\u0003\u008aE\u0000\u0255\u026f\u0003\u0086C\u0000"+
+ "\u0256\u0258\u0003\u008aE\u0000\u0257\u0259\u0005C\u0000\u0000\u0258\u0257"+
+ "\u0001\u0000\u0000\u0000\u0258\u0259\u0001\u0000\u0000\u0000\u0259\u025a"+
+ "\u0001\u0000\u0000\u0000\u025a\u025b\u0005?\u0000\u0000\u025b\u025c\u0005"+
+ "_\u0000\u0000\u025c\u0261\u0003\u008aE\u0000\u025d\u025e\u0005:\u0000"+
+ "\u0000\u025e\u0260\u0003\u008aE\u0000\u025f\u025d\u0001\u0000\u0000\u0000"+
+ "\u0260\u0263\u0001\u0000\u0000\u0000\u0261\u025f\u0001\u0000\u0000\u0000"+
+ "\u0261\u0262\u0001\u0000\u0000\u0000\u0262\u0264\u0001\u0000\u0000\u0000"+
+ "\u0263\u0261\u0001\u0000\u0000\u0000\u0264\u0265\u0005`\u0000\u0000\u0265"+
+ "\u026f\u0001\u0000\u0000\u0000\u0266\u0267\u0003\u008aE\u0000\u0267\u0269"+
+ "\u0005@\u0000\u0000\u0268\u026a\u0005C\u0000\u0000\u0269\u0268\u0001\u0000"+
+ "\u0000\u0000\u0269\u026a\u0001\u0000\u0000\u0000\u026a\u026b\u0001\u0000"+
+ "\u0000\u0000\u026b\u026c\u0005D\u0000\u0000\u026c\u026f\u0001\u0000\u0000"+
+ "\u0000\u026d\u026f\u0003\u0088D\u0000\u026e\u0251\u0001\u0000\u0000\u0000"+
+ "\u026e\u0254\u0001\u0000\u0000\u0000\u026e\u0255\u0001\u0000\u0000\u0000"+
+ "\u026e\u0256\u0001\u0000\u0000\u0000\u026e\u0266\u0001\u0000\u0000\u0000"+
+ "\u026e\u026d\u0001\u0000\u0000\u0000\u026f\u0278\u0001\u0000\u0000\u0000"+
+ "\u0270\u0271\n\u0005\u0000\u0000\u0271\u0272\u00054\u0000\u0000\u0272"+
+ "\u0277\u0003\u0084B\u0006\u0273\u0274\n\u0004\u0000\u0000\u0274\u0275"+
+ "\u0005G\u0000\u0000\u0275\u0277\u0003\u0084B\u0005\u0276\u0270\u0001\u0000"+
+ "\u0000\u0000\u0276\u0273\u0001\u0000\u0000\u0000\u0277\u027a\u0001\u0000"+
+ "\u0000\u0000\u0278\u0276\u0001\u0000\u0000\u0000\u0278\u0279\u0001\u0000"+
+ "\u0000\u0000\u0279\u0085\u0001\u0000\u0000\u0000\u027a\u0278\u0001\u0000"+
+ "\u0000\u0000\u027b\u027d\u0003\u008aE\u0000\u027c\u027e\u0005C\u0000\u0000"+
+ "\u027d\u027c\u0001\u0000\u0000\u0000\u027d\u027e\u0001\u0000\u0000\u0000"+
+ "\u027e\u027f\u0001\u0000\u0000\u0000\u027f\u0280\u0005B\u0000\u0000\u0280"+
+ "\u0281\u0003\u00a4R\u0000\u0281\u02aa\u0001\u0000\u0000\u0000\u0282\u0284"+
+ "\u0003\u008aE\u0000\u0283\u0285\u0005C\u0000\u0000\u0284\u0283\u0001\u0000"+
+ "\u0000\u0000\u0284\u0285\u0001\u0000\u0000\u0000\u0285\u0286\u0001\u0000"+
+ "\u0000\u0000\u0286\u0287\u0005I\u0000\u0000\u0287\u0288\u0003\u00a4R\u0000"+
+ "\u0288\u02aa\u0001\u0000\u0000\u0000\u0289\u028b\u0003\u008aE\u0000\u028a"+
+ "\u028c\u0005C\u0000\u0000\u028b\u028a\u0001\u0000\u0000\u0000\u028b\u028c"+
+ "\u0001\u0000\u0000\u0000\u028c\u028d\u0001\u0000\u0000\u0000\u028d\u028e"+
+ "\u0005B\u0000\u0000\u028e\u028f\u0005_\u0000\u0000\u028f\u0294\u0003\u00a4"+
+ "R\u0000\u0290\u0291\u0005:\u0000\u0000\u0291\u0293\u0003\u00a4R\u0000"+
+ "\u0292\u0290\u0001\u0000\u0000\u0000\u0293\u0296\u0001\u0000\u0000\u0000"+
+ "\u0294\u0292\u0001\u0000\u0000\u0000\u0294\u0295\u0001\u0000\u0000\u0000"+
+ "\u0295\u0297\u0001\u0000\u0000\u0000\u0296\u0294\u0001\u0000\u0000\u0000"+
+ "\u0297\u0298\u0005`\u0000\u0000\u0298\u02aa\u0001\u0000\u0000\u0000\u0299"+
+ "\u029b\u0003\u008aE\u0000\u029a\u029c\u0005C\u0000\u0000\u029b\u029a\u0001"+
+ "\u0000\u0000\u0000\u029b\u029c\u0001\u0000\u0000\u0000\u029c\u029d\u0001"+
+ "\u0000\u0000\u0000\u029d\u029e\u0005I\u0000\u0000\u029e\u029f\u0005_\u0000"+
+ "\u0000\u029f\u02a4\u0003\u00a4R\u0000\u02a0\u02a1\u0005:\u0000\u0000\u02a1"+
+ "\u02a3\u0003\u00a4R\u0000\u02a2\u02a0\u0001\u0000\u0000\u0000\u02a3\u02a6"+
+ "\u0001\u0000\u0000\u0000\u02a4\u02a2\u0001\u0000\u0000\u0000\u02a4\u02a5"+
+ "\u0001\u0000\u0000\u0000\u02a5\u02a7\u0001\u0000\u0000\u0000\u02a6\u02a4"+
+ "\u0001\u0000\u0000\u0000\u02a7\u02a8\u0005`\u0000\u0000\u02a8\u02aa\u0001"+
+ "\u0000\u0000\u0000\u02a9\u027b\u0001\u0000\u0000\u0000\u02a9\u0282\u0001"+
+ "\u0000\u0000\u0000\u02a9\u0289\u0001\u0000\u0000\u0000\u02a9\u0299\u0001"+
+ "\u0000\u0000\u0000\u02aa\u0087\u0001\u0000\u0000\u0000\u02ab\u02ae\u0003"+
+ "0\u0018\u0000\u02ac\u02ad\u00058\u0000\u0000\u02ad\u02af\u0003\n\u0005"+
+ "\u0000\u02ae\u02ac\u0001\u0000\u0000\u0000\u02ae\u02af\u0001\u0000\u0000"+
+ "\u0000\u02af\u02b0\u0001\u0000\u0000\u0000\u02b0\u02b1\u00059\u0000\u0000"+
+ "\u02b1\u02b2\u0003\u009aM\u0000\u02b2\u0089\u0001\u0000\u0000\u0000\u02b3"+
+ "\u02b9\u0003\u008cF\u0000\u02b4\u02b5\u0003\u008cF\u0000\u02b5\u02b6\u0003"+
+ "\u00a6S\u0000\u02b6\u02b7\u0003\u008cF\u0000\u02b7\u02b9\u0001\u0000\u0000"+
+ "\u0000\u02b8\u02b3\u0001\u0000\u0000\u0000\u02b8\u02b4\u0001\u0000\u0000"+
+ "\u0000\u02b9\u008b\u0001\u0000\u0000\u0000\u02ba\u02bb\u0006F\uffff\uffff"+
+ "\u0000\u02bb\u02bf\u0003\u008eG\u0000\u02bc\u02bd\u0007\u0005\u0000\u0000"+
+ "\u02bd\u02bf\u0003\u008cF\u0003\u02be\u02ba\u0001\u0000\u0000\u0000\u02be"+
+ "\u02bc\u0001\u0000\u0000\u0000\u02bf\u02c8\u0001\u0000\u0000\u0000\u02c0"+
+ "\u02c1\n\u0002\u0000\u0000\u02c1\u02c2\u0007\u0006\u0000\u0000\u02c2\u02c7"+
+ "\u0003\u008cF\u0003\u02c3\u02c4\n\u0001\u0000\u0000\u02c4\u02c5\u0007"+
+ "\u0005\u0000\u0000\u02c5\u02c7\u0003\u008cF\u0002\u02c6\u02c0\u0001\u0000"+
+ "\u0000\u0000\u02c6\u02c3\u0001\u0000\u0000\u0000\u02c7\u02ca\u0001\u0000"+
+ "\u0000\u0000\u02c8\u02c6\u0001\u0000\u0000\u0000\u02c8\u02c9\u0001\u0000"+
+ "\u0000\u0000\u02c9\u008d\u0001\u0000\u0000\u0000\u02ca\u02c8\u0001\u0000"+
+ "\u0000\u0000\u02cb\u02cc\u0006G\uffff\uffff\u0000\u02cc\u02d4\u0003\u009a"+
+ "M\u0000\u02cd\u02d4\u00030\u0018\u0000\u02ce\u02d4\u0003\u0090H\u0000"+
+ "\u02cf\u02d0\u0005_\u0000\u0000\u02d0\u02d1\u0003\u0084B\u0000\u02d1\u02d2"+
+ "\u0005`\u0000\u0000\u02d2\u02d4\u0001\u0000\u0000\u0000\u02d3\u02cb\u0001"+
+ "\u0000\u0000\u0000\u02d3\u02cd\u0001\u0000\u0000\u0000\u02d3\u02ce\u0001"+
+ "\u0000\u0000\u0000\u02d3\u02cf\u0001\u0000\u0000\u0000\u02d4\u02da\u0001"+
+ "\u0000\u0000\u0000\u02d5\u02d6\n\u0001\u0000\u0000\u02d6\u02d7\u00058"+
+ "\u0000\u0000\u02d7\u02d9\u0003\n\u0005\u0000\u02d8\u02d5\u0001\u0000\u0000"+
+ "\u0000\u02d9\u02dc\u0001\u0000\u0000\u0000\u02da\u02d8\u0001\u0000\u0000"+
+ "\u0000\u02da\u02db\u0001\u0000\u0000\u0000\u02db\u008f\u0001\u0000\u0000"+
+ "\u0000\u02dc\u02da\u0001\u0000\u0000\u0000\u02dd\u02de\u0003\u0092I\u0000"+
+ "\u02de\u02ec\u0005_\u0000\u0000\u02df\u02ed\u0005U\u0000\u0000\u02e0\u02e5"+
+ "\u0003\u0084B\u0000\u02e1\u02e2\u0005:\u0000\u0000\u02e2\u02e4\u0003\u0084"+
+ "B\u0000\u02e3\u02e1\u0001\u0000\u0000\u0000\u02e4\u02e7\u0001\u0000\u0000"+
+ "\u0000\u02e5\u02e3\u0001\u0000\u0000\u0000\u02e5\u02e6\u0001\u0000\u0000"+
+ "\u0000\u02e6\u02ea\u0001\u0000\u0000\u0000\u02e7\u02e5\u0001\u0000\u0000"+
+ "\u0000\u02e8\u02e9\u0005:\u0000\u0000\u02e9\u02eb\u0003\u0094J\u0000\u02ea"+
+ "\u02e8\u0001\u0000\u0000\u0000\u02ea\u02eb\u0001\u0000\u0000\u0000\u02eb"+
+ "\u02ed\u0001\u0000\u0000\u0000\u02ec\u02df\u0001\u0000\u0000\u0000\u02ec"+
+ "\u02e0\u0001\u0000\u0000\u0000\u02ec\u02ed\u0001\u0000\u0000\u0000\u02ed"+
+ "\u02ee\u0001\u0000\u0000\u0000\u02ee\u02ef\u0005`\u0000\u0000\u02ef\u0091"+
+ "\u0001\u0000\u0000\u0000\u02f0\u02f4\u0003B!\u0000\u02f1\u02f4\u0005>"+
+ "\u0000\u0000\u02f2\u02f4\u0005A\u0000\u0000\u02f3\u02f0\u0001\u0000\u0000"+
+ "\u0000\u02f3\u02f1\u0001\u0000\u0000\u0000\u02f3\u02f2\u0001\u0000\u0000"+
+ "\u0000\u02f4\u0093\u0001\u0000\u0000\u0000\u02f5\u02fe\u0005X\u0000\u0000"+
+ "\u02f6\u02fb\u0003\u0096K\u0000\u02f7\u02f8\u0005:\u0000\u0000\u02f8\u02fa"+
+ "\u0003\u0096K\u0000\u02f9\u02f7\u0001\u0000\u0000\u0000\u02fa\u02fd\u0001"+
+ "\u0000\u0000\u0000\u02fb\u02f9\u0001\u0000\u0000\u0000\u02fb\u02fc\u0001"+
+ "\u0000\u0000\u0000\u02fc\u02ff\u0001\u0000\u0000\u0000\u02fd\u02fb\u0001"+
+ "\u0000\u0000\u0000\u02fe\u02f6\u0001\u0000\u0000\u0000\u02fe\u02ff\u0001"+
+ "\u0000\u0000\u0000\u02ff\u0300\u0001\u0000\u0000\u0000\u0300\u0301\u0005"+
+ "Y\u0000\u0000\u0301\u0095\u0001\u0000\u0000\u0000\u0302\u0303\u0003\u00a4"+
+ "R\u0000\u0303\u0304\u00059\u0000\u0000\u0304\u0305\u0003\u0098L\u0000"+
+ "\u0305\u0097\u0001\u0000\u0000\u0000\u0306\u0309\u0003\u009aM\u0000\u0307"+
+ "\u0309\u0003\u0094J\u0000\u0308\u0306\u0001\u0000\u0000\u0000\u0308\u0307"+
+ "\u0001\u0000\u0000\u0000\u0309\u0099\u0001\u0000\u0000\u0000\u030a\u0335"+
+ "\u0005D\u0000\u0000\u030b\u030c\u0003\u00a2Q\u0000\u030c\u030d\u0005a"+
+ "\u0000\u0000\u030d\u0335\u0001\u0000\u0000\u0000\u030e\u0335\u0003\u00a0"+
+ "P\u0000\u030f\u0335\u0003\u00a2Q\u0000\u0310\u0335\u0003\u009cN\u0000"+
+ "\u0311\u0335\u0003>\u001f\u0000\u0312\u0335\u0003\u00a4R\u0000\u0313\u0314"+
+ "\u0005]\u0000\u0000\u0314\u0319\u0003\u009eO\u0000\u0315\u0316\u0005:"+
+ "\u0000\u0000\u0316\u0318\u0003\u009eO\u0000\u0317\u0315\u0001\u0000\u0000"+
+ "\u0000\u0318\u031b\u0001\u0000\u0000\u0000\u0319\u0317\u0001\u0000\u0000"+
+ "\u0000\u0319\u031a\u0001\u0000\u0000\u0000\u031a\u031c\u0001\u0000\u0000"+
+ "\u0000\u031b\u0319\u0001\u0000\u0000\u0000\u031c\u031d\u0005^\u0000\u0000"+
+ "\u031d\u0335\u0001\u0000\u0000\u0000\u031e\u031f\u0005]\u0000\u0000\u031f"+
+ "\u0324\u0003\u009cN\u0000\u0320\u0321\u0005:\u0000\u0000\u0321\u0323\u0003"+
+ "\u009cN\u0000\u0322\u0320\u0001\u0000\u0000\u0000\u0323\u0326\u0001\u0000"+
+ "\u0000\u0000\u0324\u0322\u0001\u0000\u0000\u0000\u0324\u0325\u0001\u0000"+
+ "\u0000\u0000\u0325\u0327\u0001\u0000\u0000\u0000\u0326\u0324\u0001\u0000"+
+ "\u0000\u0000\u0327\u0328\u0005^\u0000\u0000\u0328\u0335\u0001\u0000\u0000"+
+ "\u0000\u0329\u032a\u0005]\u0000\u0000\u032a\u032f\u0003\u00a4R\u0000\u032b"+
+ "\u032c\u0005:\u0000\u0000\u032c\u032e\u0003\u00a4R\u0000\u032d\u032b\u0001"+
+ "\u0000\u0000\u0000\u032e\u0331\u0001\u0000\u0000\u0000\u032f\u032d\u0001"+
+ "\u0000\u0000\u0000\u032f\u0330\u0001\u0000\u0000\u0000\u0330\u0332\u0001"+
+ "\u0000\u0000\u0000\u0331\u032f\u0001\u0000\u0000\u0000\u0332\u0333\u0005"+
+ "^\u0000\u0000\u0333\u0335\u0001\u0000\u0000\u0000\u0334\u030a\u0001\u0000"+
+ "\u0000\u0000\u0334\u030b\u0001\u0000\u0000\u0000\u0334\u030e\u0001\u0000"+
+ "\u0000\u0000\u0334\u030f\u0001\u0000\u0000\u0000\u0334\u0310\u0001\u0000"+
+ "\u0000\u0000\u0334\u0311\u0001\u0000\u0000\u0000\u0334\u0312\u0001\u0000"+
+ "\u0000\u0000\u0334\u0313\u0001\u0000\u0000\u0000\u0334\u031e\u0001\u0000"+
+ "\u0000\u0000\u0334\u0329\u0001\u0000\u0000\u0000\u0335\u009b\u0001\u0000"+
+ "\u0000\u0000\u0336\u0337\u0007\u0007\u0000\u0000\u0337\u009d\u0001\u0000"+
+ "\u0000\u0000\u0338\u033b\u0003\u00a0P\u0000\u0339\u033b\u0003\u00a2Q\u0000"+
+ "\u033a\u0338\u0001\u0000\u0000\u0000\u033a\u0339\u0001\u0000\u0000\u0000"+
+ "\u033b\u009f\u0001\u0000\u0000\u0000\u033c\u033e\u0007\u0005\u0000\u0000"+
+ "\u033d\u033c\u0001\u0000\u0000\u0000\u033d\u033e\u0001\u0000\u0000\u0000"+
+ "\u033e\u033f\u0001\u0000\u0000\u0000\u033f\u0340\u00053\u0000\u0000\u0340"+
+ "\u00a1\u0001\u0000\u0000\u0000\u0341\u0343\u0007\u0005\u0000\u0000\u0342"+
+ "\u0341\u0001\u0000\u0000\u0000\u0342\u0343\u0001\u0000\u0000\u0000\u0343"+
+ "\u0344\u0001\u0000\u0000\u0000\u0344\u0345\u00052\u0000\u0000\u0345\u00a3"+
+ "\u0001\u0000\u0000\u0000\u0346\u0347\u00051\u0000\u0000\u0347\u00a5\u0001"+
+ "\u0000\u0000\u0000\u0348\u0349\u0007\b\u0000\u0000\u0349\u00a7\u0001\u0000"+
+ "\u0000\u0000\u034a\u034b\u0007\t\u0000\u0000\u034b\u034c\u0005n\u0000"+
+ "\u0000\u034c\u034d\u0003\u00aaU\u0000\u034d\u034e\u0003\u00acV\u0000\u034e"+
+ "\u00a9\u0001\u0000\u0000\u0000\u034f\u0350\u0004U\u000f\u0000\u0350\u0352"+
+ "\u0003\u001c\u000e\u0000\u0351\u0353\u0005\u0080\u0000\u0000\u0352\u0351"+
+ "\u0001\u0000\u0000\u0000\u0352\u0353\u0001\u0000\u0000\u0000\u0353\u0354"+
+ "\u0001\u0000\u0000\u0000\u0354\u0355\u0005g\u0000\u0000\u0355\u0358\u0001"+
+ "\u0000\u0000\u0000\u0356\u0358\u0003\u001c\u000e\u0000\u0357\u034f\u0001"+
+ "\u0000\u0000\u0000\u0357\u0356\u0001\u0000\u0000\u0000\u0358\u00ab\u0001"+
+ "\u0000\u0000\u0000\u0359\u035a\u0005F\u0000\u0000\u035a\u035f\u00030\u0018"+
+ "\u0000\u035b\u035c\u0005:\u0000\u0000\u035c\u035e\u00030\u0018\u0000\u035d"+
+ "\u035b\u0001\u0000\u0000\u0000\u035e\u0361\u0001\u0000\u0000\u0000\u035f"+
+ "\u035d\u0001\u0000\u0000\u0000\u035f\u0360\u0001\u0000\u0000\u0000\u0360"+
+ "\u036c\u0001\u0000\u0000\u0000\u0361\u035f\u0001\u0000\u0000\u0000\u0362"+
+ "\u0363\u0005F\u0000\u0000\u0363\u0368\u0003\u00b0X\u0000\u0364\u0365\u0005"+
+ "4\u0000\u0000\u0365\u0367\u0003\u00b0X\u0000\u0366\u0364\u0001\u0000\u0000"+
+ "\u0000\u0367\u036a\u0001\u0000\u0000\u0000\u0368\u0366\u0001\u0000\u0000"+
+ "\u0000\u0368\u0369\u0001\u0000\u0000\u0000\u0369\u036c\u0001\u0000\u0000"+
+ "\u0000\u036a\u0368\u0001\u0000\u0000\u0000\u036b\u0359\u0001\u0000\u0000"+
+ "\u0000\u036b\u0362\u0001\u0000\u0000\u0000\u036c\u00ad\u0001\u0000\u0000"+
+ "\u0000\u036d\u036e\u0003\u008aE\u0000\u036e\u00af\u0001\u0000\u0000\u0000"+
+ "\u036f\u0370\u0003\u008cF\u0000\u0370\u0371\u0003\u00a6S\u0000\u0371\u0372"+
+ "\u0003\u008cF\u0000\u0372\u00b1\u0001\u0000\u0000\u0000T\u00bd\u00c7\u00e3"+
+ "\u00f2\u00f8\u0101\u0107\u0114\u0118\u0123\u0133\u013b\u013f\u0146\u014c"+
+ "\u0151\u015a\u0161\u0167\u0170\u0177\u017f\u0187\u018b\u018f\u0194\u019f"+
+ "\u01a4\u01a8\u01b6\u01c1\u01c7\u01ce\u01d7\u01ee\u01f6\u01f9\u0200\u020b"+
+ "\u0212\u021a\u0228\u0231\u023c\u024a\u0258\u0261\u0269\u026e\u0276\u0278"+
+ "\u027d\u0284\u028b\u0294\u029b\u02a4\u02a9\u02ae\u02b8\u02be\u02c6\u02c8"+
+ "\u02d3\u02da\u02e5\u02ea\u02ec\u02f3\u02fb\u02fe\u0308\u0319\u0324\u032f"+
+ "\u0334\u033a\u033d\u0342\u0352\u0357\u035f\u0368\u036b";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseListener.java
index a2c1d8e7db563..4a16025705af1 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseListener.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseListener.java
@@ -1028,18 +1028,6 @@ public class EsqlBaseParserBaseListener implements EsqlBaseParserListener {
* The default implementation does nothing.
*/
@Override public void exitComparison(EsqlBaseParser.ComparisonContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void enterComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) { }
- /**
- * {@inheritDoc}
- *
- * The default implementation does nothing.
- */
- @Override public void exitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) { }
/**
* {@inheritDoc}
*
@@ -1436,6 +1424,30 @@ public class EsqlBaseParserBaseListener implements EsqlBaseParserListener {
* The default implementation does nothing.
*/
@Override public void exitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) { }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation does nothing.
+ */
+ @Override public void enterJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx) { }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation does nothing.
+ */
+ @Override public void exitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx) { }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation does nothing.
+ */
+ @Override public void enterJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx) { }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation does nothing.
+ */
+ @Override public void exitJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx) { }
/**
* {@inheritDoc}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseVisitor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseVisitor.java
index 2d8f85fd0a6a1..e67b6732d6fa7 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseVisitor.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserBaseVisitor.java
@@ -608,13 +608,6 @@ public class EsqlBaseParserBaseVisitor extends AbstractParseTreeVisitor im
* {@link #visitChildren} on {@code ctx}.
*/
@Override public T visitComparison(EsqlBaseParser.ComparisonContext ctx) { return visitChildren(ctx); }
- /**
- * {@inheritDoc}
- *
- * The default implementation returns the result of calling
- * {@link #visitChildren} on {@code ctx}.
- */
- @Override public T visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@@ -846,4 +839,18 @@ public class EsqlBaseParserBaseVisitor extends AbstractParseTreeVisitor im
* {@link #visitChildren} on {@code ctx}.
*/
@Override public T visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx) { return visitChildren(ctx); }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation returns the result of calling
+ * {@link #visitChildren} on {@code ctx}.
+ */
+ @Override public T visitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx) { return visitChildren(ctx); }
+ /**
+ * {@inheritDoc}
+ *
+ * The default implementation returns the result of calling
+ * {@link #visitChildren} on {@code ctx}.
+ */
+ @Override public T visitJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx) { return visitChildren(ctx); }
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
index 49bb4302d0352..10c47a5c8f29f 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserListener.java
@@ -901,16 +901,6 @@ public interface EsqlBaseParserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitComparison(EsqlBaseParser.ComparisonContext ctx);
- /**
- * Enter a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
- * @param ctx the parse tree
- */
- void enterComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
- /**
- * Exit a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
- * @param ctx the parse tree
- */
- void exitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
/**
* Enter a parse tree produced by the {@code operatorExpressionDefault}
* labeled alternative in {@link EsqlBaseParser#operatorExpression}.
@@ -1281,4 +1271,24 @@ public interface EsqlBaseParserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx);
+ /**
+ * Enter a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * @param ctx the parse tree
+ */
+ void enterJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ /**
+ * Exit a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * @param ctx the parse tree
+ */
+ void exitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ /**
+ * Enter a parse tree produced by {@link EsqlBaseParser#joinPredicateExpression}.
+ * @param ctx the parse tree
+ */
+ void enterJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx);
+ /**
+ * Exit a parse tree produced by {@link EsqlBaseParser#joinPredicateExpression}.
+ * @param ctx the parse tree
+ */
+ void exitJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
index 7c2c4d4c8f306..8dc9df75d6b22 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParserVisitor.java
@@ -545,12 +545,6 @@ public interface EsqlBaseParserVisitor extends ParseTreeVisitor {
* @return the visitor result
*/
T visitComparison(EsqlBaseParser.ComparisonContext ctx);
- /**
- * Visit a parse tree produced by {@link EsqlBaseParser#comparisonExpression}.
- * @param ctx the parse tree
- * @return the visitor result
- */
- T visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx);
/**
* Visit a parse tree produced by the {@code operatorExpressionDefault}
* labeled alternative in {@link EsqlBaseParser#operatorExpression}.
@@ -769,4 +763,16 @@ public interface EsqlBaseParserVisitor extends ParseTreeVisitor {
* @return the visitor result
*/
T visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLookupJoinContext ctx);
+ /**
+ * Visit a parse tree produced by {@link EsqlBaseParser#joinPredicate}.
+ * @param ctx the parse tree
+ * @return the visitor result
+ */
+ T visitJoinPredicate(EsqlBaseParser.JoinPredicateContext ctx);
+ /**
+ * Visit a parse tree produced by {@link EsqlBaseParser#joinPredicateExpression}.
+ * @param ctx the parse tree
+ * @return the visitor result
+ */
+ T visitJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
index d3ece791c3047..b42aa68ec946a 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/ExpressionBuilder.java
@@ -608,16 +608,21 @@ public Expression visitArithmeticBinary(EsqlBaseParser.ArithmeticBinaryContext c
@Override
public Expression visitComparison(EsqlBaseParser.ComparisonContext ctx) {
- return expression(ctx.comparisonExpression());
+ Expression left = expression(ctx.left);
+ Expression right = expression(ctx.right);
+ TerminalNode op = (TerminalNode) ctx.comparisonOperator().getChild(0);
+ return buildComparison(source(ctx), left, right, op);
}
@Override
- public Expression visitComparisonExpression(EsqlBaseParser.ComparisonExpressionContext ctx) {
+ public Expression visitJoinPredicateExpression(EsqlBaseParser.JoinPredicateExpressionContext ctx) {
Expression left = expression(ctx.left);
Expression right = expression(ctx.right);
TerminalNode op = (TerminalNode) ctx.comparisonOperator().getChild(0);
+ return buildComparison(source(ctx), left, right, op);
+ }
- Source source = source(ctx);
+ private Expression buildComparison(Source source, Expression left, Expression right, TerminalNode op) {
ZoneId zoneId = DateUtils.UTC;
return switch (op.getSymbol().getType()) {
@@ -628,7 +633,7 @@ public Expression visitComparisonExpression(EsqlBaseParser.ComparisonExpressionC
case EsqlBaseParser.LTE -> new LessThanOrEqual(source, left, right, zoneId);
case EsqlBaseParser.GT -> new GreaterThan(source, left, right, zoneId);
case EsqlBaseParser.GTE -> new GreaterThanOrEqual(source, left, right, zoneId);
- default -> throw new ParsingException(source, "Unknown comparison operator {}", source.text());
+ default -> throw new ParsingException(source, "Unknown comparison operator {}", op.getText());
};
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
index 340c583738e0e..e2cae4555659a 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java
@@ -698,7 +698,7 @@ public JoinInfo visitExpressionBasedLookupJoin(EsqlBaseParser.ExpressionBasedLoo
if (LOOKUP_JOIN_ON_BOOLEAN_EXPRESSION.isEnabled() == false) {
throw new ParsingException(ctx.getText(), "JOIN ON clause only supports fields at the moment, found [{}]", ctx.getText());
}
- var predicates = visitList(this, ctx.comparisonExpression(), Expression.class);
+ var predicates = visitList(this, ctx.joinPredicateExpression(), Expression.class);
List joinFields = new ArrayList<>(predicates.size());
List joinExpressions = new ArrayList<>(predicates.size());
for (var f : predicates) {
From fb85f31e5c49d57ada2c596e0a7da36d8d63e07a Mon Sep 17 00:00:00 2001
From: Julian Kiryakov
Date: Wed, 10 Sep 2025 16:38:06 -0400
Subject: [PATCH 12/30] Address more feedback, refactoring
---
.../xpack/esql/analysis/Analyzer.java | 15 +--
.../enrich/BinaryComparisonQueryList.java | 3 +-
.../esql/enrich/ExpressionQueryList.java | 92 ++++++++++++-------
.../esql/enrich/LookupFromIndexService.java | 11 ++-
4 files changed, 76 insertions(+), 45 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
index ffa64e49ac1df..aa3a7febaccb7 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
@@ -24,6 +24,7 @@
import org.elasticsearch.xpack.esql.core.capabilities.Resolvables;
import org.elasticsearch.xpack.esql.core.expression.Alias;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
+import org.elasticsearch.xpack.esql.core.expression.AttributeSet;
import org.elasticsearch.xpack.esql.core.expression.EmptyAttribute;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Expressions;
@@ -713,8 +714,8 @@ private LogicalPlan resolveLookup(Lookup l, List childrenOutput) {
private List resolveJoinFiltersAndSwapIfNeeded(
List filters,
- List leftOutput,
- List rightOutput
+ AttributeSet leftOutput,
+ AttributeSet rightOutput
) {
if (filters.isEmpty()) {
return emptyList();
@@ -730,7 +731,7 @@ private List resolveJoinFiltersAndSwapIfNeeded(
return resolvedFilters;
}
- private Expression resolveAndOrientJoinCondition(Expression condition, List leftOutput, List rightOutput) {
+ private Expression resolveAndOrientJoinCondition(Expression condition, AttributeSet leftOutput, AttributeSet rightOutput) {
if (condition instanceof EsqlBinaryComparison comp
&& comp.left() instanceof Attribute leftAttr
&& comp.right() instanceof Attribute rightAttr) {
@@ -751,7 +752,9 @@ private Expression resolveAndOrientJoinCondition(Expression condition, List queryLists;
private final List preJoinFilters = new ArrayList<>();
private final SearchExecutionContext context;
- boolean isExpressionJoin = false;
private final AliasFilter aliasFilter;
- public ExpressionQueryList(
+ private ExpressionQueryList(
List queryLists,
SearchExecutionContext context,
PhysicalPlan rightPreJoinPlan,
ClusterService clusterService,
- LookupFromIndexService.TransportRequest request,
- AliasFilter aliasFilter,
- Warnings warnings
+ AliasFilter aliasFilter
) {
- if (queryLists.size() < 2 && (rightPreJoinPlan instanceof FilterExec == false) && request.getJoinOnConditions() == null) {
- throw new IllegalArgumentException("ExpressionQueryList must have at least two QueryLists or a pre-join filter");
- }
this.queryLists = new ArrayList<>(queryLists);
this.context = context;
this.aliasFilter = aliasFilter;
- buildJoinOnConditions(request, clusterService, warnings);
buildPreJoinFilter(rightPreJoinPlan, clusterService);
+ }
+ public static ExpressionQueryList fieldBasedJoin(
+ List queryLists,
+ SearchExecutionContext context,
+ PhysicalPlan rightPreJoinPlan,
+ ClusterService clusterService,
+ AliasFilter aliasFilter
+ ) {
+ if (queryLists.size() < 2 && (rightPreJoinPlan instanceof FilterExec == false)) {
+ throw new IllegalArgumentException("ExpressionQueryList must have at least two QueryLists or a pre-join filter");
+ }
+ return new ExpressionQueryList(queryLists, context, rightPreJoinPlan, clusterService, aliasFilter);
}
- private void buildJoinOnConditions(LookupFromIndexService.TransportRequest request, ClusterService clusterService, Warnings warnings) {
- // we support 2 modes of operation:
- // Join on fields
- // Join on AND of binary comparisons
- Expression filter = request.getJoinOnConditions();
- if (filter == null) {
- // the join on conditions are already populated via the queryLists
- // there is nothing to do here
- return;
- } else if (queryLists.isEmpty() == false) {
- throw new IllegalArgumentException("ExpressionQueryList called with both join on expression and join on fields");
+ public static ExpressionQueryList expressionBasedJoin(
+ SearchExecutionContext context,
+ PhysicalPlan rightPreJoinPlan,
+ ClusterService clusterService,
+ LookupFromIndexService.TransportRequest request,
+ AliasFilter aliasFilter,
+ Warnings warnings
+ ) {
+ if (request.getJoinOnConditions() == null) {
+ throw new IllegalStateException("expressionBasedJoin must have join conditions");
}
- List expressions = Predicates.splitAnd(filter);
+ ExpressionQueryList expressionQueryList = new ExpressionQueryList(
+ new ArrayList<>(),
+ context,
+ rightPreJoinPlan,
+ clusterService,
+ aliasFilter
+ );
+ expressionQueryList.buildJoinOnForExpressionJoin(
+ request.getJoinOnConditions(),
+ request.getMatchFields(),
+ request.getInputPage(),
+ clusterService,
+ warnings
+ );
+ return expressionQueryList;
+ }
+
+ private void buildJoinOnForExpressionJoin(
+ Expression joinOnConditions,
+ List matchFields,
+ Page inputPage,
+ ClusterService clusterService,
+ Warnings warnings
+ ) {
+ List expressions = Predicates.splitAnd(joinOnConditions);
for (Expression expr : expressions) {
if (expr instanceof EsqlBinaryComparison binaryComparison) {
// the left side comes from the page that was sent to the lookup node
// the right side is the field from the lookup index
- // check if the left side is in the request.getMatchFields()
- // if it is its corresponding page is the corresponding number in request.inputPage
+ // check if the left side is in the matchFields
+ // if it is its corresponding page is the corresponding number in inputPage
Expression left = binaryComparison.left();
if (left instanceof Attribute leftAttribute) {
boolean matched = false;
- for (int i = 0; i < request.getMatchFields().size(); i++) {
- if (request.getMatchFields().get(i).fieldName().equals(leftAttribute.name())) {
- Block block = request.getInputPage().getBlock(i);
+ for (int i = 0; i < matchFields.size(); i++) {
+ if (matchFields.get(i).fieldName().equals(leftAttribute.name())) {
+ Block block = inputPage.getBlock(i);
Expression right = binaryComparison.right();
if (right instanceof Attribute rightAttribute) {
MappedFieldType fieldType = context.getFieldType(rightAttribute.name());
if (fieldType != null) {
- isExpressionJoin = true;
queryLists.add(
new BinaryComparisonQueryList(
fieldType,
@@ -115,30 +143,30 @@ private void buildJoinOnConditions(LookupFromIndexService.TransportRequest reque
matched = true;
break;
} else {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"Could not find field [" + rightAttribute.name() + "] in the lookup join index"
);
}
} else {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"Only field from the right dataset are supported on the right of the join on condition but got: " + expr
);
}
}
}
if (matched == false) {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"Could not find field [" + leftAttribute.name() + "] in the left side of the lookup join"
);
}
} else {
- throw new IllegalArgumentException(
+ throw new IllegalStateException(
"Only field from the left dataset are supported on the left of the join on condition but got: " + expr
);
}
} else {
// we only support binary comparisons in the join on conditions
- throw new IllegalArgumentException("Only binary comparisons are supported in join ON conditions, but got: " + expr);
+ throw new IllegalStateException("Only binary comparisons are supported in join ON conditions, but got: " + expr);
}
}
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
index 309b7210c1a1f..2f3d9ae1dbdca 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/LookupFromIndexService.java
@@ -109,7 +109,9 @@ protected LookupEnrichQueryGenerator queryList(
Block inputBlock,
Warnings warnings
) {
+ PhysicalPlan physicalPlan = localLookupNodePlanning(request.rightPreJoinPlan);
if (request.joinOnConditions == null) {
+ // this is a field based join
List queryLists = new ArrayList<>();
for (int i = 0; i < request.matchFields.size(); i++) {
MatchConfig matchField = request.matchFields.get(i);
@@ -122,15 +124,14 @@ protected LookupEnrichQueryGenerator queryList(
).onlySingleValues(warnings, "LOOKUP JOIN encountered multi-value");
queryLists.add(q);
}
-
- PhysicalPlan physicalPlan = localLookupNodePlanning(request.rightPreJoinPlan);
if (queryLists.size() == 1 && physicalPlan instanceof FilterExec == false) {
return queryLists.getFirst();
}
- return new ExpressionQueryList(queryLists, context, physicalPlan, clusterService, request, aliasFilter, warnings);
+ return ExpressionQueryList.fieldBasedJoin(queryLists, context, physicalPlan, clusterService, aliasFilter);
+ } else {
+ // this is an expression based join
+ return ExpressionQueryList.expressionBasedJoin(context, physicalPlan, clusterService, request, aliasFilter, warnings);
}
- PhysicalPlan physicalPlan = localLookupNodePlanning(request.rightPreJoinPlan);
- return new ExpressionQueryList(List.of(), context, physicalPlan, clusterService, request, aliasFilter, warnings);
}
From ea6f6b925e6236ad777a293086cc972ac2d07ed8 Mon Sep 17 00:00:00 2001
From: Alexander Spies
Date: Thu, 11 Sep 2025 13:32:44 +0200
Subject: [PATCH 13/30] Remove matchFields from JoinConfig, HashJoinExec
---
.../xpack/esql/analysis/Analyzer.java | 10 ++--
.../PruneLeftJoinOnNullMatchingField.java | 2 +-
.../xpack/esql/plan/logical/InlineStats.java | 2 +-
.../xpack/esql/plan/logical/Lookup.java | 2 +-
.../esql/plan/logical/join/InlineJoin.java | 16 ++-----
.../xpack/esql/plan/logical/join/Join.java | 4 +-
.../esql/plan/logical/join/JoinConfig.java | 48 +++++++++----------
.../esql/plan/logical/join/LookupJoin.java | 7 ++-
.../esql/plan/physical/HashJoinExec.java | 24 ++++------
.../esql/planner/mapper/LocalMapper.java | 1 -
.../xpack/esql/planner/mapper/Mapper.java | 1 -
.../optimizer/PhysicalPlanOptimizerTests.java | 14 ++----
.../HashJoinExecSerializationTests.java | 15 +++---
13 files changed, 52 insertions(+), 94 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
index aa3a7febaccb7..80c5285a76d38 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java
@@ -782,7 +782,7 @@ private Join resolveLookupJoin(LookupJoin join) {
name,
"Only LEFT join is supported with USING"
);
- return join.withConfig(new JoinConfig(type, singletonList(errorAttribute), emptyList(), emptyList(), null));
+ return join.withConfig(new JoinConfig(type, singletonList(errorAttribute), emptyList(), null));
}
List leftKeys = new ArrayList<>();
List rightKeys = new ArrayList<>();
@@ -806,17 +806,13 @@ private Join resolveLookupJoin(LookupJoin join) {
throw new EsqlIllegalArgumentException("Unsupported join filter expression: " + expression);
}
}
- Set matchKeysSet = new HashSet<>(leftKeys);
- matchKeysSet.addAll(rightKeys);
- matchKeys = new ArrayList<>(matchKeysSet);
} else {
// resolve the using columns against the left and the right side then assemble the new join config
leftKeys = resolveUsingColumns(cols, join.left().output(), "left");
rightKeys = resolveUsingColumns(cols, join.right().output(), "right");
- matchKeys = leftKeys;
}
- config = new JoinConfig(coreJoin, matchKeys, leftKeys, rightKeys, Predicates.combineAnd(resolvedFilters));
+ config = new JoinConfig(coreJoin, leftKeys, rightKeys, Predicates.combineAnd(resolvedFilters));
return new LookupJoin(join.source(), join.left(), join.right(), config, join.isRemote());
} else if (type != JoinTypes.LEFT) {
// everything else is unsupported for now
@@ -824,7 +820,7 @@ private Join resolveLookupJoin(LookupJoin join) {
// more than once.
UnresolvedAttribute errorAttribute = new UnresolvedAttribute(join.source(), "unsupported", "Unsupported join type");
// add error message
- return join.withConfig(new JoinConfig(type, singletonList(errorAttribute), emptyList(), emptyList(), null));
+ return join.withConfig(new JoinConfig(type, singletonList(errorAttribute), emptyList(), null));
}
return join;
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/local/PruneLeftJoinOnNullMatchingField.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/local/PruneLeftJoinOnNullMatchingField.java
index e46ab04e8fc91..418a292c3e154 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/local/PruneLeftJoinOnNullMatchingField.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/local/PruneLeftJoinOnNullMatchingField.java
@@ -40,7 +40,7 @@ protected LogicalPlan rule(Join join, LogicalOptimizerContext ctx) {
if (join.config().type() == LEFT) { // other types will have different replacement logic
AttributeMap attributeMap = RuleUtils.foldableReferences(join, ctx);
- for (var attr : AttributeSet.of(join.config().matchFields())) {
+ for (var attr : AttributeSet.of(join.config().leftFields())) {
var resolved = attributeMap.resolve(attr);
if (resolved != null && isGuaranteedNull(resolved)) {
plan = replaceJoin(join);
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/InlineStats.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/InlineStats.java
index 4531e8958915f..889c544599185 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/InlineStats.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/InlineStats.java
@@ -118,7 +118,7 @@ private JoinConfig joinConfig() {
}
}
}
- return new JoinConfig(JoinTypes.LEFT, namedGroupings, leftFields, rightFields, null);
+ return new JoinConfig(JoinTypes.LEFT, leftFields, rightFields, null);
}
@Override
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
index 832001bb1c718..697eff24006d8 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java
@@ -115,7 +115,7 @@ public JoinConfig joinConfig() {
}
}
}
- return new JoinConfig(JoinTypes.LEFT, matchFields, leftFields, rightFields, null);
+ return new JoinConfig(JoinTypes.LEFT, leftFields, rightFields, null);
}
@Override
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/InlineJoin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/InlineJoin.java
index 6d881b7031a43..0a0ea2a350d19 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/InlineJoin.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/InlineJoin.java
@@ -62,7 +62,7 @@ public static LogicalPlan stubSource(UnaryPlan sourcePlan, LogicalPlan target) {
* Keep the join in place or replace it with an Eval in case no grouping is necessary.
*/
public static LogicalPlan inlineData(InlineJoin target, LocalRelation data) {
- if (target.config().matchFields().isEmpty()) {
+ if (target.config().leftFields().isEmpty()) {
List schema = data.output();
Block[] blocks = data.supplier().get();
List aliases = new ArrayList<>(schema.size());
@@ -165,11 +165,10 @@ public InlineJoin(
LogicalPlan left,
LogicalPlan right,
JoinType type,
- List matchFields,
List leftFields,
List rightFields
) {
- super(source, left, right, type, matchFields, leftFields, rightFields, null);
+ super(source, left, right, type, leftFields, rightFields, null);
}
private static InlineJoin readFrom(StreamInput in) throws IOException {
@@ -192,16 +191,7 @@ protected NodeInfo info() {
// Do not just add the JoinConfig as a whole - this would prevent correctly registering the
// expressions and references.
JoinConfig config = config();
- return NodeInfo.create(
- this,
- InlineJoin::new,
- left(),
- right(),
- config.type(),
- config.matchFields(),
- config.leftFields(),
- config.rightFields()
- );
+ return NodeInfo.create(this, InlineJoin::new, left(), right(), config.type(), config.leftFields(), config.rightFields());
}
@Override
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
index 9ee063e344800..fbdecadb7f1b9 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/Join.java
@@ -116,13 +116,12 @@ public Join(
LogicalPlan left,
LogicalPlan right,
JoinType type,
- List matchFields,
List leftFields,
List rightFields,
Expression joinOnConditions
) {
super(source, left, right);
- this.config = new JoinConfig(type, matchFields, leftFields, rightFields, joinOnConditions);
+ this.config = new JoinConfig(type, leftFields, rightFields, joinOnConditions);
}
public Join(StreamInput in) throws IOException {
@@ -170,7 +169,6 @@ protected NodeInfo info() {
left(),
right(),
config.type(),
- config.matchFields(),
config.leftFields(),
config.rightFields(),
config.joinOnConditions()
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
index 869c935151fa8..06e0e8d71736d 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
@@ -23,41 +23,45 @@
/**
* Configuration for a {@code JOIN} style operation.
*/
-// TODO: this class needs refactoring into a more general form (expressions) since it's currently contains
-// both the condition (equi-join) between the left and right field as well as the output of the join keys
-// which makes sense only for USING clause - which is better resolved in the analyzer (based on the names)
-// hence why for now the attributes are set inside the analyzer
public final class JoinConfig implements Writeable {
private final JoinType type;
- private final List matchFields;
private final List leftFields;
private final List rightFields;
private final Expression joinOnConditions;
/**
* @param type type of join
- * @param matchFields fields either from the left or right fields which decide which side is kept
- * @param leftFields matched with the right fields
- * @param rightFields matched with the left fields
- * @param joinOnConditions join conditions for expression based join. If null, we assume equi-join on the left/right fields
+ * @param leftFields fields from the left child to join on
+ * @param rightFields fields from the right child to join on
+ * @param joinOnConditions join conditions for expression based joins. If null, we assume equi-join on the left/right fields
*/
- public JoinConfig(
+ public JoinConfig(JoinType type, List leftFields, List rightFields, Expression joinOnConditions) {
+ this.type = type;
+ this.leftFields = leftFields;
+ this.rightFields = rightFields;
+ this.joinOnConditions = joinOnConditions;
+ }
+
+ /**
+ * Legacy constructor that included the match fields, which were always the left fields.
+ * They are kept here for serialization compatibility, but are not used anymore.
+ */
+ // TODO: Remove
+ @Deprecated(forRemoval = true)
+ private JoinConfig(
JoinType type,
List matchFields,
List leftFields,
List rightFields,
Expression joinOnConditions
) {
- this.type = type;
- this.matchFields = matchFields;
- this.leftFields = leftFields;
- this.rightFields = rightFields;
- this.joinOnConditions = joinOnConditions;
+ this(type, leftFields, rightFields, joinOnConditions);
}
public JoinConfig(StreamInput in) throws IOException {
this(
JoinTypes.readFrom(in),
+ // TODO we read the match fields for legacy reasons, they are not used anymore.
in.readNamedWriteableCollectionAsList(Attribute.class),
in.readNamedWriteableCollectionAsList(Attribute.class),
in.readNamedWriteableCollectionAsList(Attribute.class),
@@ -75,7 +79,8 @@ private static Expression readJoinConditions(StreamInput in) throws IOException
@Override
public void writeTo(StreamOutput out) throws IOException {
type.writeTo(out);
- out.writeNamedWriteableCollection(matchFields);
+ // TODO we write the match fields for legacy reasons, they used to always be the left fields.
+ out.writeNamedWriteableCollection(leftFields);
out.writeNamedWriteableCollection(leftFields);
out.writeNamedWriteableCollection(rightFields);
if (out.getTransportVersion().onOrAfter(TransportVersions.ESQL_LOOKUP_JOIN_ON_EXPRESSION)) {
@@ -87,7 +92,6 @@ public void writeTo(StreamOutput out) throws IOException {
public boolean expressionsResolved() {
return type.resolved()
- && Resolvables.resolved(matchFields)
&& Resolvables.resolved(leftFields)
&& Resolvables.resolved(rightFields)
&& (joinOnConditions == null || joinOnConditions.resolved());
@@ -97,10 +101,6 @@ public JoinType type() {
return type;
}
- public List matchFields() {
- return matchFields;
- }
-
public List leftFields() {
return leftFields;
}
@@ -119,7 +119,6 @@ public boolean equals(Object obj) {
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (JoinConfig) obj;
return Objects.equals(this.type, that.type)
- && Objects.equals(this.matchFields, that.matchFields)
&& Objects.equals(this.leftFields, that.leftFields)
&& Objects.equals(this.rightFields, that.rightFields)
&& Objects.equals(this.joinOnConditions, that.joinOnConditions);
@@ -127,7 +126,7 @@ public boolean equals(Object obj) {
@Override
public int hashCode() {
- return Objects.hash(type, matchFields, leftFields, rightFields, joinOnConditions);
+ return Objects.hash(type, leftFields, rightFields, joinOnConditions);
}
@Override
@@ -136,9 +135,6 @@ public String toString() {
+ "type="
+ type
+ ", "
- + "matchFields="
- + matchFields
- + ", "
+ "leftFields="
+ leftFields
+ ", "
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
index 8b1f8fbad2a5e..665bcdcf6a05a 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/LookupJoin.java
@@ -28,6 +28,7 @@
/**
* Lookup join - specialized LEFT (OUTER) JOIN between the main left side and a lookup index (index_mode = lookup) on the right.
+ * This is only used during parsing and substituted to a regular {@link Join} during analysis.
*/
public class LookupJoin extends Join implements SurrogateLogicalPlan, TelemetryAware, PostAnalysisVerificationAware {
@@ -39,7 +40,7 @@ public LookupJoin(
boolean isRemote,
@Nullable Expression joinOnConditions
) {
- this(source, left, right, new UsingJoinType(LEFT, joinFields), emptyList(), emptyList(), emptyList(), isRemote, joinOnConditions);
+ this(source, left, right, new UsingJoinType(LEFT, joinFields), emptyList(), emptyList(), isRemote, joinOnConditions);
}
public LookupJoin(
@@ -47,13 +48,12 @@ public LookupJoin(
LogicalPlan left,
LogicalPlan right,
JoinType type,
- List joinFields,
List leftFields,
List rightFields,
boolean isRemote,
Expression joinOnConditions
) {
- this(source, left, right, new JoinConfig(type, joinFields, leftFields, rightFields, joinOnConditions), isRemote);
+ this(source, left, right, new JoinConfig(type, leftFields, rightFields, joinOnConditions), isRemote);
}
public LookupJoin(Source source, LogicalPlan left, LogicalPlan right, JoinConfig joinConfig) {
@@ -86,7 +86,6 @@ protected NodeInfo info() {
left(),
right(),
config().type(),
- config().matchFields(),
config().leftFields(),
config().rightFields(),
isRemote(),
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExec.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExec.java
index 55a34af436f8a..58e10d76db313 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExec.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExec.java
@@ -32,7 +32,6 @@ public class HashJoinExec extends BinaryExec implements EstimatesRowSize {
HashJoinExec::new
);
- private final List matchFields;
private final List leftFields;
private final List rightFields;
private final List addedFields;
@@ -43,13 +42,11 @@ public HashJoinExec(
Source source,
PhysicalPlan left,
PhysicalPlan hashData,
- List matchFields,
List leftFields,
List rightFields,
List addedFields
) {
super(source, left, hashData);
- this.matchFields = matchFields;
this.leftFields = leftFields;
this.rightFields = rightFields;
this.addedFields = addedFields;
@@ -57,7 +54,8 @@ public HashJoinExec(
private HashJoinExec(StreamInput in) throws IOException {
super(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(PhysicalPlan.class), in.readNamedWriteable(PhysicalPlan.class));
- this.matchFields = in.readNamedWriteableCollectionAsList(Attribute.class);
+ // TODO: clean up, we used to read the match fields here.
+ in.readNamedWriteableCollectionAsList(Attribute.class);
this.leftFields = in.readNamedWriteableCollectionAsList(Attribute.class);
this.rightFields = in.readNamedWriteableCollectionAsList(Attribute.class);
this.addedFields = in.readNamedWriteableCollectionAsList(Attribute.class);
@@ -66,7 +64,8 @@ private HashJoinExec(StreamInput in) throws IOException {
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
- out.writeNamedWriteableCollection(matchFields);
+ // TODO: clean up, we used to read the match fields here.
+ out.writeNamedWriteableCollection(null);
out.writeNamedWriteableCollection(leftFields);
out.writeNamedWriteableCollection(rightFields);
out.writeNamedWriteableCollection(addedFields);
@@ -81,10 +80,6 @@ public PhysicalPlan joinData() {
return right();
}
- public List matchFields() {
- return matchFields;
- }
-
public List leftFields() {
return leftFields;
}
@@ -142,12 +137,12 @@ public AttributeSet rightReferences() {
@Override
public HashJoinExec replaceChildren(PhysicalPlan left, PhysicalPlan right) {
- return new HashJoinExec(source(), left, right, matchFields, leftFields, rightFields, addedFields);
+ return new HashJoinExec(source(), left, right, leftFields, rightFields, addedFields);
}
@Override
protected NodeInfo extends PhysicalPlan> info() {
- return NodeInfo.create(this, HashJoinExec::new, left(), right(), matchFields, leftFields, rightFields, addedFields);
+ return NodeInfo.create(this, HashJoinExec::new, left(), right(), leftFields, rightFields, addedFields);
}
@Override
@@ -162,14 +157,11 @@ public boolean equals(Object o) {
return false;
}
HashJoinExec hash = (HashJoinExec) o;
- return matchFields.equals(hash.matchFields)
- && leftFields.equals(hash.leftFields)
- && rightFields.equals(hash.rightFields)
- && addedFields.equals(hash.addedFields);
+ return leftFields.equals(hash.leftFields) && rightFields.equals(hash.rightFields) && addedFields.equals(hash.addedFields);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), matchFields, leftFields, rightFields, addedFields);
+ return Objects.hash(super.hashCode(), leftFields, rightFields, addedFields);
}
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/LocalMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/LocalMapper.java
index 56682e3aa568f..8aa9255f44dd3 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/LocalMapper.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/LocalMapper.java
@@ -108,7 +108,6 @@ private PhysicalPlan mapBinary(BinaryPlan binary) {
join.source(),
left,
localData,
- config.matchFields(),
config.leftFields(),
config.rightFields(),
join.rightOutputFields()
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/Mapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/Mapper.java
index 57da1112f83a0..dd9b733d97872 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/Mapper.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/Mapper.java
@@ -226,7 +226,6 @@ private PhysicalPlan mapBinary(BinaryPlan bp) {
join.source(),
left,
localData,
- config.matchFields(),
config.leftFields(),
config.rightFields(),
join.rightOutputFields()
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
index 9e255d9322e73..250237f8d6fdc 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
@@ -2936,15 +2936,7 @@ public void testVerifierOnMissingReferencesWithBinaryPlans() throws Exception {
var planWithInvalidJoinRightSide = plan.transformUp(
LookupJoinExec.class,
// LookupJoinExec.rightReferences() is currently EMPTY (hack); use a HashJoinExec instead.
- join -> new HashJoinExec(
- join.source(),
- join.left(),
- join.left(),
- join.leftFields(),
- join.leftFields(),
- join.rightFields(),
- join.output()
- )
+ join -> new HashJoinExec(join.source(), join.left(), join.left(), join.leftFields(), join.rightFields(), join.output())
);
e = expectThrows(IllegalStateException.class, () -> physicalPlanOptimizer.verify(planWithInvalidJoinRightSide, plan.output()));
@@ -7489,7 +7481,7 @@ public void testLookupSimple() {
}
PhysicalPlan plan = physicalPlan(query);
var join = as(plan, HashJoinExec.class);
- assertMap(join.matchFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
+ assertMap(join.leftFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
assertMap(
join.output().stream().map(Object::toString).toList(),
matchesList().item(startsWith("_meta_field{f}"))
@@ -7542,7 +7534,7 @@ public void testLookupThenProject() {
var outerProject = as(plan, ProjectExec.class);
assertThat(outerProject.projections().toString(), containsString("AS lang_name"));
var join = as(outerProject.child(), HashJoinExec.class);
- assertMap(join.matchFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
+ assertMap(join.leftFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
assertMap(
join.output().stream().map(Object::toString).toList(),
matchesList().item(startsWith("_meta_field{f}"))
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExecSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExecSerializationTests.java
index 78ff1a5973ea3..f19b727171cb8 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExecSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/physical/HashJoinExecSerializationTests.java
@@ -18,11 +18,10 @@ public static HashJoinExec randomHashJoinExec(int depth) {
Source source = randomSource();
PhysicalPlan child = randomChild(depth);
LocalSourceExec joinData = LocalSourceExecSerializationTests.randomLocalSourceExec();
- List matchFields = randomFields();
List leftFields = randomFields();
List rightFields = randomFields();
List output = randomFields();
- return new HashJoinExec(source, child, joinData, matchFields, leftFields, rightFields, output);
+ return new HashJoinExec(source, child, joinData, leftFields, rightFields, output);
}
private static List randomFields() {
@@ -38,20 +37,18 @@ protected HashJoinExec createTestInstance() {
protected HashJoinExec mutateInstance(HashJoinExec instance) throws IOException {
PhysicalPlan child = instance.left();
PhysicalPlan joinData = instance.joinData();
- List matchFields = randomFieldAttributes(1, 5, false);
List leftFields = randomFieldAttributes(1, 5, false);
List rightFields = randomFieldAttributes(1, 5, false);
List output = randomFieldAttributes(1, 5, false);
- switch (between(0, 5)) {
+ switch (between(0, 4)) {
case 0 -> child = randomValueOtherThan(child, () -> randomChild(0));
case 1 -> joinData = randomValueOtherThan(joinData, LocalSourceExecSerializationTests::randomLocalSourceExec);
- case 2 -> matchFields = randomValueOtherThan(matchFields, HashJoinExecSerializationTests::randomFields);
- case 3 -> leftFields = randomValueOtherThan(leftFields, HashJoinExecSerializationTests::randomFields);
- case 4 -> rightFields = randomValueOtherThan(rightFields, HashJoinExecSerializationTests::randomFields);
- case 5 -> output = randomValueOtherThan(output, HashJoinExecSerializationTests::randomFields);
+ case 2 -> leftFields = randomValueOtherThan(leftFields, HashJoinExecSerializationTests::randomFields);
+ case 3 -> rightFields = randomValueOtherThan(rightFields, HashJoinExecSerializationTests::randomFields);
+ case 4 -> output = randomValueOtherThan(output, HashJoinExecSerializationTests::randomFields);
default -> throw new UnsupportedOperationException();
}
- return new HashJoinExec(instance.source(), child, joinData, matchFields, leftFields, rightFields, output);
+ return new HashJoinExec(instance.source(), child, joinData, leftFields, rightFields, output);
}
@Override
From bc1a6ece0d53f8f61dc86852a4423a6703f5d777 Mon Sep 17 00:00:00 2001
From: Alexander Spies
Date: Thu, 11 Sep 2025 14:23:45 +0200
Subject: [PATCH 14/30] Revert JoinConfig back to Record
---
.../esql/plan/logical/join/JoinConfig.java | 60 +++----------------
1 file changed, 8 insertions(+), 52 deletions(-)
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
index 06e0e8d71736d..efe4a76fd1351 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/join/JoinConfig.java
@@ -11,6 +11,7 @@
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.core.Nullable;
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
import org.elasticsearch.xpack.esql.core.capabilities.Resolvables;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
@@ -18,29 +19,16 @@
import java.io.IOException;
import java.util.List;
-import java.util.Objects;
/**
- * Configuration for a {@code JOIN} style operation.
+ * @param type type of join
+ * @param leftFields fields from the left child to join on
+ * @param rightFields fields from the right child to join on
+ * @param joinOnConditions join conditions for expression based joins. If null, we assume equi-join on the left/right fields
*/
-public final class JoinConfig implements Writeable {
- private final JoinType type;
- private final List leftFields;
- private final List rightFields;
- private final Expression joinOnConditions;
-
- /**
- * @param type type of join
- * @param leftFields fields from the left child to join on
- * @param rightFields fields from the right child to join on
- * @param joinOnConditions join conditions for expression based joins. If null, we assume equi-join on the left/right fields
- */
- public JoinConfig(JoinType type, List leftFields, List rightFields, Expression joinOnConditions) {
- this.type = type;
- this.leftFields = leftFields;
- this.rightFields = rightFields;
- this.joinOnConditions = joinOnConditions;
- }
+public record JoinConfig(JoinType type, List leftFields, List rightFields, @Nullable Expression joinOnConditions)
+ implements
+ Writeable {
/**
* Legacy constructor that included the match fields, which were always the left fields.
@@ -97,38 +85,6 @@ public boolean expressionsResolved() {
&& (joinOnConditions == null || joinOnConditions.resolved());
}
- public JoinType type() {
- return type;
- }
-
- public List leftFields() {
- return leftFields;
- }
-
- public List rightFields() {
- return rightFields;
- }
-
- public Expression joinOnConditions() {
- return joinOnConditions;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == this) return true;
- if (obj == null || obj.getClass() != this.getClass()) return false;
- var that = (JoinConfig) obj;
- return Objects.equals(this.type, that.type)
- && Objects.equals(this.leftFields, that.leftFields)
- && Objects.equals(this.rightFields, that.rightFields)
- && Objects.equals(this.joinOnConditions, that.joinOnConditions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(type, leftFields, rightFields, joinOnConditions);
- }
-
@Override
public String toString() {
return "JoinConfig["
From 31e86824bff8f72b805b599dd11bd752deccbb7f Mon Sep 17 00:00:00 2001
From: Alexander Spies
Date: Thu, 11 Sep 2025 15:19:02 +0200
Subject: [PATCH 15/30] Stop using deprecated c'tor in test
---
.../xpack/esql/plan/logical/JoinSerializationTests.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
index 5d1b2193c1dcd..76d40ccce072c 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinSerializationTests.java
@@ -31,10 +31,9 @@ protected Join createTestInstance() {
private static JoinConfig randomJoinConfig() {
JoinType type = randomFrom(JoinTypes.LEFT, JoinTypes.RIGHT, JoinTypes.INNER, JoinTypes.FULL, JoinTypes.CROSS);
- List matchFields = randomFieldAttributes(1, 10, false);
List leftFields = randomFieldAttributes(1, 10, false);
List rightFields = randomFieldAttributes(1, 10, false);
- return new JoinConfig(type, matchFields, leftFields, rightFields, randomJoinOnExpression());
+ return new JoinConfig(type, leftFields, rightFields, randomJoinOnExpression());
}
@Override
From 221d2d4697faa0d2f49a11f8638d8ead85fe8222 Mon Sep 17 00:00:00 2001
From: Alexander Spies
Date: Thu, 11 Sep 2025 15:37:43 +0200
Subject: [PATCH 16/30] Fix some more tests
---
.../optimizer/LogicalPlanOptimizerTests.java | 16 ++++++++--------
.../optimizer/PhysicalPlanOptimizerTests.java | 2 +-
.../logical/PushDownAndCombineFiltersTests.java | 6 +++---
.../xpack/esql/plan/logical/JoinTests.java | 14 ++++----------
.../xpack/esql/tree/EsqlNodeSubclassTests.java | 1 -
5 files changed, 16 insertions(+), 23 deletions(-)
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
index ab01d950c5dd0..7a00df39e9b8a 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java
@@ -1032,7 +1032,7 @@ public void testPushdownLimitsPastLeftJoin() {
var rightChild = new LocalRelation(Source.EMPTY, List.of(fieldAttribute()), EmptyLocalSupplier.EMPTY);
assertNotEquals(leftChild, rightChild);
- var joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(), List.of(), List.of(), null);
+ var joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(), List.of(), null);
var join = switch (randomIntBetween(0, 2)) {
case 0 -> new Join(EMPTY, leftChild, rightChild, joinConfig);
case 1 -> new LookupJoin(EMPTY, leftChild, rightChild, joinConfig, false);
@@ -5819,7 +5819,7 @@ public void testDoubleInlinestatsWithEvalGetsPrunedEntirely() {
var ref = as(order.child(), FieldAttribute.class);
assertThat(ref.name(), is("emp_no"));
var inlineJoin = as(topN.child(), InlineJoin.class);
- assertThat(Expressions.names(inlineJoin.config().matchFields()), is(List.of("salaryK")));
+ assertThat(Expressions.names(inlineJoin.config().leftFields()), is(List.of("salaryK")));
// Left
var eval = as(inlineJoin.left(), Eval.class);
assertThat(Expressions.names(eval.fields()), is(List.of("salaryK")));
@@ -5885,7 +5885,7 @@ public void testInlinestatsGetsPrunedPartially() {
assertThat(Expressions.names(project.projections()), is(List.of("x", "a", "emp_no")));
var upperLimit = asLimit(project.child(), 1, false);
var inlineJoin = as(upperLimit.child(), InlineJoin.class);
- assertThat(Expressions.names(inlineJoin.config().matchFields()), is(List.of("emp_no")));
+ assertThat(Expressions.names(inlineJoin.config().leftFields()), is(List.of("emp_no")));
// Left
var relation = as(inlineJoin.left(), EsRelation.class);
// Right
@@ -5914,7 +5914,7 @@ public void testTripleInlinestatsGetsPrunedPartially() {
assertThat(Expressions.names(project.projections()), is(List.of("x", "a", "emp_no")));
var upperLimit = asLimit(project.child(), 1, false);
var inlineJoin = as(upperLimit.child(), InlineJoin.class);
- assertThat(Expressions.names(inlineJoin.config().matchFields()), is(List.of("emp_no")));
+ assertThat(Expressions.names(inlineJoin.config().leftFields()), is(List.of("emp_no")));
// Left
var relation = as(inlineJoin.left(), EsRelation.class);
// Right
@@ -6214,7 +6214,7 @@ public void testInlinestatsWithLookupJoin() {
assertThat(order.nullsPosition(), equalTo(Order.NullsPosition.FIRST));
assertThat(Expressions.name(order.child()), equalTo("abbrev"));
var join = as(topN.child(), Join.class);
- assertThat(Expressions.names(join.config().matchFields()), is(List.of("scalerank")));
+ assertThat(Expressions.names(join.config().leftFields()), is(List.of("scalerank")));
var left = as(join.left(), EsRelation.class);
assertThat(left.concreteIndices(), is(Set.of("airports")));
var right = as(join.right(), EsRelation.class);
@@ -6249,7 +6249,7 @@ public void testInlinestatsWithAvg() {
assertThat(Expressions.names(esqlProject.projections()), is(List.of("avg", "emp_no", "first_name")));
var upperLimit = asLimit(esqlProject.child(), 10, false);
var inlineJoin = as(upperLimit.child(), InlineJoin.class);
- assertThat(Expressions.names(inlineJoin.config().matchFields()), is(List.of("emp_no")));
+ assertThat(Expressions.names(inlineJoin.config().leftFields()), is(List.of("emp_no")));
// Left
var relation = as(inlineJoin.left(), EsRelation.class);
// Right
@@ -6840,7 +6840,7 @@ public void testLookupSimple() {
assertThat(limit.limit().fold(FoldContext.small()), equalTo(1000));
assertThat(join.config().type(), equalTo(JoinTypes.LEFT));
- assertThat(join.config().matchFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
+ assertThat(join.config().leftFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
assertThat(join.config().leftFields().size(), equalTo(1));
assertThat(join.config().rightFields().size(), equalTo(1));
Attribute lhs = join.config().leftFields().get(0);
@@ -6929,7 +6929,7 @@ public void testLookupStats() {
as(left.child(), EsRelation.class);
assertThat(join.config().type(), equalTo(JoinTypes.LEFT));
- assertThat(join.config().matchFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
+ assertThat(join.config().leftFields().stream().map(Object::toString).toList(), matchesList().item(startsWith("int{r}")));
assertThat(join.config().leftFields().size(), equalTo(1));
assertThat(join.config().rightFields().size(), equalTo(1));
Attribute lhs = join.config().leftFields().get(0);
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
index 250237f8d6fdc..31401469c0572 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java
@@ -7602,7 +7602,7 @@ public void testLookupThenTopN() {
);
Join join = as(innerTopN.child(), Join.class);
assertThat(join.config().type(), equalTo(JoinTypes.LEFT));
- assertMap(join.config().matchFields().stream().map(Objects::toString).toList(), matchesList().item(startsWith("int{r}")));
+ assertMap(join.config().leftFields().stream().map(Objects::toString).toList(), matchesList().item(startsWith("int{r}")));
Project innerProject = as(join.left(), Project.class);
assertThat(innerProject.projections(), hasSize(10));
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
index 81e45411fa260..3212c79ae882d 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java
@@ -587,7 +587,7 @@ public void testPushDownFilterPastLeftJoinWithComplexMix() {
FieldAttribute g = getFieldAttribute("g");
EsRelation left = relation(List.of(a, getFieldAttribute("b")));
EsRelation right = relation(List.of(c, d, e, f, g));
- JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(a), List.of(a), List.of(c), null);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(a), List.of(c), null);
Join join = new Join(EMPTY, left, right, joinConfig);
// Predicates
@@ -838,7 +838,7 @@ private Join createLeftJoinOnFields() {
EsRelation left = relation(List.of(a, b));
EsRelation right = relation(List.of(c, b));
- JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(b), List.of(a, b), List.of(b, c), null);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(a, b), List.of(b, c), null);
return new Join(EMPTY, left, right, joinConfig);
}
@@ -850,7 +850,7 @@ private Join createLeftJoinOnExpression() {
EsRelation left = relation(List.of(a, b1));
EsRelation right = relation(List.of(c, b2));
Expression joinOnCondition = new GreaterThanOrEqual(Source.EMPTY, b1, b2);
- JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(b1), List.of(a, b1, b2), List.of(b2, c), joinOnCondition);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, List.of(a, b1, b2), List.of(b2, c), joinOnCondition);
return new Join(EMPTY, left, right, joinConfig);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinTests.java
index 21f6f5cd49f43..187dcbd07d778 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plan/logical/JoinTests.java
@@ -27,7 +27,6 @@ public class JoinTests extends ESTestCase {
public void testExpressionsAndReferences() {
int numMatchFields = between(1, 10);
- List matchFields = new ArrayList<>(numMatchFields);
List leftFields = new ArrayList<>(numMatchFields);
List leftAttributes = new ArrayList<>(numMatchFields);
List rightFields = new ArrayList<>(numMatchFields);
@@ -41,25 +40,22 @@ public void testExpressionsAndReferences() {
leftAttributes.add(left.toAttribute());
rightFields.add(right);
rightAttributes.add(right.toAttribute());
- matchFields.add(randomBoolean() ? left.toAttribute() : right.toAttribute());
}
Row left = new Row(Source.EMPTY, leftFields);
Row right = new Row(Source.EMPTY, rightFields);
- JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, matchFields, leftAttributes, rightAttributes, null);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, leftAttributes, rightAttributes, null);
Join join = new Join(Source.EMPTY, left, right, joinConfig);
// matchfields are a subset of the left and right fields, so they don't contribute to the size of the references set.
// assertEquals(2 * numMatchFields, join.references().size());
AttributeSet refs = join.references();
- assertTrue(refs.containsAll(matchFields));
assertTrue(refs.containsAll(leftAttributes));
assertTrue(refs.containsAll(rightAttributes));
Set exprs = Set.copyOf(join.expressions());
- assertTrue(exprs.containsAll(matchFields));
assertTrue(exprs.containsAll(leftAttributes));
assertTrue(exprs.containsAll(rightAttributes));
}
@@ -67,7 +63,6 @@ public void testExpressionsAndReferences() {
public void testTransformExprs() {
int numMatchFields = between(1, 10);
- List matchFields = new ArrayList<>(numMatchFields);
List leftFields = new ArrayList<>(numMatchFields);
List leftAttributes = new ArrayList<>(numMatchFields);
List rightFields = new ArrayList<>(numMatchFields);
@@ -81,18 +76,17 @@ public void testTransformExprs() {
leftAttributes.add(left.toAttribute());
rightFields.add(right);
rightAttributes.add(right.toAttribute());
- matchFields.add(randomBoolean() ? left.toAttribute() : right.toAttribute());
}
Row left = new Row(Source.EMPTY, leftFields);
Row right = new Row(Source.EMPTY, rightFields);
- JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, matchFields, leftAttributes, rightAttributes, null);
+ JoinConfig joinConfig = new JoinConfig(JoinTypes.LEFT, leftAttributes, rightAttributes, null);
Join join = new Join(Source.EMPTY, left, right, joinConfig);
- assertTrue(join.config().matchFields().stream().allMatch(ref -> ref.dataType().equals(DataType.INTEGER)));
+ assertTrue(join.config().leftFields().stream().allMatch(ref -> ref.dataType().equals(DataType.INTEGER)));
Join transformedJoin = (Join) join.transformExpressionsOnly(Attribute.class, attr -> attr.withDataType(DataType.BOOLEAN));
- assertTrue(transformedJoin.config().matchFields().stream().allMatch(ref -> ref.dataType().equals(DataType.BOOLEAN)));
+ assertTrue(transformedJoin.config().leftFields().stream().allMatch(ref -> ref.dataType().equals(DataType.BOOLEAN)));
}
private static Alias aliasForLiteral(String name) {
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
index c234c25edc57c..49cc6dfc57f1e 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java
@@ -515,7 +515,6 @@ public void accept(Page page) {
JoinTypes.LEFT,
List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
- List.of(UnresolvedAttributeTests.randomUnresolvedAttribute()),
randomJoinOnExpression()
);
}
From f0ff7ce6d15984ce9cfea832a3f65392b28e64d1 Mon Sep 17 00:00:00 2001
From: Julian Kiryakov