From 64b73e94d5439c51c62e86be59b1ca734d0853cb Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Mon, 10 Jul 2023 19:58:51 +0100 Subject: [PATCH 01/11] Revert "Reworked Wolfs' comments" This reverts commit 35a76f25f0431220de95ff43752232bbf6a59efc. --- .../exist/xquery/modules/range/RangeQueryRewriter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java index 6881d9e44f5..d77a82eaa4e 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java @@ -21,7 +21,7 @@ */ package org.exist.xquery.modules.range; -import org.exist.indexing.range.RangeIndex; +import org.exist.indexing.range.*; import org.exist.storage.NodePath; import org.exist.xquery.*; import org.exist.xquery.Constants.Comparison; @@ -127,7 +127,7 @@ public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathE protected static Lookup rewrite(Expression expression, NodePath path) throws XPathException { ArrayList eqArgs = new ArrayList<>(2); - if (expression instanceof GeneralComparison comparison) { + if (expression instanceof final GeneralComparison comparison) { eqArgs.add(comparison.getLeft()); eqArgs.add(comparison.getRight()); Lookup func = Lookup.create(comparison.getContext(), getOperator(expression), path); @@ -136,9 +136,9 @@ protected static Lookup rewrite(Expression expression, NodePath path) throws XPa return func; } - } else if (expression instanceof InternalFunctionCall fcall) { + } else if (expression instanceof final InternalFunctionCall fcall) { Function function = fcall.getFunction(); - if (function != null && function instanceof Lookup) { + if (function instanceof Lookup) { final RangeIndex.Operator operator = RangeIndex.Operator.getByName(function.getName().getLocalPart()); eqArgs.add(function.getArgument(0)); eqArgs.add(function.getArgument(1)); From 5ceb86c0a667ea4510968d94349bc9c413679440 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Mon, 10 Jul 2023 19:59:00 +0100 Subject: [PATCH 02/11] Revert "Proposed fix for #3624 NPE at org.exist.xquery.modules.range.RangeQueryRewriter.rewriteLocationStep" This reverts commit 56e190ad70cfc2e67e7dc713bbffe9643f1b10d1. --- .../xquery/modules/range/RangeQueryRewriter.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java index d77a82eaa4e..faf63b0213a 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java @@ -139,12 +139,13 @@ protected static Lookup rewrite(Expression expression, NodePath path) throws XPa } else if (expression instanceof final InternalFunctionCall fcall) { Function function = fcall.getFunction(); if (function instanceof Lookup) { - final RangeIndex.Operator operator = RangeIndex.Operator.getByName(function.getName().getLocalPart()); - eqArgs.add(function.getArgument(0)); - eqArgs.add(function.getArgument(1)); - Lookup func = Lookup.create(function.getContext(), operator, path); - func.setArguments(eqArgs); - return func; + if (function.isCalledAs("matches")) { + eqArgs.add(function.getArgument(0)); + eqArgs.add(function.getArgument(1)); + Lookup func = Lookup.create(function.getContext(), RangeIndex.Operator.MATCH, path); + func.setArguments(eqArgs); + return func; + } } } return null; From 3a3e3b6187079b022536457957996f2d8c0d976b Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Mon, 10 Jul 2023 22:32:23 +0100 Subject: [PATCH 03/11] [bugfix] Add missing assertions to XQSuite test --- extensions/indexes/range/src/test/xquery/range/optimizer.xql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/indexes/range/src/test/xquery/range/optimizer.xql b/extensions/indexes/range/src/test/xquery/range/optimizer.xql index cac8d3c8984..a2b00c44d34 100644 --- a/extensions/indexes/range/src/test/xquery/range/optimizer.xql +++ b/extensions/indexes/range/src/test/xquery/range/optimizer.xql @@ -472,7 +472,9 @@ function ot:optimize-matches-field($city as xs:string) { }; declare + %test:stats %test:args("[rR]üssel.*") + %test:assertXPath("$result//stats:index[@type = 'new-range'][@optimization = 1]") function ot:matches-field-filtered($city as xs:string) { let $address := collection($ot:COLLECTION)//address return From 1a1edbae8c58071948ddd848eb9364648c759185 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:09:00 +0100 Subject: [PATCH 04/11] [cleanup] Members don't need to be 'protected' scope, change to 'private' scope --- .../org/exist/xquery/modules/range/Lookup.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index 670b58dbfd3..f0cb66013ab 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -144,15 +144,15 @@ public static Lookup create(XQueryContext context, RangeIndex.Operator operator, } private LocationStep contextStep = null; - protected QName contextQName = null; - protected int axis = Constants.UNKNOWN_AXIS; + private QName contextQName = null; + private int axis = Constants.UNKNOWN_AXIS; private NodeSet preselectResult = null; - protected boolean canOptimize = false; - protected boolean optimizeSelf = false; - protected boolean optimizeChild = false; - protected boolean usesCollation = false; - protected Expression fallback = null; - protected NodePath contextPath = null; + private boolean canOptimize = false; + private boolean optimizeSelf = false; + private boolean optimizeChild = false; + private boolean usesCollation = false; + private Expression fallback = null; + private NodePath contextPath; public Lookup(XQueryContext context, FunctionSignature signature) { this(context, signature, null); From 2583ed971922a2f142a8e907636d4ec35e0ade37 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:10:21 +0100 Subject: [PATCH 05/11] [cleanup] Constructor doesn't need to be public --- .../src/main/java/org/exist/xquery/modules/range/Lookup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index f0cb66013ab..f7a30fee45d 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -158,7 +158,7 @@ public Lookup(XQueryContext context, FunctionSignature signature) { this(context, signature, null); } - public Lookup(XQueryContext context, FunctionSignature signature, NodePath contextPath) { + private Lookup(XQueryContext context, FunctionSignature signature, NodePath contextPath) { super(context, signature); this.contextPath = contextPath; } From fae8310a2c0312419a34fe50abca65842681cb8a Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:12:36 +0100 Subject: [PATCH 06/11] [cleanup] Mark variables as 'final' --- .../java/org/exist/xquery/modules/range/Lookup.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index f7a30fee45d..ad5c8dfab4a 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -134,8 +134,8 @@ public class Lookup extends Function implements Optimizable { ) }; - public static Lookup create(XQueryContext context, RangeIndex.Operator operator, NodePath contextPath) { - for (FunctionSignature sig: signatures) { + public static Lookup create(final XQueryContext context, final RangeIndex.Operator operator, final NodePath contextPath) { + for (final FunctionSignature sig : signatures) { if (sig.getName().getLocalPart().equals(operator.toString())) { return new Lookup(context, sig, contextPath); } @@ -152,13 +152,13 @@ public static Lookup create(XQueryContext context, RangeIndex.Operator operator, private boolean optimizeChild = false; private boolean usesCollation = false; private Expression fallback = null; - private NodePath contextPath; + private final NodePath contextPath; - public Lookup(XQueryContext context, FunctionSignature signature) { + public Lookup(final XQueryContext context, final FunctionSignature signature) { this(context, signature, null); } - private Lookup(XQueryContext context, FunctionSignature signature, NodePath contextPath) { + private Lookup(final XQueryContext context, final FunctionSignature signature, final NodePath contextPath) { super(context, signature); this.contextPath = contextPath; } From f93b3dec94b40054a5256b43407ef85c467858e2 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:13:45 +0100 Subject: [PATCH 07/11] [cleanup] Mark nullable members and function result as such --- .../exist/xquery/modules/range/Lookup.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index ad5c8dfab4a..70666eb34d9 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -38,6 +38,7 @@ import org.exist.xquery.*; import org.exist.xquery.value.*; +import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -134,7 +135,7 @@ public class Lookup extends Function implements Optimizable { ) }; - public static Lookup create(final XQueryContext context, final RangeIndex.Operator operator, final NodePath contextPath) { + public static @Nullable Lookup create(final XQueryContext context, final RangeIndex.Operator operator, final NodePath contextPath) { for (final FunctionSignature sig : signatures) { if (sig.getName().getLocalPart().equals(operator.toString())) { return new Lookup(context, sig, contextPath); @@ -143,29 +144,29 @@ public static Lookup create(final XQueryContext context, final RangeIndex.Operat return null; } - private LocationStep contextStep = null; - private QName contextQName = null; + @Nullable private LocationStep contextStep = null; + @Nullable private QName contextQName = null; private int axis = Constants.UNKNOWN_AXIS; - private NodeSet preselectResult = null; + @Nullable private NodeSet preselectResult = null; private boolean canOptimize = false; private boolean optimizeSelf = false; private boolean optimizeChild = false; private boolean usesCollation = false; - private Expression fallback = null; - private final NodePath contextPath; + @Nullable private Expression fallback = null; + @Nullable private final NodePath contextPath; public Lookup(final XQueryContext context, final FunctionSignature signature) { this(context, signature, null); } - private Lookup(final XQueryContext context, final FunctionSignature signature, final NodePath contextPath) { + private Lookup(final XQueryContext context, final FunctionSignature signature, @Nullable final NodePath contextPath) { super(context, signature); this.contextPath = contextPath; } - public void setFallback(Expression expression, int optimizeAxis) { - if (expression instanceof InternalFunctionCall) { - expression = ((InternalFunctionCall)expression).getFunction(); + public void setFallback(@Nullable Expression expression, final int optimizeAxis) { + if (expression instanceof final InternalFunctionCall fcall) { + expression = fcall.getFunction(); } this.fallback = expression; // we need to know the axis at this point. the optimizer will call @@ -173,7 +174,7 @@ public void setFallback(Expression expression, int optimizeAxis) { this.axis = optimizeAxis; } - public Expression getFallback() { + public @Nullable Expression getFallback() { return fallback; } From dde0cbd6a261b87f8df15657ab6ba66c611b1055 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:23:12 +0100 Subject: [PATCH 08/11] [doc] Add missing JavaDoc --- .../org/exist/xquery/modules/range/Lookup.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index 70666eb34d9..5955eb0eb1a 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -155,10 +155,24 @@ public class Lookup extends Function implements Optimizable { @Nullable private Expression fallback = null; @Nullable private final NodePath contextPath; + /** + * Constructor called via reflection from {@link Function#createFunction(XQueryContext, XQueryAST, Module, FunctionDef)}. + * + * @param context The XQuery Context. + * @param signature The signature of the Lookup function. + */ + @SuppressWarnings("unused") public Lookup(final XQueryContext context, final FunctionSignature signature) { this(context, signature, null); } + /** + * Constructor called via {@link #create(XQueryContext, RangeIndex.Operator, NodePath)}. + * + * @param context The XQuery Context. + * @param signature The signature of the Lookup function. + * @param contextPath the node path of the optimization. + */ private Lookup(final XQueryContext context, final FunctionSignature signature, @Nullable final NodePath contextPath) { super(context, signature); this.contextPath = contextPath; From b5c6e18fd42a02a29a70bf2f52d00cbf0da09a66 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:49:18 +0100 Subject: [PATCH 09/11] [bugfix] Fix NodePath#toString() invalidly reporting '//' as '///' --- exist-core/src/main/java/org/exist/storage/NodePath.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/exist-core/src/main/java/org/exist/storage/NodePath.java b/exist-core/src/main/java/org/exist/storage/NodePath.java index 4eb89bce087..931f0ab4119 100644 --- a/exist-core/src/main/java/org/exist/storage/NodePath.java +++ b/exist-core/src/main/java/org/exist/storage/NodePath.java @@ -243,10 +243,15 @@ public final boolean match(final NodePath other, int j) { public String toString() { final StringBuilder buf = new StringBuilder(); for (int i = 0; i < pos; i++) { - buf.append("/"); + + if (!(SKIP.equals(components[i]) || (i > 0 && SKIP.equals(components[i - 1])))) { + buf.append("/"); + } + if (components[i].getNameType() == ElementValue.ATTRIBUTE) { buf.append("@"); } + buf.append(components[i]); } return buf.toString(); From e758bf0b6420bb96df6b8df40c252ddaa38a5cc1 Mon Sep 17 00:00:00 2001 From: Adam Retter Date: Sun, 16 Jul 2023 17:59:54 +0100 Subject: [PATCH 10/11] [cleanup] Make the name of the test suite recognisable from the XSuite output --- extensions/indexes/range/src/test/xquery/range/range.xql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/indexes/range/src/test/xquery/range/range.xql b/extensions/indexes/range/src/test/xquery/range/range.xql index 778169bb413..38b4489bbc0 100644 --- a/extensions/indexes/range/src/test/xquery/range/range.xql +++ b/extensions/indexes/range/src/test/xquery/range/range.xql @@ -21,7 +21,7 @@ :) xquery version "3.0"; -module namespace rt="http://exist-db.org/xquery/range/test"; +module namespace rt="http://exist-db.org/xquery/range/test/range"; import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql"; From 90883bd8fcb0d83df8e905baa2013eacc4c72f44 Mon Sep 17 00:00:00 2001 From: marmoure Date: Mon, 24 Apr 2023 17:35:30 +0200 Subject: [PATCH 11/11] [bugfix] Check before optimizing innerExpr. No need to optimize if it is already optimized. Closes https://github.com/eXist-db/exist/issues/3624 --- .../exist/xquery/modules/range/Lookup.java | 20 +++- .../modules/range/RangeQueryRewriter.java | 97 +++++++++++-------- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java index 5955eb0eb1a..6704fd9ca31 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/Lookup.java @@ -153,7 +153,7 @@ public class Lookup extends Function implements Optimizable { private boolean optimizeChild = false; private boolean usesCollation = false; @Nullable private Expression fallback = null; - @Nullable private final NodePath contextPath; + @Nullable private NodePath contextPath; /** * Constructor called via reflection from {@link Function#createFunction(XQueryContext, XQueryAST, Module, FunctionDef)}. @@ -178,6 +178,24 @@ private Lookup(final XQueryContext context, final FunctionSignature signature, @ this.contextPath = contextPath; } + /** + * Get the node path of the optimization. + * + * @return the node path of the optimization, or null if unknown. + */ + public @Nullable NodePath getContextPath() { + return contextPath; + } + + /** + * Set the node path of the optimization. + * + * @param contextPath the node path of the optimization, or null if unknown. + */ + public void setContextPath(@Nullable final NodePath contextPath) { + this.contextPath = contextPath; + } + public void setFallback(@Nullable Expression expression, final int optimizeAxis) { if (expression instanceof final InternalFunctionCall fcall) { expression = fcall.getFunction(); diff --git a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java index faf63b0213a..7a4574c6747 100644 --- a/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java +++ b/extensions/indexes/range/src/main/java/org/exist/xquery/modules/range/RangeQueryRewriter.java @@ -28,6 +28,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -60,16 +61,16 @@ public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathE // will become true if optimizable expression is found boolean canOptimize = false; // get path of path expression before the predicates - NodePath contextPath = toNodePath(getPrecedingSteps(locationStep)); + final NodePath contextPath = toNodePath(getPrecedingSteps(locationStep)); // process the remaining predicates - for (Predicate pred : preds) { + for (final Predicate pred : preds) { if (pred.getLength() != 1) { // can only optimize predicates with one expression break; } - Expression innerExpr = pred.getExpression(0); - List steps = getStepsToOptimize(innerExpr); + final Expression innerExpr = pred.getExpression(0); + final List steps = getStepsToOptimize(innerExpr); if (steps == null || steps.isEmpty()) { // no optimizable steps found continue; @@ -89,11 +90,11 @@ public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathE } // compute left hand path - NodePath innerPath = toNodePath(steps); + final NodePath innerPath = toNodePath(steps); if (innerPath == null) { continue; } - NodePath path; + final NodePath path; if (contextPath == null) { path = innerPath; } else { @@ -102,14 +103,24 @@ public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathE } if (path.length() > 0) { - // replace with call to lookup function - // collect arguments - Lookup func = rewrite(innerExpr, path); - // preserve original comparison: may need it for in-memory lookups - func.setFallback(innerExpr, axis); - func.setLocation(innerExpr.getLine(), innerExpr.getColumn()); - - pred.replace(innerExpr, new InternalFunctionCall(func)); + if (innerExpr instanceof final InternalFunctionCall internalFunctionCall + && internalFunctionCall.getFunction() instanceof final Lookup lookup) { + + // innerExpr was already optimized, just update the contextPath if it is missing + if (lookup.getContextPath() == null) { + lookup.setContextPath(path);; + } + + } else { + // replace with call to lookup function + final Lookup func = rewrite(innerExpr, path); + // preserve original comparison: may need it for in-memory lookups + func.setFallback(innerExpr, axis); + func.setLocation(innerExpr.getLine(), innerExpr.getColumn()); + + pred.replace(innerExpr, new InternalFunctionCall(func)); + } + canOptimize = true; } } @@ -125,29 +136,22 @@ public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathE return null; } - protected static Lookup rewrite(Expression expression, NodePath path) throws XPathException { - ArrayList eqArgs = new ArrayList<>(2); + protected static @Nullable Lookup rewrite(final Expression expression, final NodePath path) throws XPathException { if (expression instanceof final GeneralComparison comparison) { - eqArgs.add(comparison.getLeft()); - eqArgs.add(comparison.getRight()); - Lookup func = Lookup.create(comparison.getContext(), getOperator(expression), path); - if (func != null) { - func.setArguments(eqArgs); - return func; + final List eqArgs = Arrays.asList(comparison.getLeft(), comparison.getRight()); + final Lookup lookup = Lookup.create(comparison.getContext(), getOperator(expression), path); + if (lookup != null) { + lookup.setArguments(eqArgs); } - + return lookup; } else if (expression instanceof final InternalFunctionCall fcall) { - Function function = fcall.getFunction(); - if (function instanceof Lookup) { - if (function.isCalledAs("matches")) { - eqArgs.add(function.getArgument(0)); - eqArgs.add(function.getArgument(1)); - Lookup func = Lookup.create(function.getContext(), RangeIndex.Operator.MATCH, path); - func.setArguments(eqArgs); - return func; - } + final Function function = fcall.getFunction(); + if (function instanceof final Lookup lookup && lookup.getContextPath() == null) { + lookup.setContextPath(path); + return lookup; } } + return null; } @@ -157,6 +161,7 @@ protected static List getStepsToOptimize(Expression expr) { } else if (expr instanceof InternalFunctionCall fcall) { Function function = fcall.getFunction(); if (function instanceof Lookup) { + // TODO(AR) is this check for range:matches needed here? if (function.isCalledAs("matches")) { return BasicExpressionVisitor.findLocationSteps(function.getArgument(0)); } else { @@ -169,14 +174,20 @@ protected static List getStepsToOptimize(Expression expr) { } public static RangeIndex.Operator getOperator(Expression expr) { - if (expr instanceof InternalFunctionCall fcall) { - Function function = fcall.getFunction(); - if (function instanceof Lookup) { - expr = ((Lookup)function).getFallback(); + if (expr instanceof final InternalFunctionCall fcall) { + final Function function = fcall.getFunction(); + if (function instanceof final Lookup lookup) { + final Expression fallback = lookup.getFallback(); + if (fallback != null) { + expr = fallback; + } else { + expr = lookup; + } } } + RangeIndex.Operator operator = RangeIndex.Operator.EQ; - if (expr instanceof GeneralComparison comparison) { + if (expr instanceof final GeneralComparison comparison) { final Comparison relation = comparison.getRelation(); operator = switch (relation) { case LT -> RangeIndex.Operator.LT; @@ -192,14 +203,14 @@ public static RangeIndex.Operator getOperator(Expression expr) { case NEQ -> RangeIndex.Operator.NE; default -> operator; }; - } else if (expr instanceof InternalFunctionCall fcall) { - Function function = fcall.getFunction(); - if (function instanceof Lookup && function.isCalledAs("matches")) { - operator = RangeIndex.Operator.MATCH; - } - } else if (expr instanceof Lookup && ((Function)expr).isCalledAs("matches")) { + } else if (expr instanceof final InternalFunctionCall fcall) { + expr = fcall.getFunction(); + } + + if (expr instanceof final Lookup lookup && lookup.isCalledAs("matches")) { operator = RangeIndex.Operator.MATCH; } + return operator; }