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

com.bigdata.rdf.sparql.ast.optimizers.AbstractOptimizerTestCase Maven / Gradle / Ivy

There is a newer version: 2.1.4
Show newest version
/**

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
*/
package com.bigdata.rdf.sparql.ast.optimizers;


import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.query.algebra.StatementPattern.Scope;

import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IValueExpression;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.ModifiableBOpBase;
import com.bigdata.journal.ITx;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.constraints.IsBoundBOp;
import com.bigdata.rdf.internal.constraints.OrBOp;
import com.bigdata.rdf.sparql.ast.ASTBase;
import com.bigdata.rdf.sparql.ast.ASTContainer;
import com.bigdata.rdf.sparql.ast.AbstractASTEvaluationTestCase;
import com.bigdata.rdf.sparql.ast.ArbitraryLengthPathNode;
import com.bigdata.rdf.sparql.ast.AssignmentNode;
import com.bigdata.rdf.sparql.ast.ConstantNode;
import com.bigdata.rdf.sparql.ast.ExistsNode;
import com.bigdata.rdf.sparql.ast.FilterNode;
import com.bigdata.rdf.sparql.ast.FunctionNode;
import com.bigdata.rdf.sparql.ast.FunctionRegistry;
import com.bigdata.rdf.sparql.ast.GlobalAnnotations;
import com.bigdata.rdf.sparql.ast.GraphPatternGroup;
import com.bigdata.rdf.sparql.ast.GroupMemberNodeBase;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.IQueryNode;
import com.bigdata.rdf.sparql.ast.IValueExpressionNode;
import com.bigdata.rdf.sparql.ast.JoinGroupNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueryInclude;
import com.bigdata.rdf.sparql.ast.NamedSubqueryRoot;
import com.bigdata.rdf.sparql.ast.NotExistsNode;
import com.bigdata.rdf.sparql.ast.PathNode;
import com.bigdata.rdf.sparql.ast.PathNode.PathAlternative;
import com.bigdata.rdf.sparql.ast.PathNode.PathElt;
import com.bigdata.rdf.sparql.ast.PathNode.PathMod;
import com.bigdata.rdf.sparql.ast.PathNode.PathSequence;
import com.bigdata.rdf.sparql.ast.ProjectionNode;
import com.bigdata.rdf.sparql.ast.PropertyPathNode;
import com.bigdata.rdf.sparql.ast.PropertyPathUnionNode;
import com.bigdata.rdf.sparql.ast.QueryBase;
import com.bigdata.rdf.sparql.ast.QueryNodeWithBindingSet;
import com.bigdata.rdf.sparql.ast.QueryRoot;
import com.bigdata.rdf.sparql.ast.QueryType;
import com.bigdata.rdf.sparql.ast.StatementPatternNode;
import com.bigdata.rdf.sparql.ast.SubqueryRoot;
import com.bigdata.rdf.sparql.ast.TermNode;
import com.bigdata.rdf.sparql.ast.UnionNode;
import com.bigdata.rdf.sparql.ast.ValueExpressionNode;
import com.bigdata.rdf.sparql.ast.VarNode;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpUtility;
import com.bigdata.rdf.sparql.ast.explainhints.ExplainHints;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;

/**
 * A helper class that can make it easier to write {@link IASTOptimizer} tests.
 * 
 * @author jeremycarroll
 */
public abstract class AbstractOptimizerTestCase extends
		AbstractASTEvaluationTestCase {

	public interface Annotations extends
			com.bigdata.rdf.sparql.ast.GraphPatternGroup.Annotations,
			com.bigdata.rdf.sparql.ast.ArbitraryLengthPathNode.Annotations,
			com.bigdata.rdf.sparql.ast.eval.AST2BOpBase.Annotations,
			com.bigdata.rdf.sparql.ast.StatementPatternNode.Annotations
			{
	}

	enum HelperFlag {
		ZERO_OR_ONE {
			@Override
			public void apply(ASTBase sp) {
				setPathMod(((ArbitraryLengthPathNode) sp), PathMod.ZERO_OR_ONE);
			}
		},
		
		ZERO_OR_MORE {
			@Override
			public void apply(ASTBase sp) {
				setPathMod(((ArbitraryLengthPathNode) sp), PathMod.ZERO_OR_MORE);
			}
			
		},
		
		ONE_OR_MORE {
			@Override
			public void apply(ASTBase sp) {
				setPathMod(((ArbitraryLengthPathNode) sp), PathMod.ONE_OR_MORE);
			}
			
		},
		DEFAULT_CONTEXTS {
			@Override
			public void apply(ASTBase sp) {
				((StatementPatternNode) sp).setScope(Scope.DEFAULT_CONTEXTS);
			}
		},
		NAMED_CONTEXTS {
			@Override
			public void apply(ASTBase sp) {
				((StatementPatternNode) sp).setScope(Scope.DEFAULT_CONTEXTS);
			}
		},
		OPTIONAL {
			@Override
			public void apply(ASTBase sp) {
				((ModifiableBOpBase) sp)
						.setProperty(Annotations.OPTIONAL, true);
			}
		},
		DISTINCT {
			@Override
			public void apply(ASTBase rslt) {
				((QueryBase) rslt).getProjection().setDistinct(true);
			}
		},
		REDUCED {
			@Override
			public void apply(ASTBase rslt) {
				((QueryBase) rslt).getProjection().setReduced(true);
			}
		},
		NOT_DISTINCT {
			@Override
			public void apply(ASTBase rslt) {
				((QueryBase) rslt).getProjection().setDistinct(false);
			}
		},
		NOT_REDUCED {
			@Override
			public void apply(ASTBase rslt) {
				((QueryBase) rslt).getProjection().setReduced(false);
			}
		};

		/**
		 * 
		 * @param target
		 * @throws ClassCastException
		 *             If there is a mismatch between the flag and its usage.
		 */
		abstract public void apply(ASTBase target);

		private static void setPathMod(ArbitraryLengthPathNode alp, PathMod mod ) {
			alp.setProperty(Annotations.LOWER_BOUND, mod == PathMod.ONE_OR_MORE ? 1L : 0L);
			alp.setProperty(Annotations.UPPER_BOUND,mod == PathMod.ZERO_OR_ONE ? 1L : Long.MAX_VALUE);
			
		}
	};

	protected AbstractOptimizerTestCase(final String name) {
		super(name);
	}

	protected AbstractOptimizerTestCase() {
		super();
	}

	/**
	 * Return the {@link IASTOptimizer} to be evaluated by the {@link Helper}.
	 * This may be an {@link ASTOptimizerList} if you need to chain multiple
	 * optimizers together in order to evaluate their behavior.
	 */
    abstract IASTOptimizer newOptimizer() ;

	/**
	 * The purpose of this class is to make the tests look like the old
	 * comments. The first example
	 * {@link TestASTStaticJoinOptimizer#test_simpleOptional01A()} is based on
	 * the comments of
	 * {@link TestASTStaticJoinOptimizer#test_simpleOptional01()} and
	 * demonstrates that the comment is out of date.
	 * 

* NB: Given this goal, several Java naming conventions are ignored. e.g. * methods whose names are ALLCAPS or the same as ClassNames *

* Also, note that the intent is that this class be used in * anonymous subclasses with a single invocation of the {@link #test()} method, * and the two fields {@link #given} and {@link #expected} initialized * in the subclasses constructor (i.e. inside a second pair of braces). *

* All of the protected members are wrappers around constructors, * to allow the initialization of these two fields, to have a style * much more like Prolog than Java. * * @author jeremycarroll */ @SuppressWarnings("rawtypes") public abstract class Helper { /** * Wrapper for the annotation property name-value. */ protected class StatementPatternProperty { private String annotation; private Object value; public StatementPatternProperty(String annotation, Object value) { this.annotation = annotation; this.value = value; } public String getAnnotation() { return annotation; } public Object getValue() { return value; } } /** * The given AST is the input to the {@link IASTOptimizer}. */ protected QueryRoot given; /** * The expected AST output from the {@link IASTOptimizer}. */ protected QueryRoot expected; /** * Field for construction of arbitrary AST nodes. */ protected ASTBase tmp; /** * Variables */ protected final String w = "w", x = "x", y = "y", z = "z", s = "s", p = "p", o = "o"; /** * Constants ... */ protected final IV a = iv("a"), b = iv("b"), c = iv("c"), d = iv("d"), e = iv("e"), f = iv("f"), g = iv("g"), h = iv("h"); private VarNode rightVar; private VarNode leftVar; int varCount = 0; private GlobalAnnotations globals = new GlobalAnnotations(getName(), ITx.READ_COMMITTED); /** * Execute the test comparing the rewrite of the {@link #given} AST by * the {@link IASTOptimizer} with the {@link #expected} AST. */ public void test() { final IASTOptimizer rewriter = newOptimizer(); final AST2BOpContext context = new AST2BOpContext(new ASTContainer( given), store); final IQueryNode actual = rewriter.optimize(context, new QueryNodeWithBindingSet(given, new IBindingSet[] {})). getQueryNode(); assertSameAST(expected, actual); } /** * Execute the test comparing the rewrite of the {@link #given} AST by * the {@link IASTOptimizer} with the {@link #expected} AST. Thereby, * explain hints attached to the computed AST are ignored when comparing * the ASTs. */ public void testWhileIgnoringExplainHints() { final IASTOptimizer rewriter = newOptimizer(); final AST2BOpContext context = new AST2BOpContext(new ASTContainer( given), store); final IQueryNode actual = rewriter.optimize(context, new QueryNodeWithBindingSet(given, new IBindingSet[] {})). getQueryNode(); // remove explain hints, to ease comparison ExplainHints.removeExplainHintAnnotationsFromBOp(actual); assertSameAST(expected, actual); } private IV iv(final String id) { return makeIV(new URIImpl("http://example/" + id)); } protected VarNode wildcard() { return varNode("*"); } /** * Create a top-level SELECT query. * * @param varNodes * The projected variables. * @param namedSubQuery * A named subquery that is declared to that top-level query. * @param where * The WHERE clause of the top-level query. * @param flags * The flags to be applied to the resulting AST. */ protected QueryRoot select(final VarNode[] varNodes, final NamedSubqueryRoot namedSubQuery, final JoinGroupNode where, final HelperFlag... flags) { final QueryRoot rslt = select(varNodes, where, flags); rslt.getNamedSubqueriesNotNull().add(namedSubQuery); return rslt; } /** * * @param varNodes * The variables that will appear in the projection. * @param where * The top-level WHERE clause. * @param flags * Zero or more flags that are applied to the operations. * * @return The {@link QueryRoot}. */ protected QueryRoot select(final VarNode[] varNodes, final JoinGroupNode where, final HelperFlag... flags) { // // Create QueryRoot. // final QueryRoot select = new QueryRoot(QueryType.SELECT); // // Setup projection. // final ProjectionNode projection = new ProjectionNode(); // for (VarNode varNode : varNodes) // projection.addProjectionVar(varNode); return select(projection(varNodes), where, flags); // // Set Projection and Where clause on QueryRoot. // select.setProjection(projection); // select.setWhereClause(where); // // // Apply helper flags. // for (HelperFlag flag : flags) // flag.apply(select); // // return select; } /** * Return a PROJECTION node. * * @param varNodes * The variables that will appear in the projection. */ protected ProjectionNode projection(final VarNode... varNodes) { // Setup projection. final ProjectionNode projection = new ProjectionNode(); for (VarNode varNode : varNodes) projection.addProjectionVar(varNode); return projection; } /** * Return a PROJECTION node. * * @param assignmentNodes * The BIND()s that will appear in the projection. */ protected ProjectionNode projection(final AssignmentNode... assignmentNodes) { // Setup projection. final ProjectionNode projection = new ProjectionNode(); for (AssignmentNode varNode : assignmentNodes) projection.addProjectionExpression(varNode); return projection; } /** * * @param projection * The projection. * @param where * The top-level WHERE clause. * @param flags * Zero or more flags that are applied to the operations. * * @return The {@link QueryRoot}. */ protected QueryRoot select(final ProjectionNode projection, final JoinGroupNode where, final HelperFlag... flags) { final QueryRoot select = new QueryRoot(QueryType.SELECT); return select(select, projection, where, flags); } protected QueryRoot select(final VarNode varNode, final NamedSubqueryRoot namedSubQuery, final JoinGroupNode where, final HelperFlag... flags) { return select(new VarNode[] { varNode }, namedSubQuery, where, flags); } protected QueryRoot select(final VarNode varNode, final JoinGroupNode where, final HelperFlag... flags) { return select(new VarNode[] { varNode }, where, flags); } protected SubqueryRoot selectSubQuery(final ProjectionNode projection, final JoinGroupNode where, final HelperFlag... flags) { final SubqueryRoot rslt = new SubqueryRoot(QueryType.SELECT); return select(rslt, projection, where, flags); } private T select(T select, final ProjectionNode projection, final JoinGroupNode where, final HelperFlag... flags) { assert projection != null; // Set Projection and Where clause on QueryRoot. select.setProjection(projection); select.setWhereClause(where); // Apply helper flags. for (HelperFlag flag : flags) flag.apply(select); return select; } /** * Return an ASK subquery. * * @param varNode * The "ASK" variable. See * {@link SubqueryRoot.Annotations#ASK_VAR}. * @param where * The WHERE clause. * * @return the ASK subquery. */ protected SubqueryRoot ask(final VarNode varNode, final JoinGroupNode where) { final SubqueryRoot rslt = new SubqueryRoot(QueryType.ASK); final ProjectionNode projection = new ProjectionNode(); varNode.setAnonymous(true); rslt.setProjection(projection); projection.addProjectionExpression(new AssignmentNode(varNode, varNode)); rslt.setWhereClause(where); rslt.setAskVar(toValueExpression(varNode)); return rslt; } /** * Return a named subquery. * * @param name * The name associated with the named subquery result. * @param varNode * The projected variable. * @param where * The where clause. * * @return The named subquyery. */ protected NamedSubqueryRoot namedSubQuery(final String name, final VarNode varNode, final JoinGroupNode where) { final NamedSubqueryRoot namedSubquery = new NamedSubqueryRoot( QueryType.SELECT, name); final ProjectionNode projection = new ProjectionNode(); namedSubquery.setProjection(projection); projection.addProjectionExpression(new AssignmentNode(varNode, new VarNode(varNode))); namedSubquery.setWhereClause(where); return namedSubquery; } protected GroupMemberNodeBase namedSubQueryInclude(final String name) { return new NamedSubqueryInclude(name); } protected VarNode leftVar() { leftVar = newAlppVar("-tVarLeft-",leftVar); return leftVar; } protected VarNode rightVar() { rightVar = newAlppVar("-tVarRight-",rightVar); return rightVar; } private VarNode newAlppVar(final String prefix, VarNode v) { if (v != null) { return v; } v = varNode(prefix+varCount++); v.setAnonymous(true); return v ; } protected ArbitraryLengthPathNode arbitartyLengthPropertyPath(TermNode left, TermNode right, HelperFlag card, JoinGroupNode joinGroupNode) { assert leftVar != null; assert rightVar != null; ArbitraryLengthPathNode rslt = new ArbitraryLengthPathNode(left, right, leftVar, rightVar, PathMod.ONE_OR_MORE); card.apply(rslt); rslt.setArg(0, joinGroupNode); leftVar = null; rightVar = null; return rslt; } protected VarNode[] varNodes(String... names) { VarNode rslt[] = new VarNode[names.length]; for (int i = 0; i < names.length; i++) rslt[i] = varNode(names[i]); return rslt; } protected VarNode varNode(String varName) { return new VarNode(varName); } protected TermNode constantNode(IV iv) { return new ConstantNode(iv); } protected TermNode constantNode(String c) { return new ConstantNode(iv(c)); } protected PropertyPathNode propertyPathNode(final TermNode s, String pattern, final TermNode o) { return new PropertyPathNode(s, pathNode(pattern), o); } /** * This method is only implemented in part. The issue is what patterns are supported. * The desired support as of the intended contract of this method is that pattern * can be any sparql property path expression without any whitespace, and where * every property in the path is reduced to a single letter a-z. * e.g. "(a/b?/c)+|a|c*" * * The current support is that parenthesis, alternatives and negated properties are not supported. * * @param pattern * @return */ protected PathNode pathNode(String pattern) { final String seq[] = pattern.split("/"); final PathElt elements[] = new PathElt[seq.length]; PathMod mod = null; for (int i =0;i *

  • A context node: must be first (i.e. fourth)
  • *
  • integer cardinality
  • *
  • HelperFlag's
  • * * @param s * @param p * @param o * @param more * @return */ protected StatementPatternNode statementPatternNode(// final TermNode s, final TermNode p, final TermNode o, final Object ... more) { final StatementPatternNode rslt = newStatementPatternNode(s, p, o); if (more.length > 0) { int i = 0; if (more[0] instanceof TermNode) { rslt.setC((TermNode) more[0]); i = 1; } for (; i < more.length; i++) { if (more[i] instanceof Integer) { rslt.setProperty(Annotations.ESTIMATED_CARDINALITY, Long.valueOf((Integer) more[i])); } else if(more[i] instanceof StatementPatternProperty) { StatementPatternProperty prop = (StatementPatternProperty) more[i]; rslt.setProperty(prop.annotation, prop.value); } else { final HelperFlag flag = (HelperFlag) more[i]; flag.apply(rslt); } } } return rslt; } @SuppressWarnings("unchecked") private > T initGraphPatternGroup( T rslt, Object... statements) { for (Object mem : statements) { if (mem instanceof IGroupMemberNode) { rslt.addChild((E) mem); } else { ((HelperFlag) mem).apply(rslt); } } return rslt; } /** * Return a graph join group. * * @param context * The graph (named graph variable or IRI). * @param statements * The children (including any optional {@link HelperFlag}s). * * @return The group join group node. */ protected JoinGroupNode joinGroupNode(final TermNode context, final Object... statements) { final JoinGroupNode rslt = joinGroupNode(statements); rslt.setContext(context); return rslt; } /** * Wrap the arguments in a {@link JoinGroupNode} and apply any * inter-mixed {@link HelperFlag}s. * * @param statements * The arguments (group group members or {@link HelperFlag}s) * * @return The {@link JoinGroupNode}. */ protected JoinGroupNode joinGroupNode(final Object... statements) { return initGraphPatternGroup(new JoinGroupNode(), statements); } /** * Wrap the triple patterns in a WHERE clause (aka join group). * * @param statements * The triple patterns. * * @return The {@link JoinGroupNode} */ protected JoinGroupNode where(final IGroupMemberNode... statements) { return joinGroupNode((Object[]) statements); } protected PropertyPathUnionNode propertyPathUnionNode(Object... statements) { return initGraphPatternGroup(new PropertyPathUnionNode(), statements); } protected UnionNode unionNode(Object... statements) { return initGraphPatternGroup(new UnionNode(), statements); } protected FunctionNode bound(final VarNode varNode) { final FunctionNode rslt = new FunctionNode(FunctionRegistry.BOUND, null, new ValueExpressionNode[] { varNode } ); rslt.setValueExpression(new IsBoundBOp(varNode.getValueExpression())); return rslt; } protected FunctionNode knownUnbound(final VarNode varNode) { final VarNode vv = varNode("-unbound-var-"+varNode.getValueExpression().getName()+"-0"); final FunctionNode rslt = new FunctionNode(FunctionRegistry.BOUND, null, vv); rslt.setValueExpression(new IsBoundBOp(vv.getValueExpression())); return rslt; } protected FilterNode filter(final IValueExpressionNode f) { return new FilterNode(f); } /** * Return a {@link FunctionNode} * * @param uri * the function URI. see {@link FunctionRegistry} * @param args * the arguments to the function. * @return */ // * @param scalarValues // * One or more scalar values that are passed to the function protected IValueExpressionNode functionNode(// final String uri,// final ValueExpressionNode... args// ) { return functionNode(new URIImpl(uri), args); } /** * Return a {@link FunctionNode} * * @param uri * the function URI. see {@link FunctionRegistry} * @param args * the arguments to the function. * @return */ // * @param scalarValues // * One or more scalar values that are passed to the function protected IValueExpressionNode functionNode(// final URI uri,// final ValueExpressionNode... args// ) { return new FunctionNode(uri, null, args); } protected ServiceNode service(// final TermNode serviceRef, final GraphPatternGroup groupNode// ) { return new ServiceNode(serviceRef, groupNode); } /** * BIND(expression AS variable) * * @param valueNode * The expression * @param varNode * The variable. */ protected AssignmentNode bind(final IValueExpressionNode valueNode, final VarNode varNode) { return new AssignmentNode(varNode, valueNode); } /** * Logical OR of two value expressions. */ protected FunctionNode or(final ValueExpressionNode v1, final ValueExpressionNode v2) { final FunctionNode rslt = FunctionNode.OR(v1, v2); rslt.setValueExpression(new OrBOp(// v1.getValueExpression(), // v2.getValueExpression())); return rslt; } protected ExistsNode exists(VarNode v, GraphPatternGroup jg) { v.setAnonymous(true); ExistsNode existsNode = new ExistsNode(v, jg); existsNode.setValueExpression(toValueExpression(v)); return existsNode; } private IVariable toValueExpression(VarNode v) { return (IVariable) AST2BOpUtility.toVE(getBOpContext(), globals, v); } private IValueExpression toValueExpression(FunctionNode n) { return AST2BOpUtility.toVE(getBOpContext(), globals, n); } protected NotExistsNode notExists(VarNode v, GraphPatternGroup jg) { return new NotExistsNode(v, jg); } protected ASTBase getTmp() { return tmp; } } protected static final class ASTPropertyPathOptimizerInTest extends ASTPropertyPathOptimizer { private int counter = 0; @Override protected VarNode anonVar(final String anon) { VarNode v = new VarNode(anon+counter++); v.setAnonymous(true); return v; } } protected StatementPatternNode newStatementPatternNode(final TermNode s, final TermNode p, final TermNode o) { return newStatementPatternNode(s, p, o, -1, false); } protected StatementPatternNode newStatementPatternNode(final TermNode s, final TermNode p, final TermNode o, final long cardinality) { return newStatementPatternNode(s, p, o, cardinality, false); } /** * Return a new triple pattern. * * @param s * The subject. * @param p * The predicate. * @param o * The object. * @param cardinality * The estimated cardinality -or- -1 to NOT * associate cardinality with the triple pattern. * @param optional * true iff the triple pattern should be marked as a * simple OPTIONAL. */ protected StatementPatternNode newStatementPatternNode( final TermNode s, final TermNode p, final TermNode o, final long cardinality, final boolean optional) { final StatementPatternNode sp = new StatementPatternNode(s, p, o); if (cardinality != -1) { sp.setProperty(Annotations.ESTIMATED_CARDINALITY, cardinality); } if (optional) { sp.setOptional(true); } return sp; } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy