Package org.exist.xquery.modules.range

Source Code of org.exist.xquery.modules.range.RangeQueryRewriter

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2013 The eXist Project
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*
*  $Id$
*/
package org.exist.xquery.modules.range;

import org.exist.indexing.range.*;
import org.exist.storage.NodePath;
import org.exist.xquery.*;

import java.util.ArrayList;
import java.util.List;

/**
* Query rewriter for the range index. May replace path expressions like a[b = "c"] or a[b = "c"][d = "e"]
* with either a[range:equals(b, "c")] or range:field-equals(...).
*/
public class RangeQueryRewriter extends QueryRewriter {

    public RangeQueryRewriter(XQueryContext context) {
        super(context);
    }

    @Override
    public Pragma rewriteLocationStep(LocationStep locationStep) throws XPathException {
        if (locationStep.hasPredicates()) {
            Expression parentExpr = locationStep.getParentExpression();
            if ((parentExpr instanceof RewritableExpression)) {
                // Step 1: replace all optimizable expressions within predicates with
                // calls to the range functions. If those functions are used or not will
                // be decided at run time.

                final List<Predicate> preds = locationStep.getPredicates();

                // will become true if optimizable expression is found
                boolean canOptimize = false;
                // get path of path expression before the predicates
                NodePath contextPath = toNodePath(getPrecedingSteps(locationStep));
                // process the remaining predicates
                for (Predicate pred : preds) {
                    if (pred.getLength() != 1) {
                        // can only optimize predicates with one expression
                        break;
                    }

                    Expression innerExpr = pred.getExpression(0);
                    List<LocationStep> steps = getStepsToOptimize(innerExpr);
                    if (steps == null || steps.size() == 0) {
                        // no optimizable steps found
                        continue;
                    }

                    // check if inner steps are on an axis we can optimize
                    final int axis;
                    if (innerExpr instanceof InternalFunctionCall) {
                        InternalFunctionCall fcall = (InternalFunctionCall) innerExpr;
                        axis = ((Optimizable) fcall.getFunction()).getOptimizeAxis();
                    } else {
                        axis = ((Optimizable) innerExpr).getOptimizeAxis();
                    }
                    if (!(axis == Constants.CHILD_AXIS || axis == Constants.DESCENDANT_AXIS ||
                            axis == Constants.DESCENDANT_SELF_AXIS || axis == Constants.ATTRIBUTE_AXIS ||
                            axis == Constants.DESCENDANT_ATTRIBUTE_AXIS || axis == Constants.SELF_AXIS
                    )) {
                        continue;
                    }

                    // compute left hand path
                    NodePath innerPath = toNodePath(steps);
                    if (innerPath == null) {
                        continue;
                    }
                    NodePath path;
                    if (contextPath == null) {
                        path = innerPath;
                    } else {
                        path = new NodePath(contextPath);
                        path.append(innerPath);
                    }

                    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));
                        canOptimize = true;
                    }
                }

                if (canOptimize) {
                    // Step 2: return an OptimizeFieldPragma to handle field lookups and optimize the entire
                    // path expression. If the pragma can optimize the path expression, the original code will
                    // not be called.
                    return new OptimizeFieldPragma(OptimizeFieldPragma.OPTIMIZE_RANGE_PRAGMA, null, getContext());
                }
            }
        }
        return null;
    }

    protected static Lookup rewrite(Expression expression, NodePath path) throws XPathException {
        ArrayList<Expression> eqArgs = new ArrayList<Expression>(2);
        if (expression instanceof GeneralComparison) {
            GeneralComparison comparison = (GeneralComparison) expression;
            eqArgs.add(comparison.getLeft());
            eqArgs.add(comparison.getRight());
            Lookup func = Lookup.create(comparison.getContext(), getOperator(expression), path);
            func.setArguments(eqArgs);
            return func;
        } else if (expression instanceof InternalFunctionCall) {
            InternalFunctionCall fcall = (InternalFunctionCall) expression;
            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;
                }
            }
        }
        return null;
    }

    protected static List<LocationStep> getStepsToOptimize(Expression expr) {
        if (expr instanceof GeneralComparison) {
            GeneralComparison comparison = (GeneralComparison) expr;
            return BasicExpressionVisitor.findLocationSteps(comparison.getLeft());
        } else if (expr instanceof InternalFunctionCall) {
            InternalFunctionCall fcall = (InternalFunctionCall) expr;
            Function function = fcall.getFunction();
            if (function instanceof Lookup) {
                if (function.isCalledAs("matches")) {
                    return BasicExpressionVisitor.findLocationSteps(function.getArgument(0));
                } else {
                    Expression original = ((Lookup)function).getFallback();
                    return getStepsToOptimize(original);
                }
            }
        }
        return null;
    }

    protected static RangeIndex.Operator getOperator(Expression expr) {
        if (expr instanceof InternalFunctionCall) {
            InternalFunctionCall fcall = (InternalFunctionCall) expr;
            Function function = fcall.getFunction();
            if (function instanceof Lookup) {
                expr = ((Lookup)function).getFallback();
            }
        }
        RangeIndex.Operator operator = RangeIndex.Operator.EQ;
        if (expr instanceof GeneralComparison) {
            GeneralComparison comparison = (GeneralComparison) expr;
            int relation = comparison.getRelation();
            switch(relation) {
                case Constants.LT:
                    operator = RangeIndex.Operator.LT;
                    break;
                case Constants.GT:
                    operator = RangeIndex.Operator.GT;
                    break;
                case Constants.LTEQ:
                    operator = RangeIndex.Operator.LE;
                    break;
                case Constants.GTEQ:
                    operator = RangeIndex.Operator.GE;
                    break;
                case Constants.EQ:
                    switch (comparison.getTruncation()) {
                        case Constants.TRUNC_BOTH:
                            operator = RangeIndex.Operator.CONTAINS;
                            break;
                        case Constants.TRUNC_LEFT:
                            operator = RangeIndex.Operator.ENDS_WITH;
                            break;
                        case Constants.TRUNC_RIGHT:
                            operator = RangeIndex.Operator.STARTS_WITH;
                            break;
                        default:
                            operator = RangeIndex.Operator.EQ;
                            break;
                    }
                    break;
            }
        } else if (expr instanceof InternalFunctionCall) {
            InternalFunctionCall fcall = (InternalFunctionCall) expr;
            Function function = fcall.getFunction();
            if (function instanceof Lookup && function.isCalledAs("matches")) {
                operator = RangeIndex.Operator.MATCH;
            }
        } else if (expr instanceof Lookup && ((Function)expr).isCalledAs("matches")) {
            operator = RangeIndex.Operator.MATCH;
        }
        return operator;
    }

    protected static NodePath toNodePath(List<LocationStep> steps) {
        NodePath path = new NodePath();
        for (LocationStep step: steps) {
            if (step == null) {
                return null;
            }
            NodeTest test = step.getTest();
            if (test.isWildcardTest() && step.getAxis() == Constants.SELF_AXIS) {
                //return path;
                continue;
            }
            if (!test.isWildcardTest() && test.getName() != null) {
                int axis = step.getAxis();
                if (axis == Constants.DESCENDANT_AXIS || axis == Constants.DESCENDANT_SELF_AXIS) {
                    path.addComponent(NodePath.SKIP);
                } else if (axis != Constants.CHILD_AXIS && axis != Constants.ATTRIBUTE_AXIS) {
                    return null// not optimizable
                }
                path.addComponent(test.getName());
            }
        }
        return path;
    }

    protected static List<LocationStep> getPrecedingSteps(LocationStep current) {
        Expression parentExpr = current.getParentExpression();
        if (!(parentExpr instanceof RewritableExpression)) {
            return null;
        }
        final List<LocationStep> prevSteps = new ArrayList<LocationStep>();
        prevSteps.add(current);
        RewritableExpression parent = (RewritableExpression) parentExpr;
        Expression previous = parent.getPrevious(current);
        if (previous != null) {
            while (previous != null && previous != parent.getFirst() && previous instanceof LocationStep) {
                final LocationStep prevStep = (LocationStep) previous;
                prevSteps.add(0, prevStep);
                previous = parent.getPrevious(previous);
            }
        }
        return prevSteps;
    }
}
TOP

Related Classes of org.exist.xquery.modules.range.RangeQueryRewriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.