Package org.exist.xquery.pragmas

Source Code of org.exist.xquery.pragmas.Optimize

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-06 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 program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*  \$Id\$
*/
package org.exist.xquery.pragmas;

import org.apache.log4j.Logger;
import org.exist.Namespaces;
import org.exist.collections.Collection;
import org.exist.dom.NodeSet;
import org.exist.dom.QName;
import org.exist.indexing.StructuralIndex;
import org.exist.storage.QNameRangeIndexSpec;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.*;
import org.exist.xquery.functions.fn.ExtFulltext;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.Type;

import java.util.Iterator;
import java.util.List;

public class Optimize extends Pragma {

    public  final static QName OPTIMIZE_PRAGMA = new QName("optimize", Namespaces.EXIST_NS, "exist");

    private final static Logger LOG = Logger.getLogger(Optimize.class);

    private boolean enabled = true;
    private XQueryContext context;
    private Optimizable optimizables[];
    private Expression innerExpr = null;
    private LocationStep contextStep = null;
    private VariableReference contextVar = null;
    private int contextId = Expression.NO_CONTEXT_ID;

    private NodeSet cachedContext = null;
    private int cachedTimestamp;
    private boolean cachedOptimize;
   
    public Optimize(XQueryContext context, QName pragmaName, String contents, boolean explicit) throws XPathException {
        super(pragmaName, contents);
        this.context = context;
        this.enabled = explicit || context.optimizationsEnabled();
        if (contents != null && contents.length() > 0) {
            final String param[] = Option.parseKeyValuePair(contents);
            if (param == null)
                {throw new XPathException("Invalid content found for pragma exist:optimize: " + contents);}
            if ("enable".equals(param[0])) {
                enabled = "yes".equals(param[1]);
            }
        }
    }

    public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
        super.analyze(contextInfo);
        this.contextId = contextInfo.getContextId();
    }

    public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
        if (contextItem != null)
                {contextSequence = contextItem.toSequence();}

        boolean useCached = false;
        boolean optimize = false;
        NodeSet originalContext = null;

        if (contextSequence == null || contextSequence.isPersistentSet()) {    // don't try to optimize in-memory node sets!
            // contextSequence will be overwritten
            originalContext = contextSequence == null ? null : contextSequence.toNodeSet();
            if (cachedContext != null && cachedContext == originalContext)
                {useCached = !originalContext.hasChanged(cachedTimestamp);}
            if (contextVar != null) {
                contextSequence = contextVar.eval(contextSequence);
            }
            // check if all Optimizable expressions signal that they can indeed optimize
            // in the current context
            if (useCached)
                {optimize = cachedOptimize;}
            else {
                if (optimizables != null && optimizables.length > 0) {
                    for (int i = 0; i < optimizables.length; i++) {
                        if (optimizables[i].canOptimize(contextSequence))
                            {optimize = true;}
                        else {
                            optimize = false;
                            break;
                        }
                    }
                }
            }
        }
        if (optimize) {
            cachedContext = originalContext;
            cachedTimestamp = originalContext == null ? 0 : originalContext.getState();
            cachedOptimize = true;
            NodeSet ancestors;
            NodeSet result = null;
            for (int current = 0; current < optimizables.length; current++) {
                NodeSet selection = optimizables[current].preSelect(contextSequence, current > 0);
                if (LOG.isTraceEnabled())
                    {LOG.trace("exist:optimize: pre-selection: " + selection.getLength());}
                // determine the set of potential ancestors for which the predicate has to
                // be re-evaluated to filter out wrong matches
                if (selection.isEmpty())
                  {ancestors = selection;}
                else if (contextStep == null || current > 0) {
                    ancestors = selection.selectAncestorDescendant(contextSequence.toNodeSet(), NodeSet.ANCESTOR,
                        true, contextId, true);
                } else {
//                    NodeSelector selector;
                    final long start = System.currentTimeMillis();
//                    selector = new AncestorSelector(selection, contextId, true, false);
                    final StructuralIndex index = context.getBroker().getStructuralIndex();
                    final QName ancestorQN = contextStep.getTest().getName();
                    if (optimizables[current].optimizeOnSelf()) {
                        ancestors = index.findAncestorsByTagName(ancestorQN.getNameType(), ancestorQN, Constants.SELF_AXIS,
                            selection.getDocumentSet(), selection, contextId);
                    } else {
                        ancestors = index.findAncestorsByTagName(ancestorQN.getNameType(), ancestorQN,
                            optimizables[current].optimizeOnChild() ? Constants.PARENT_AXIS : Constants.ANCESTOR_SELF_AXIS,
                            selection.getDocumentSet(), selection, contextId);
                    }
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Ancestor selection took " + (System.currentTimeMillis() - start));
                        LOG.trace("Found: " + ancestors.getLength());
                    }
                }
                result = ancestors;
                contextSequence = result;
            }
            if (contextStep == null) {
                return innerExpr.eval(result);
            } else {
                contextStep.setPreloadedData(result.getDocumentSet(), result);
                if (LOG.isTraceEnabled())
                    {LOG.trace("exist:optimize: context after optimize: " + result.getLength());}
                final long start = System.currentTimeMillis();
                if (originalContext != null)
                    {contextSequence = originalContext.filterDocuments(result);}
                else
                    {contextSequence = null;}
                final Sequence seq = innerExpr.eval(contextSequence);
                if (LOG.isTraceEnabled())
                    {LOG.trace("exist:optimize: inner expr took " + (System.currentTimeMillis() - start) +
                        "; found: "+ seq.getItemCount());}
                return seq;
            }
        } else {
            if (LOG.isTraceEnabled())
                {LOG.trace("exist:optimize: Cannot optimize expression.");}
            if (originalContext != null)
                {contextSequence = originalContext;}
            return innerExpr.eval(contextSequence, contextItem);
        }
    }

    public void before(XQueryContext context, Expression expression, Sequence contextSequence) throws XPathException {
        if (innerExpr != null)
            {return;}
        innerExpr = expression;
        if (!enabled)
            {return;}
        innerExpr.accept(new BasicExpressionVisitor() {

            public void visitPathExpr(PathExpr expression) {
                for (int i = 0; i < expression.getLength(); i++) {
                    final Expression next = expression.getExpression(i);
              next.accept(this);
                }
            }

            public void visitLocationStep(LocationStep locationStep) {
                final List<Predicate> predicates = locationStep.getPredicates();
                for (final Predicate pred : predicates) {
                    pred.accept(this);
                }
            }

            public void visitFilteredExpr(FilteredExpression filtered) {
                final Expression filteredExpr = filtered.getExpression();
                if (filteredExpr instanceof VariableReference)
                    {contextVar = (VariableReference) filteredExpr;}

                final List<Predicate> predicates = filtered.getPredicates();
                for (final Predicate pred : predicates) {
                    pred.accept(this);
                }
            }

            public void visit(Expression expression) {
                super.visit(expression);
            }

            public void visitFtExpression(ExtFulltext fulltext) {
                if (LOG.isTraceEnabled())
                    {LOG.trace("exist:optimize: found optimizable: " + fulltext.getClass().getName());}
                addOptimizable(fulltext);
            }

            public void visitGeneralComparison(GeneralComparison comparison) {
                if (LOG.isTraceEnabled())
                    {LOG.trace("exist:optimize: found optimizable: " + comparison.getClass().getName());}
                addOptimizable(comparison);
            }

            public void visitPredicate(Predicate predicate) {
                predicate.getExpression(0).accept(this);
            }

            public void visitBuiltinFunction(Function function) {
                if (function instanceof Optimizable) {
                    if (LOG.isTraceEnabled())
                        {LOG.trace("exist:optimize: found optimizable function: " + function.getClass().getName());}
                    addOptimizable((Optimizable) function);
                }
            }
        });

        contextStep = BasicExpressionVisitor.findFirstStep(innerExpr);
        if (contextStep != null && contextStep.getTest().isWildcardTest())
            {contextStep = null;}
        if (LOG.isTraceEnabled()) {
            LOG.trace("exist:optimize: context step: " + contextStep);
            LOG.trace("exist:optimize: context var: " + contextVar);
        }
    }

    public void after(XQueryContext context, Expression expression) throws XPathException {
    }

    private void addOptimizable(Optimizable optimizable) {final int axis = optimizable.getOptimizeAxis();
        if (!(axis == Constants.CHILD_AXIS || axis == Constants.SELF_AXIS || axis == Constants.DESCENDANT_AXIS ||
                axis == Constants.DESCENDANT_SELF_AXIS || axis == Constants.ATTRIBUTE_AXIS ||
                axis == Constants.DESCENDANT_ATTRIBUTE_AXIS)) {
            // reverse axes cannot be optimized
            return;
        }
        if (optimizables == null) {
            optimizables = new Optimizable[1];
            optimizables[0] = optimizable;
        } else {
            Optimizable o[] = new Optimizable[optimizables.length + 1];
            System.arraycopy(optimizables, 0, o, 0, optimizables.length);
            o[optimizables.length] = optimizable;
            optimizables = o;
        }
    }

    public void resetState(boolean postOptimization) {
        super.resetState(postOptimization);
        cachedContext = null;
    }

    /**
     * Check every collection in the context sequence for an existing range index by QName.
     *
     * @param contextSequence
     * @return the type of a usable index or {@link org.exist.xquery.value.Type#ITEM} if there is no common
     *  index.
     */
    public static int getQNameIndexType(XQueryContext context, Sequence contextSequence, QName qname) {
        if (contextSequence == null || qname == null)
            {return Type.ITEM;}
       
        final String enforceIndexUse =
            (String) context.getBroker().getConfiguration().getProperty(XQueryContext.PROPERTY_ENFORCE_INDEX_USE);
        int indexType = Type.ITEM;
        for (final Iterator<Collection> i = contextSequence.getCollectionIterator(); i.hasNext(); ) {
            final Collection collection = i.next();
            if (collection.getURI().startsWith(XmldbURI.SYSTEM_COLLECTION_URI))
                {continue;}
            final QNameRangeIndexSpec config = collection.getIndexByQNameConfiguration(context.getBroker(), qname);
            if (config == null) {
                // no index found for this collection
                if (LOG.isTraceEnabled())
                    {LOG.trace("Cannot optimize: collection " + collection.getURI() + " does not define an index " +
                        "on " + qname);}
                // if enfoceIndexUse == "always", continue to check other collections
                // for indexes. It is sufficient if one collection defines an index
                if (enforceIndexUse == null || !"always".equals(enforceIndexUse))
                    {return Type.ITEM;}   // found a collection without index
            } else {
            int type = config.getType();
                if (indexType == Type.ITEM) {
                    indexType = type;
                    // if enforceIndexUse == "always", it is sufficient if one collection
                    // defines an index. Just return it.
                    if (enforceIndexUse != null && "always".equals(enforceIndexUse))
                        {return indexType;}
                } else if (indexType != type) {
                    // found an index with a bad type. cannot optimize.
                    // TODO: should this continue checking other collections?
                    if (LOG.isTraceEnabled())
                        {LOG.trace("Cannot optimize: collection " + collection.getURI() + " does not define an index " +
                            "with the required type " + Type.getTypeName(type) + " on " + qname);}
                    return Type.ITEM;   // found a collection with a different type
                }
            }
        }
        return indexType;
    }
}
TOP

Related Classes of org.exist.xquery.pragmas.Optimize

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.