diff --git a/exist-core/src/main/java/org/exist/xquery/Optimizer.java b/exist-core/src/main/java/org/exist/xquery/Optimizer.java index 54adfc34b9e..83b7113b439 100644 --- a/exist-core/src/main/java/org/exist/xquery/Optimizer.java +++ b/exist-core/src/main/java/org/exist/xquery/Optimizer.java @@ -80,8 +80,14 @@ public class Optimizer extends DefaultExpressionVisitor { public Optimizer(XQueryContext context) { this.context = context; + final List defaultRewriters = List.of( + new PositionalPredicateRewriter(context) + ); + this.rewriters = new ArrayList<>(defaultRewriters); final DBBroker broker = context.getBroker(); - this.rewriters = broker != null ? broker.getIndexController().getQueryRewriters(context) : Collections.emptyList(); + if (broker != null) { + rewriters.addAll(broker.getIndexController().getQueryRewriters(context)); + } } public boolean hasOptimized() { @@ -169,37 +175,6 @@ public void visitLocationStep(final LocationStep locationStep) { } } - /** - * Test whether we can optimise position() withing a predicate to a POSITIONAL version of a predicate. - * - * @param predicate the predicate to test - * - * @return the inner expr to be replaced and the literal value to replace it with if it can be optimised, null otherwise. - */ - private @Nullable Tuple2 canOptimizeToPositionalPredicate(final Predicate predicate) { - if (predicate.getSubExpressionCount() == 1) { - final Expression predInnerExpr = predicate.getSubExpression(0); - if (predInnerExpr instanceof final ValueComparison valueComparison && valueComparison.getRelation() == Constants.Comparison.EQ) { - final Expression left = valueComparison.getLeft(); - final Expression right = valueComparison.getRight(); - - if (left instanceof final InternalFunctionCall leftInternalFunctionCall && leftInternalFunctionCall.getFunction() instanceof FunPosition - && right instanceof final LiteralValue rightLiteralValue && rightLiteralValue.returnsType() == Type.INTEGER) { - - // NOTE(AR) if the predicate is like [position() eq 1] or [position() = 1] then we could rewrite it to [1] - return Tuple(predInnerExpr, rightLiteralValue); - - } else if (left instanceof final LiteralValue leftLiteralValue && leftLiteralValue.returnsType() == Type.INTEGER - && right instanceof final InternalFunctionCall rightInternalFunctionCall && rightInternalFunctionCall.getFunction() instanceof FunPosition) { - - // NOTE(AR) if the predicate is like [1 eq position()] or [1 = position()] then we could rewrite it to [1] - return Tuple(predInnerExpr, leftLiteralValue); - } - } - } - return null; - } - @Override public void visitFilteredExpr(final FilteredExpression filtered) { super.visitFilteredExpr(filtered); @@ -345,30 +320,6 @@ public void visitGeneralComparison(GeneralComparison comparison) { public void visitPredicate(Predicate predicate) { ++predicates; - - // NOTE(AR) try and optimise position() withing a predicate to a POSITIONAL version of a predicate - @Nullable final Tuple2 predOptimizableInnerExpr = canOptimizeToPositionalPredicate(predicate); - if (predOptimizableInnerExpr != null) { - try { - // Replace the predicates expression with a literal number - - predicate.replace(predOptimizableInnerExpr._1, predOptimizableInnerExpr._2); - - // traverse to outermost predicate and then call analyze! to make sure execution mode is correct - Expression parentExpr = predicate; - Predicate outermostPredicate = predicate; - while ((parentExpr = parentExpr.getParent()) != null) { - if (parentExpr instanceof final Predicate parentPredicate) { - outermostPredicate = parentPredicate; - } - } - outermostPredicate.analyze(new AnalyzeContextInfo(outermostPredicate.getParent(), 0)); - - } catch (final XPathException e) { - LOG.warn("Failed to optimize expression within predicate: {}: {}", predOptimizableInnerExpr, e.getMessage(), e); - } - } - super.visitPredicate(predicate); --predicates; } diff --git a/exist-core/src/main/java/org/exist/xquery/PositionalPredicateRewriter.java b/exist-core/src/main/java/org/exist/xquery/PositionalPredicateRewriter.java new file mode 100644 index 00000000000..13e858d86e2 --- /dev/null +++ b/exist-core/src/main/java/org/exist/xquery/PositionalPredicateRewriter.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, Evolved Binary Ltd + */ +package org.exist.xquery; + +import com.evolvedbinary.j8fu.tuple.Tuple2; +import org.exist.xquery.functions.fn.FunPosition; +import org.exist.xquery.value.Type; + +import javax.annotation.Nullable; + +import static com.evolvedbinary.j8fu.tuple.Tuple.Tuple; + +/** + * Rewrite position() withing a predicate to a POSITIONAL version of the predicate where possible. + * + * @author Adam Retter + */ +public class PositionalPredicateRewriter extends QueryRewriter { + + public PositionalPredicateRewriter(final XQueryContext context) { + super(context); + } + + @Override + public Pragma rewriteLocationStep(final LocationStep locationStep) throws XPathException { + + @Nullable final Predicate[] predicates = locationStep.getPredicates(); + if (predicates != null) { + for (final Predicate predicate : predicates) { + @Nullable final Tuple2 predicateOptimizableInnerExpr = canOptimizeToPositionalPredicate(predicate); + if (predicateOptimizableInnerExpr != null) { + predicate.replace(predicateOptimizableInnerExpr._1, predicateOptimizableInnerExpr._2); + } + } + } + + return null; + } + + /** + * Test whether we can optimise position() withing a predicate to a POSITIONAL version of a predicate. + * + * @param predicate the predicate to test + * + * @return the inner expr to be replaced and the literal value to replace it with if it can be optimised, null otherwise. + */ + private @Nullable Tuple2 canOptimizeToPositionalPredicate(final Predicate predicate) { + if (predicate.getSubExpressionCount() == 1) { + final org.exist.xquery.Expression predInnerExpr = predicate.getSubExpression(0); + if (predInnerExpr instanceof final ValueComparison valueComparison && valueComparison.getRelation() == Constants.Comparison.EQ) { + final org.exist.xquery.Expression left = valueComparison.getLeft(); + final org.exist.xquery.Expression right = valueComparison.getRight(); + + if (left instanceof final InternalFunctionCall leftInternalFunctionCall && leftInternalFunctionCall.getFunction() instanceof FunPosition + && right instanceof final org.exist.xquery.LiteralValue rightLiteralValue && rightLiteralValue.returnsType() == Type.INTEGER) { + + // NOTE(AR) if the predicate is like [position() eq 1] or [position() = 1] then we could rewrite it to [1] + return Tuple(predInnerExpr, rightLiteralValue); + + } else if (left instanceof final org.exist.xquery.LiteralValue leftLiteralValue && leftLiteralValue.returnsType() == Type.INTEGER + && right instanceof final InternalFunctionCall rightInternalFunctionCall && rightInternalFunctionCall.getFunction() instanceof FunPosition) { + + // NOTE(AR) if the predicate is like [1 eq position()] or [1 = position()] then we could rewrite it to [1] + return Tuple(predInnerExpr, leftLiteralValue); + } + } + } + return null; + } +}