All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.rdf.sparql.ast.StaticAnalysisBase Maven / Gradle / Ivy

/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Oct 20, 2011
 */

package com.bigdata.rdf.sparql.ast;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IConstant;
import com.bigdata.bop.IVariable;
import com.bigdata.rdf.sparql.ast.eval.IEvaluationContext;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;
import com.bigdata.rdf.sparql.ast.ssets.ISolutionSetManager;

/**
 * Base class for static analysis.
 * 
 * @author Bryan Thompson
 * @version $Id$
 */
public class StaticAnalysisBase {

//    private static final Logger log = Logger.getLogger(StaticAnalysisBase.class);
    
    protected final QueryRoot queryRoot;
    
    protected final IEvaluationContext evaluationContext;
    
    /**
     * Return the {@link QueryRoot} parameter given to the constructor.
     */
    public QueryRoot getQueryRoot() {
       
        return queryRoot;
        
    }
    
    /**
     * 
     * @param queryRoot
     *            The root of the query. We need to have this on hand in order
     *            to resolve {@link NamedSubqueryInclude}s during static
     *            analysis.
     * @param evaluationContext
	 *            The evaluation context provides access to the
	 *            {@link ISolutionSetStats} and the {@link ISolutionSetManager} for
	 *            named solution sets.
     */
    protected StaticAnalysisBase(final QueryRoot queryRoot,
            final IEvaluationContext evaluationContext) {
        
        if(queryRoot == null)
            throw new IllegalArgumentException();
        
        this.queryRoot = queryRoot;
                
        this.evaluationContext = evaluationContext;

    }
    
    /**
     * Return the distinct variables in the operator tree, including on those on
     * annotations attached to operators. Variables projected by a subquery are
     * included, but not variables within the WHERE clause of the subquery.
     * Variables projected by a {@link NamedSubqueryInclude} are also reported,
     * but not those used within the WHERE clause of the corresponding
     * {@link NamedSubqueryRoot}.
     * 
     * @param op
     *            An operator.
     * @param varSet
     *            The variables are inserted into this {@link Set}.
     * 
     * @return The caller's {@link Set}.
     */
    public Set> getSpannedVariables(final BOp op,
            final Set> varSet) {

        return getSpannedVariables(op, true/* filters */, varSet);

    }

    /**
     * Return the distinct variables in the operator tree. Variables projected
     * by a subquery are included, but not variables within the WHERE clause of
     * the subquery. Variables projected by a {@link NamedSubqueryInclude} are
     * also reported, but not those used within the WHERE clause of the
     * corresponding {@link NamedSubqueryRoot}.
     * 
     * @param op
     *            An operator.
     * @param filters
     *            When true, variables on {@link FilterNode}s are
     *            also reported.
     * @param varSet
     *            The variables are inserted into this {@link Set}.
     * 
     * @return The caller's {@link Set}.
     * 
     *         TODO Unit tests for different kinds of AST nodes to make sure
     *         that we always get/ignore the variables in filters as appropriate.
     *         For example, an optional {@link StatementPatternNode} can have
     *         filter nodes attached.
     */
    public Set> getSpannedVariables(final BOp op,
            final boolean filters, final Set> varSet) {

        if (op == null) {

            return varSet;
            
        }
        
//        System.err.println("vars=" + varSet + ", op=" + op.toShortString());
        
        if (op instanceof IVariable) {
         
            varSet.add((IVariable)op);
            
        } else if(op instanceof IConstant) {
            
            final IConstant c = (IConstant)op;
            
            final IVariable var = (IVariable )c.getProperty(Constant.Annotations.VAR);
                
            if( var != null) {
                
                varSet.add(var);
                
            }
            
        } else if (op instanceof ArbitraryLengthPathNode) {
        	
        	varSet.addAll(((ArbitraryLengthPathNode) op).getMaybeProducedBindings());
        	
        	// do not recurse
        	return varSet;
        	
        } else if (op instanceof ZeroLengthPathNode) {
        	
        	varSet.addAll(((ZeroLengthPathNode) op).getProducedBindings());
        	
        	// do not recurse
        	return varSet;

        } else if (op instanceof ServiceNode) {

            // @see http://trac.blazegraph.com/ticket/816
            final ServiceNode serviceNode = (ServiceNode) op;
            
            // Look for the SERVICE URI, it might be a variable as well.
            final TermNode uriRef = serviceNode.getServiceRef();

            if (uriRef instanceof VarNode) {

                varSet.add(((VarNode) uriRef).getValueExpression());

            }

            // pick up anything in the group graph pattern.
            getSpannedVariables(serviceNode.getGraphPattern(), filters, varSet);

            // fall through - look for attached filters.

        } else if (op instanceof BindingsClause) {
    
            final BindingsClause bc = (BindingsClause)op;
            
            varSet.addAll(bc.getDeclaredVariables());
           return varSet;
          
        } else if (op instanceof FilterNode && !filters) {

            // DO NOT RECURSE INTO THE FILTER!
            return varSet;
            
        } else if (op instanceof SubqueryRoot) {

            /*
             * Do not recurse into a subquery, but report any variables
             * projected by that subquery.
             */

            final SubqueryRoot subquery = (SubqueryRoot) op;

            subquery.getProjectedVars(varSet);
//            addProjectedVariables(subquery, varSet);
            
            // DO NOT RECURSE INTO THE SUBQUERY!
            return varSet;

        } else if (op instanceof NamedSubqueryInclude) {

            final NamedSubqueryInclude namedInclude = (NamedSubqueryInclude) op;

            final String name = namedInclude.getName();
            
            final NamedSubqueryRoot subquery = getNamedSubqueryRoot(name);

            if(subquery != null) {
            	
                subquery.getProjectedVars(varSet);
//              addProjectedVariables(subquery, varSet);
            	
            } else {

                final ISolutionSetStats stats = getSolutionSetStats(name);

                /*
                 * Note: This is all variables which are bound in ANY solution.
                 * It MAY include variables which are NOT bound in some
                 * solutions.
                 */

                varSet.addAll(stats.getUsedVars());
            	
            }
            
            // DO NOT RECURSE INTO THE SUBQUERY!
            return varSet;
            
//        } else {
//            
//            throw new AssertionError("Not handled: " + op);
            
        }
        
        if (filters && op instanceof IJoinNode) {
            /*
             * Join nodes may have attached filters.
             */
            final IJoinNode t = (IJoinNode) op;
            final List list = t.getAttachedJoinFilters();
            if (list != null) {
                for (FilterNode f : list) {
                    getSpannedVariables(f, filters, varSet);
                }
            }
        }

        /*
         * Recursion.
         */
        final int arity = op.arity();

        for (int i = 0; i < arity; i++) {

            final BOp child = op.get(i);
            
            getSpannedVariables(child, filters, varSet);

        }

        return varSet;

    }

    /**
	 * Return the corresponding {@link NamedSubqueryRoot}.
	 * 
	 * @param name
	 *            The name of the solution set.
	 * 
	 * @return The {@link NamedSubqueryRoot} and never null.
	 * 
	 * @throws RuntimeException
	 *             if no {@link NamedSubqueryRoot} was found for the named set
	 *             associated with this {@link NamedSubqueryInclude}.
	 * 
	 * @deprecated Caller's MUST BE CHANGED to look for both a
	 *             {@link NamedSubqueryRoot} and an {@link ISolutionSetStats}
	 *             and then handle these as appropriate. In one case, that means
	 *             static analysis of the {@link NamedSubqueryRoot}. In the
	 *             other, the relevant information are present in pre-computed
	 *             metadata on the {@link ISolutionSetStats}.
	 */
    protected NamedSubqueryRoot getRequiredNamedSubqueryRoot(final String name) {
        
        final NamedSubqueryRoot nsr = getNamedSubqueryRoot(name);
        
        if(nsr == null)
            throw new RuntimeException(
                    "Named subquery does not exist for namedSet: " + name);
        
        return nsr;
        
    }

    /**
	 * Return the corresponding {@link NamedSubqueryRoot}.
	 * 

* Note: You can not resolve pre-existing named solution sets with this * method, only those which are defined within the scope of a query by a * {@link NamedSubqueryRoot}. *

* Note: Typically, callers MUST look for both a {@link NamedSubqueryRoot} * and an {@link ISolutionSetStats} and then handle these as appropriate. In * one case, that means static analysis of the {@link NamedSubqueryRoot}. In * the other, the relevant information are present in pre-computed metadata * on the {@link ISolutionSetStats}. * * @param name * The name of the solution set. * * @return The {@link NamedSubqueryRoot} -or- null if none was * found. * * @see #getSolutionSetStats(String) */ public NamedSubqueryRoot getNamedSubqueryRoot(final String name) { final NamedSubqueriesNode namedSubqueries = queryRoot .getNamedSubqueries(); if (namedSubqueries == null) return null; for (NamedSubqueryRoot namedSubquery : namedSubqueries) { if(name.equals(namedSubquery.getName())) return namedSubquery; } return null; } /** * Return the {@link ISolutionSetStats} for the named solution set. *

* Note: This does NOT report on {@link NamedSubqueryRoot}s for the query. * It only checks the {@link ISolutionSetManager}. *

* Note: Typically, callers MUST look for both a {@link NamedSubqueryRoot} * and an {@link ISolutionSetStats} and then handle these as appropriate. In * one case, that means static analysis of the {@link NamedSubqueryRoot}. In * the other, the relevant information are present in pre-computed metadata * on the {@link ISolutionSetStats}. * * @param name * The name of the solution set. * * @return The {@link ISolutionSetStats} and never null. * * @throws RuntimeException * if there is no such pre-existing named solution set. * * @see #getNamedSubqueryRoot(String) */ public ISolutionSetStats getSolutionSetStats(final String name) { return evaluationContext.getSolutionSetStats(name); } /** * Add all variables spanned by the operator. *

* WARNING: This method does not consider the variable * scoping rules. It is safe to use with a FILTER as all variables will be * in scope, but it is not safe to use with a {@link SubqueryBase} as only * the projected variables will be in scope. * * @param bindings * The set to which the variables will be added. * @param op * The operator. */ protected void addAll(final Set> bindings, final IGroupMemberNode op) { final Iterator> it = BOpUtility .getSpannedVariables((BOp) op); while (it.hasNext()) { bindings.add(it.next()); } } // /** // * Add all variables on the {@link ProjectionNode} of the subquery to the // * set of distinct variables visible within the scope of the parent query. // * // * @param subquery // * @param varSet // */ // static private void addProjectedVariables(final QueryBase subquery, // final Set> varSet) { // // final ProjectionNode proj = subquery.getProjection(); // // if (proj.isWildcard()) { // /* The subquery's projection should already have been rewritten. */ // throw new AssertionError(); // } // // proj.getProjectionVars(varSet); // //// for (IVariable var : proj.getProjectionVars()) { //// //// varSet.add(var); //// //// } // // } /** * Return true if the {@link FilterNode} is fully bound for the * given variables. * * @param f * The {@link FilterNode} * @param vars * Some collection of variables which are known to be bound. * @return true if all variables on which the filter depends * are present in that collection. */ public boolean isFullyBound(final FilterNode f, final Set> vars) { final Set> fvars = getSpannedVariables(f, true/* filters */, new LinkedHashSet>()); fvars.removeAll(vars); if (fvars.isEmpty()) { /* * The variables for this filter are all present in the given set of * variables. */ return true; } return false; } /** * Return any variables appearing in the Subject, Predicate, or Object * position (the Context position is ignored). * * @param sp * The statement pattern. * * @return The SPO variables in that statement pattern. */ public static Set> getSPOVariables( final StatementPatternNode sp) { final LinkedHashSet> set = new LinkedHashSet>(); for (int i = 0; i < 3; i++) { final TermNode tmp = (TermNode) sp.get(0); if (tmp.isVariable()) { set.add((IVariable) tmp.getValueExpression()); } } return set; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy