Skip to content

Commit

Permalink
[refactor] Refactor the predicate position() optimisation into a Quer…
Browse files Browse the repository at this point in the history
…yRewriter
  • Loading branch information
adamretter committed Dec 1, 2024
1 parent bae0899 commit 21aaf1b
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 56 deletions.
63 changes: 7 additions & 56 deletions exist-core/src/main/java/org/exist/xquery/Optimizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,14 @@ public class Optimizer extends DefaultExpressionVisitor {

public Optimizer(XQueryContext context) {
this.context = context;
final List<QueryRewriter> 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() {
Expand Down Expand Up @@ -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<Expression, LiteralValue> 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);
Expand Down Expand Up @@ -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<Expression, LiteralValue> 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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
*/
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<Expression, LiteralValue> 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<Expression, LiteralValue> 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;
}
}

0 comments on commit 21aaf1b

Please sign in to comment.