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

com.bigdata.rdf.sparql.ast.eval.TestQueryHints 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
*/
/*
 * Created on Oct 26, 2011
 */

package com.bigdata.rdf.sparql.ast.eval;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.query.BindingSet;
import org.openrdf.query.impl.MapBindingSet;

import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.BufferAnnotations;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.PipelineOp;
import com.bigdata.bop.Var;
import com.bigdata.bop.bset.ConditionalRoutingOp;
import com.bigdata.bop.controller.INamedSubqueryOp;
import com.bigdata.bop.controller.ServiceCallJoin;
import com.bigdata.bop.join.HashJoinOp;
import com.bigdata.bop.join.PipelineJoin;
import com.bigdata.bop.join.SolutionSetHashJoinOp;
import com.bigdata.bop.solutions.ProjectionOp;
import com.bigdata.bop.solutions.SliceOp;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.sparql.ast.ASTBase;
import com.bigdata.rdf.sparql.ast.ASTContainer;
import com.bigdata.rdf.sparql.ast.JoinGroupNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueriesNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueryInclude;
import com.bigdata.rdf.sparql.ast.QueryHints;
import com.bigdata.rdf.sparql.ast.QueryOptimizerEnum;
import com.bigdata.rdf.sparql.ast.QueryRoot;
import com.bigdata.rdf.sparql.ast.StatementPatternNode;
import com.bigdata.rdf.sparql.ast.SubqueryBase;
import com.bigdata.rdf.sparql.ast.SubqueryRoot;
import com.bigdata.rdf.sparql.ast.eval.service.OpenrdfNativeMockServiceFactory;
import com.bigdata.rdf.sparql.ast.hints.QueryHintException;
import com.bigdata.rdf.sparql.ast.optimizers.ASTQueryHintOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.TestASTQueryHintOptimizer;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;
import com.bigdata.rdf.sparql.ast.service.ServiceRegistry;
import com.bigdata.rdf.spo.SPOKeyOrder;
import com.bigdata.util.InnerCause;

import cutthecrap.utils.striterators.Filter;
import cutthecrap.utils.striterators.Striterator;

/**
 * Test suite for SPARQL queries with embedded query hints.
 * 
 * @see ASTQueryHintOptimizer
 * @see TestASTQueryHintOptimizer
 * 
 * @author Bryan Thompson
 * @version $Id$
 * 
 *          TODO Unit test to verify that hint:optimistic actually changes the
 *          value so the query runs differently (that is, test the query hint
 *          not the interpretation of the hint by the static join order
 *          optimizer). (I believe that this was hitting one of the BSBM, or
 *          maybe the BSBM BI queries.)
 * 
 *          TODO Unit test query hints for APs both at the BGP scope and in the
 *          Query scope (where they bind on AST2BOpContext). (E.g., the use of
 *          remote access paths, hash joins, sample limits, etc.).
 * 
 *          TODO Unit test to verify that query hints are being attached to the
 *          {@link ConditionalRoutingOp}.
 */
public class TestQueryHints extends AbstractDataDrivenSPARQLTestCase {

    private static final Logger log = Logger.getLogger(TestQueryHints.class);

    /**
     * 
     */
    public TestQueryHints() {
    }

    /**
     * @param name
     */
    public TestQueryHints(final String name) {
        super(name);
    }

    /**
     * Return all {@link BOp}s in which the specified property name appears
     * either as a direct annotation of that {@link BOp} or as a property in the
     * query hints properties object for that {@link BOp}.
     * 
     * @param op
     *            The root operator.
     * @param name
     *            The name of the desired property.
     *            
     * @return An iterator visiting the operators having a binding for that
     *         property.
     */
    @SuppressWarnings("unchecked")
    static private Iterator visitQueryHints(final BOp op, final String name) {
        return new Striterator(BOpUtility.preOrderIteratorWithAnnotations(op))
                .addFilter(new Filter() {
                    private static final long serialVersionUID = 1L;
                    @Override
                    public boolean isValid(final Object arg0) {
                        if (!(arg0 instanceof BOp)) {
                            return false;
                        }
                        final BOp tmp = (BOp) arg0;
                        if (tmp.getProperty(name) != null) {
                            // Exists as bare property.
                            if (log.isDebugEnabled())
                                log.debug("Found as bare property: name="
                                        + name + ", op=" + tmp.toShortString()
                                        + "#" + tmp.hashCode());
                            return true;
                        }
                        final Properties queryHints = (Properties) tmp
                                .getProperty(ASTBase.Annotations.QUERY_HINTS);
                        if (queryHints != null) {
                            if (queryHints.getProperty(name) != null) {
                                // exists as query hint property.
                                if (log.isDebugEnabled())
                                    log.debug("Found as query hint: name="
                                            + name + ", op="
                                            + tmp.toShortString() + "#"
                                            + tmp.hashCode());
                                return true;
                            }
                        }
                        return false;
                    }
                });
    }

    /**
     * Return the number of {@link BOp}s for which the specified property name
     * appears either as a direct annotation of that {@link BOp} or as a
     * property in the query hints properties object for that {@link BOp}.
     * 
     * @param op
     *            The root operator.
     * @param name
     *            The name of the desired property.
     *            
     * @return The number of occurrences of that query hint.
     */
    static private int countQueryHints(final BOp op, final String name) {
        int n = 0;
        final Iterator itr = visitQueryHints(op, name);
        while (itr.hasNext()) {
            itr.next();
            n++;
        }
        return n;
    }

    /**
     * A simple SELECT query with some query hints. This verifies that the query
     * hints are correctly applied to the AST and/or the generated query plan.
     * 
     * 
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     * 
     *   # disable join order optimizer
     *   hint:Group hint:optimizer "None" .
     * 
     *   # query hint binds for the group.
     *   hint:Group hint:maxParallel 10 .
     * 
     *   # query hint binds for the group and any subgroups.
     *   hint:GroupAndSubGroups hint:com.bigdata.bop.PipelineOp.pipelineQueueCapacity 20 .
     * 
     *   # query hint binds for the (sub)query but does not effect other (sub)queries.
     *   hint:SubQuery hint:com.bigdata.relation.accesspath.BlockingBuffer.chunkOfChunksCapacity 20 .
     * 
     *   ?x rdfs:label ?o .
     * 
     *   ?x rdf:type foaf:Person .
     * 
     *   # query hint binds for the immediately proceeding basic graph pattern.
     *   hint:Prior hint:com.bigdata.relation.accesspath.IBuffer.chunkCapacity 1000 .
     * 
     *   # query hint binds for the immediately proceeding basic graph pattern.
     *   #hint:Prior hint:com.bigdata.bop.IPredicate.fullyBufferedReadThreshold 5000 .
     * 
     * }
     * 
*/ public void test_query_hints_01() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-01") .runTest(); /* * Verify that the PREFIX "hint:" was implicitly declared. */ assertEquals(QueryHints.NAMESPACE, astContainer.getOriginalAST() .getPrefixDecls().get("hint")); /* * Check the optimized AST. The magic predicates which correspond to * query hints should have be removed and various annotations made to * the AST which capture the semantics of those query hints. */ { if (log.isInfoEnabled()) log.info(BOpUtility.toString(astContainer.getOptimizedAST())); final JoinGroupNode whereClause = (JoinGroupNode) astContainer .getOptimizedAST().getWhereClause(); /* * The hint to disable the join order optimizer should show up on * the JoinGroupNodes. For the sample query, that means just the * top-level WHERE clause. */ assertEquals(QueryOptimizerEnum.None, whereClause.getQueryOptimizer()); /* * The Query/Group scope pipeline operator hints are found on the * JoinGroupNode. */ assertEquals("10", whereClause .getQueryHint(PipelineOp.Annotations.MAX_PARALLEL, "-1"/* defaultValue */)); assertEquals("20", whereClause.getQueryHint( PipelineOp.Annotations.PIPELINE_QUEUE_CAPACITY, "-1"/* defaultValue */)); assertEquals("20", whereClause.getQueryHint( PipelineOp.Annotations.CHUNK_OF_CHUNKS_CAPACITY, "-1"/* defaultValue */)); /* * Note: This query hint is bound *only* to the prior SP (Scope := * Prior) in the query. That binding is tested below. */ assertNull(whereClause .getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertEquals( 1, countQueryHints(astContainer.getOptimizedAST(), PipelineOp.Annotations.CHUNK_CAPACITY)); /* * hint:optimizer only appears on join groups. The only join group * in this query is the top-level whereClause. */ assertSameIteratorAnyOrder( new Object[] { whereClause }, visitQueryHints(astContainer.getOptimizedAST(), QueryHints.OPTIMIZER)); // There are two statement pattern nodes left (after removing the // query hint SPs). assertEquals(2, whereClause.arity()); // The 1st SP. { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(0); assertEquals(RDFS.LABEL, sp.p().getValue()); assertEquals(10, Integer.parseInt(sp .getQueryHint( PipelineOp.Annotations.MAX_PARALLEL, "-1"/* defaultValue */))); assertEquals(20, Integer.parseInt(sp .getQueryHint( PipelineOp.Annotations.PIPELINE_QUEUE_CAPACITY, "-1"/* defaultValue */))); assertEquals(20, Integer.parseInt(sp .getQueryHint( BufferAnnotations.CHUNK_OF_CHUNKS_CAPACITY, "-1"/* defaultValue */))); /* * The join order optimizer query hint is not be on the SPs (it * is only applied to JoinGroupNode instances). */ assertNull(sp.getProperty(QueryHints.OPTIMIZER)); } // The 2nd SP. { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(1); assertEquals(RDF.TYPE, sp.p().getValue()); assertEquals(1000, Integer.parseInt(sp .getQueryHint(BufferAnnotations.CHUNK_CAPACITY, "-1"/* defaultValue */))); assertEquals(20, Integer.parseInt(sp .getQueryHint( BufferAnnotations.CHUNK_OF_CHUNKS_CAPACITY, "-1"/* defaultValue */))); /* * The join order optimizer query hint is not be on the SPs (it * is only applied to JoinGroupNode instances). */ assertNull(sp.getProperty(QueryHints.OPTIMIZER)); } } // end check of the AST. /* * Check the query plan. Various query hints should have be transferred * onto pipeline operators so they can effect the evaluation of those * operators when they execute on the query engine. The join order was * turned off, so we can expect to see the joins in the same order that * the SPs were given in the query. */ { if (log.isInfoEnabled()) log.info(BOpUtility.toString(astContainer.getQueryPlan())); @SuppressWarnings("rawtypes") final Iterator jitr = BOpUtility.visitAll( astContainer.getQueryPlan(), PipelineJoin.class); /* * Note: The joins are coming back from visitAll() out of the * expected order. This puts them into the expected slots. */ @SuppressWarnings("rawtypes") final PipelineJoin[] joins = new PipelineJoin[2]; joins[1] = jitr.next(); joins[0] = jitr.next(); assertFalse(jitr.hasNext()); { @SuppressWarnings("rawtypes") final PipelineJoin join = joins[0]; final IPredicate pred = join.getPredicate(); if (log.isInfoEnabled()) log.info("pred=" + BOpUtility.toString(pred)); assertEquals(RDFS.LABEL, ((IV) pred.get(1/* p */).get()).getValue()); assertEquals(10, join.getMaxParallel()); assertEquals(20, Integer.parseInt(join .getProperty( PipelineOp.Annotations.PIPELINE_QUEUE_CAPACITY, "-1"/* defaultValue */))); assertEquals(20, join.getChunkOfChunksCapacity()); /* * Verify that the annotations were also applied to the * Predicate associated with the PipelineJoin operator. This is * necessary for the AccessPath class to override its defaults * for vectoring based on the query hints for the * StatementPatternNode. */ // This annotation is not given for this SP. assertNull(pred .getProperty(PipelineOp.Annotations.CHUNK_CAPACITY)); assertEquals( "20", pred.getProperty(PipelineOp.Annotations.CHUNK_OF_CHUNKS_CAPACITY)); // Note: This hint was given, but it is not copied to the Predicate. assertNull( pred.getProperty(PipelineOp.Annotations.PIPELINE_QUEUE_CAPACITY)); // Note: This annotation was not given. assertNull(pred .getProperty(IPredicate.Annotations.FULLY_BUFFERED_READ_THRESHOLD)); } { @SuppressWarnings("rawtypes") final PipelineJoin join = joins[1]; final IPredicate pred = join.getPredicate(); if (log.isInfoEnabled()) log.info("pred=" + BOpUtility.toString(pred)); assertEquals(RDF.TYPE, ((IV) pred.get(1/* p */).get()).getValue()); assertEquals(10, join.getMaxParallel()); assertEquals(1000, join.getChunkCapacity()); assertEquals(20, join.getChunkOfChunksCapacity()); /* * Verify that the annotations were also applied to the * Predicate associated with the PipelineJoin operator. This is * necessary for the AccessPath class to override its defaults * for vectoring based on the query hints for the * StatementPatternNode. */ assertEquals("1000", pred.getProperty(PipelineOp.Annotations.CHUNK_CAPACITY)); assertEquals( "20", pred.getProperty(PipelineOp.Annotations.CHUNK_OF_CHUNKS_CAPACITY)); // Note: This hint was given, but it is not copied to the Predicate. assertNull(pred .getProperty(PipelineOp.Annotations.PIPELINE_QUEUE_CAPACITY)); // Note: this hint was not given. assertNull(pred .getProperty(IPredicate.Annotations.FULLY_BUFFERED_READ_THRESHOLD)); } /* * The projection should be the first operator in the query plan. * * Note: The SELECT is outside of the join group in which these * query hints were given. Therefore, only those query hints whose * scope is Query or SubQuery should be applied to the ProjectionOp. */ { final Iterator projItr = BOpUtility.visitAll( astContainer.getQueryPlan(), ProjectionOp.class); assertTrue(projItr.hasNext()); final ProjectionOp projection = projItr.next(); // Not transferred since Scope:=Group. assertNull(projection .getProperty(PipelineOp.Annotations.MAX_PARALLEL)); // Not transferred since Scope:=Group. assertNull(projection .getProperty(PipelineOp.Annotations.CHUNK_CAPACITY)); // Scope:=SubQuery, so transferred to the SELECT clause. assertNotNull(projection .getProperty(PipelineOp.Annotations.CHUNK_OF_CHUNKS_CAPACITY)); assertEquals(20, projection.getChunkOfChunksCapacity()); assertFalse(projItr.hasNext()); } } } /** * Unit test verifies the correct handling {@link QueryHints#QUERYID}. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     * 
     *   # Set the queryId.
     *   hint:Query hint:queryId "458aed8b-2c76-4d29-9cf0-d18d6a74bc0e" .
     * 
     *   ?x rdfs:label ?o .
     * 
     *   ?x rdf:type foaf:Person .
     * 
     * }
     * 
*/ public void test_query_hints_02() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-02") .runTest(); final String expectedIdStr = "458aed8b-2c76-4d29-9cf0-d18d6a74bc0e"; final UUID expectedId = UUID.fromString(expectedIdStr); final String actualIdStr = astContainer.getQueryHint(QueryHints.QUERYID); // Verify that the right queryId was assigned to the ASTContainer. assertEquals(expectedIdStr, actualIdStr); // Verify that the statement pattern conveying the query hint was // removed. assertEquals(2, astContainer.getOptimizedAST().getWhereClause().arity()); // Verify that the queryId was included in the generated query plan. assertEquals( expectedId, astContainer .getQueryPlan() .getProperty( com.bigdata.bop.engine.QueryEngine.Annotations.QUERY_ID)); } /** * Unit test for correct rejection of an unknown query hint. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     * 
     *   # An unknown query hint. This should result in a QueryHintException.
     *   hint:Query hint:unknown "true" .
     * 
     *   ?x rdfs:label ?o .
     * 
     *   ?x rdf:type foaf:Person .
     * 
     * }
     * 
*/ public void test_query_hints_03() throws Exception { try { new TestHelper("query-hints-03").runTest(); } catch (Throwable t) { assertTrue(InnerCause.isInnerCause(t, QueryHintException.class)); } } /** * Part one of a two part unit test which verifies that we lift out a * {@link SubqueryRoot} which is marked the {@link QueryHints#RUN_ONCE} * query hint. In this version of the test, the query hint is not present * and we verify that the sub-select is NOT lifted out. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     *   ?x rdfs:label ?o .
     *   { 
     *      SELECT ?x { ?x rdf:type foaf:Person }
     *   }
     * 
*/ public void test_query_hints_04a() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-04a") .runTest(); final NamedSubqueriesNode namedSubqueries = astContainer .getOptimizedAST().getNamedSubqueries(); assertNull(namedSubqueries); } /** * Part one of a two part unit test which verifies that we lift out a * {@link SubqueryRoot} which is marked the {@link QueryHints#RUN_ONCE} * query hint. In this version of the test, the query hint is present and we * verify that the sub-select is lifted out. */ public void test_query_hints_04b() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-04b") .runTest(); final NamedSubqueriesNode namedSubqueries = astContainer .getOptimizedAST().getNamedSubqueries(); assertNotNull(namedSubqueries); assertEquals(1, namedSubqueries.arity()); } /** * Unit test for the query hints {@link AST2BOpBase.Annotations#HASH_JOIN} * and {@link IPredicate.Annotations#KEY_ORDER}. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     * 
     *   # disable join order optimizer
     *   hint:Group hint:optimizer "None" .
     * 
     *   # One-bound using P___ index.
     *   ?x rdfs:label ?o .
     * 
     *   # Two-bound.  Uses POCS index by default, which is optimal.
     *   ?x rdf:type foaf:Person .
     * 
     *   # Request a hash join against the statement index.
     *   hint:Prior hint:hashJoin "true" .
     * 
     *   # Override the statement index.
     *   hint:Prior hint:com.bigdata.bop.IPredicate.keyOrder "PCSO" .
     * 
     * }
     * 
*/ public void test_query_hints_05() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-05") .runTest(); /* * Check the optimized AST. The magic predicates which correspond to * query hints should have be removed and various annotations made to * the AST which capture the semantics of those query hints. */ { final JoinGroupNode whereClause = (JoinGroupNode) astContainer .getOptimizedAST().getWhereClause(); /* * The hint to disable the join order optimizer should show up on * the JoinGroupNodes. For the sample query, that means just the * top-level WHERE clause. */ assertEquals(QueryOptimizerEnum.None, whereClause.getQueryOptimizer()); // There are two statement pattern nodes left (after removing the // query hint SPs). assertEquals(2, whereClause.arity()); { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(0); assertEquals(RDFS.LABEL, sp.p().getValue()); } { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(1); assertEquals(RDF.TYPE, sp.p().getValue()); assertEquals(SPOKeyOrder.PCSO.toString(), sp.getQueryHint(IPredicate.Annotations.KEY_ORDER)); assertEquals("true", sp.getQueryHint(QueryHints.HASH_JOIN)); } } // end check of the AST. /* * Check the query plan. */ { // Should be one pipeline join. { @SuppressWarnings("rawtypes") final Iterator jitr = BOpUtility.visitAll( astContainer.getQueryPlan(), PipelineJoin.class); assertTrue(jitr.hasNext()); @SuppressWarnings("rawtypes") final PipelineJoin join = jitr.next(); assertEquals(RDFS.LABEL, ((IV)join.getPredicate().get(1/* p */) .get()).getValue()); assertFalse(jitr.hasNext()); } // Should be one hash join. { @SuppressWarnings("rawtypes") final Iterator jitr = BOpUtility.visitAll( astContainer.getQueryPlan(), HashJoinOp.class); assertTrue(jitr.hasNext()); @SuppressWarnings("rawtypes") final HashJoinOp join = jitr.next(); assertEquals(RDF.TYPE, ((IV) join.getPredicate().get(1/* p */) .get()).getValue()); final IPredicate pred = join.getPredicate(); assertEquals(SPOKeyOrder.PCSO, pred.getKeyOrder()); assertFalse(jitr.hasNext()); } } } /** * Unit test for the {@link IPredicate.Annotations#KEY_ORDER} query hint on * a pipeline join (versus a hash join). *

* Note: This test does NOT verify that the pipeline join actually obeys * that query hint. You need to verify that with a unit test of the pipeline * join evaluation and check to see that the right index is used for each * join. */ public void test_query_hints_05a() throws Exception { final ASTContainer astContainer = new TestHelper( "query-hints-05a", "query-hints-05a.rq", "query-hints-05.trig", "query-hints-05.srx" ) .runTest(); /* * Check the optimized AST. The magic predicates which correspond to * query hints should have be removed and various annotations made to * the AST which capture the semantics of those query hints. */ { final JoinGroupNode whereClause = (JoinGroupNode) astContainer .getOptimizedAST().getWhereClause(); /* * The hint to disable the join order optimizer should show up on * the JoinGroupNodes. For the sample query, that means just the * top-level WHERE clause. */ assertEquals(QueryOptimizerEnum.None, whereClause.getQueryOptimizer()); // There are two statement pattern nodes left (after removing the // query hint SPs). assertEquals(2, whereClause.arity()); { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(0); assertEquals(RDFS.LABEL, sp.p().getValue()); } { final StatementPatternNode sp = (StatementPatternNode) whereClause .get(1); assertEquals(RDF.TYPE, sp.p().getValue()); assertEquals(SPOKeyOrder.PCSO.toString(), sp.getQueryHint(IPredicate.Annotations.KEY_ORDER)); // assertEquals("true", // sp.getQueryHint(QueryHints.HASH_JOIN)); } } // end check of the AST. /* * Check the query plan. */ { /* * Should be two pipeline joins. * * The first pipeline join is (?x rdfs:label ?o) * * The second pipeline join is (?x rdf:type foaf:Person) */ @SuppressWarnings("rawtypes") final PipelineJoin[] joins = BOpUtility.toList( BOpUtility.visitAll(astContainer.getQueryPlan(), PipelineJoin.class)).toArray(new PipelineJoin[0]); assertEquals("#joins", 2, joins.length); { @SuppressWarnings("rawtypes") final PipelineJoin join = joins[1]; assertEquals(RDFS.LABEL, ((IV)join.getPredicate().get(1/* p */) .get()).getValue()); } { @SuppressWarnings("rawtypes") final PipelineJoin join = joins[0]; assertEquals(RDF.TYPE, ((IV) join.getPredicate().get(1/* p */) .get()).getValue()); final IPredicate pred = join.getPredicate(); assertEquals(SPOKeyOrder.PCSO, pred.getKeyOrder()); } } } /** * Unit test for {@link QueryHints#CHUNK_SIZE}. * *

     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     *   
     *   # Turn off the join order optimizer.
     *   hint:Query hint:optimizer "None".
     *   
     *   # Disable analytic query for the test.
     *   hint:Query hint:analytic "false".
     * 
     *   # Set the global chunkSize.  This will apply to the SELECT, sub-SELECT
     *   # (including the hash index and hash join operators used to realize the
     *   # sub-select), the SliceOp, etc.  This gets overridden for both SPs in 
     *   # the query.
     *   hint:Query hint:chunkSize "5" .
     * 
     *   ?x rdfs:label ?o .
     *   # Override the vector size for the previous join.
     *   hint:Prior hint:com.bigdata.relation.accesspath.IBuffer.chunkCapacity 1001 .
     *   
     *   { 
     *      SELECT ?x {
     *           ?x rdf:type foaf:Person .
     *           # Override the vector size for previous join.
     *           hint:Prior hint:chunkSize 251 .
     *          } limit 10
     *   }
     *   
     * } limit 20
     * 
*/ public void test_query_hints_06() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-06") .runTest(); // Check the optimized AST. { if (log.isInfoEnabled()) log.info(BOpUtility.toString(astContainer.getOptimizedAST())); // The top-level QueryRoot. final QueryRoot queryRoot = astContainer.getOptimizedAST(); // The top-level WHERE clause. final JoinGroupNode whereClause = (JoinGroupNode) queryRoot .getWhereClause(); /* * There should be just one subquery. It could be a * NamedSubqueryRoot or a SubqueryRoot [we need to know this to know * whether there should be an INCLUDE in the optimized AST]. */ final SubqueryBase subqueryRoot; { final Iterator it = BOpUtility.visitAll( astContainer.getOptimizedAST(), SubqueryBase.class); assertTrue(it.hasNext()); subqueryRoot = it.next(); assertFalse(it.hasNext()); } // The subquery was lifted out as a NamedSubqueryRoot. assertNotNull(astContainer.getOptimizedAST().getNamedSubqueries()); // The INCLUDE for that NamedSubquery final NamedSubqueryInclude nsi = BOpUtility.getOnly(whereClause, NamedSubqueryInclude.class); // Extract the SPs. There should be TWO (2). final List sps = BOpUtility.toList(queryRoot, StatementPatternNode.class); // Only 2 left after we remove the query hints. assertEquals(2, sps.size()); // There is only one SP in the top-level WHERE clause. final StatementPatternNode queryRootSP = BOpUtility.getOnly( whereClause, StatementPatternNode.class); // There is only one SP in the named subquery WHERE clause. final StatementPatternNode subqueryRootSP = BOpUtility.getOnly( subqueryRoot.getWhereClause(), StatementPatternNode.class); /* * The hint to disable the join order optimizer should show up on * the JoinGroupNodes. For the sample query, that means both the * top-level WHERE clause and the WHERE clause on the sub-query. */ assertSameIteratorAnyOrder( new Object[] { whereClause, subqueryRoot.getWhereClause() }, visitQueryHints(astContainer.getOptimizedAST(), QueryHints.OPTIMIZER)); /* * The hint:chunkSize annotation is NOT found. It was turned into * the PipelineOp.Annotations.CHUNK_CAPACITY annotation. */ assertEquals( 0, countQueryHints(astContainer.getOptimizedAST(), QueryHints.CHUNK_SIZE)); /* * The hint:chunkSize annotation was applied the the entire query, * including the sub-query. */ assertNotNull(queryRoot.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(queryRoot.getProjection().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(queryRoot.getWhereClause().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(queryRootSP.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(queryRoot.getSlice().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); /** * Note: The ASTSparql11SubqueryOptimizer runs *after* the * ASTQueryHintOptimizer. It needs to explicitly copy over the query * hints from the original subquery. * * @see * Clean up query hints */ assertNotNull(nsi.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(subqueryRoot.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(subqueryRoot.getProjection().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(subqueryRoot.getWhereClause().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(subqueryRootSP.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertNotNull(subqueryRoot.getSlice().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); // assertSameIteratorAnyOrder( // new Object[] { // // // Main Query. // queryRoot,// // queryRoot.getProjection(), // // whereClause, // // queryRootSP,// // nsi,// // queryRoot.getSlice(),// // // Subquery. // subqueryRoot,// // subqueryRoot.getProjection(),// // subqueryRoot.getWhereClause(), // // subqueryRootSP,// // subqueryRoot.getSlice(),// // },// // visitQueryHints(queryRoot, // PipelineOp.Annotations.CHUNK_CAPACITY)); /* * hint:chunkSize was used to set a global default. we spot check a * few AST nodes here. */ assertEquals("5",queryRoot.getProjection().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertEquals("5",subqueryRoot.getProjection().getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); /* * hint:chunkSize was also used to override that global default. we * check those overrides next. */ assertEquals("1001",queryRootSP.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); assertEquals("251",subqueryRootSP.getQueryHint(PipelineOp.Annotations.CHUNK_CAPACITY)); } // end check of the AST. /* * Check the query plan. * * Note: This is where we make sure that the vectoring overrides for * hint:chunkSize actually made it into the physical query plan. */ { final PipelineOp queryPlan = astContainer.getQueryPlan(); if (log.isInfoEnabled()) log.info(BOpUtility.toString(astContainer.getQueryPlan())); // Check the PipelineJoins. { @SuppressWarnings("rawtypes") final Iterator itr = BOpUtility.visitAll( queryPlan, PipelineJoin.class); { final PipelineJoin join = itr.next(); // Make sure this is the right join. assertEquals(Var.var("o"), join.getPredicate() .get(2/* o */)); assertEquals(join.toString(), 1001, join.getChunkCapacity()); } { final PipelineJoin join = itr.next(); // Make sure this is the right join. assertTrue(join.getPredicate().get(2/* s */).isConstant()); assertEquals(join.toString(), 251, join.getChunkCapacity()); } } // Check the SliceOps. { final List sliceOps = BOpUtility.toList(queryPlan, SliceOp.class); assertEquals(2, sliceOps.size()); final SliceOp queryRootSliceOp = sliceOps.get(0).getLimit() == 20L ? sliceOps .get(0) : sliceOps.get(1); final SliceOp subqueryRootSliceOp = sliceOps.get(0).getLimit() == 10L ? sliceOps .get(0) : sliceOps.get(1); // make we got distinct AST nodes. assertFalse(queryRootSliceOp == subqueryRootSliceOp); assertEquals(5, queryRootSliceOp.getChunkCapacity()); assertEquals(5, subqueryRootSliceOp.getChunkCapacity()); } // Check the ProjectionOps. { final List projectionOps = BOpUtility.toList(queryPlan, ProjectionOp.class); assertEquals(2, projectionOps.size()); final ProjectionOp queryRootProjectionOp = projectionOps.get(0) .getVariables().length == 2 ? projectionOps.get(0) : projectionOps.get(1); final ProjectionOp subqueryRootProjectionOp = projectionOps .get(0).getVariables().length == 1 ? projectionOps .get(0) : projectionOps.get(1); // make we got distinct AST nodes. assertFalse(queryRootProjectionOp == subqueryRootProjectionOp); assertEquals(5, queryRootProjectionOp.getChunkCapacity()); /* * Note: The ASTSparql11SubqueryOptimizer needs to explicitly * copy across the query hints to the new named subquery root in * order for the query hints to show up on the subquery's * ProjectionNode. */ assertEquals(5, subqueryRootProjectionOp.getChunkCapacity()); } /* * Check the operators that realize the named subquery evaluation * and its INCLUDE into the generated query plan. */ final INamedSubqueryOp namedSubqueryOp = BOpUtility.getOnly( queryPlan, INamedSubqueryOp.class); assertEquals(5, ((PipelineOp) namedSubqueryOp).getChunkCapacity()); final SolutionSetHashJoinOp solutionSetHashJoin = BOpUtility .getOnly(queryPlan, SolutionSetHashJoinOp.class); assertEquals(5, solutionSetHashJoin.getChunkCapacity()); } } /** * Unit test for {@link QueryHints#AT_ONCE} when applied to a * {@link PipelineJoin}. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x ?o
     * WHERE {
     *   
     *   # Turn off the join order optimizer.
     *   hint:Query hint:optimizer "None".
     *   
     *   # Disable analytic query for the test.
     *   hint:Query hint:analytic false.
     *   
     *   ?x rdfs:label ?o .
     *   hint:Prior hint:atOnce true .
     *   
     *   { 
     *      SELECT ?x {
     *           ?x rdf:type foaf:Person .
     *           hint:Prior hint:atOnce false .
     *          }
     *   }
     * 
     * }
     * 
* * TODO We have no tests for {@link QueryHints#AT_ONCE} for anything other * than {@link PipelineJoin} (this test) and {@link ServiceNode} (below). * Work through the code and tests required to get the query hint into place * for other kinds of joins as well. */ public void test_query_hints_07() throws Exception { final ASTContainer astContainer = new TestHelper("query-hints-07") .runTest(); final PipelineOp queryPlan = astContainer.getQueryPlan(); // Check the PipelineJoins. { @SuppressWarnings("rawtypes") final Iterator itr = BOpUtility.visitAll(queryPlan, PipelineJoin.class); { final PipelineJoin join = itr.next(); // Make sure this is the right join. assertTrue(join.getPredicate().get(2/* s */).isConstant()); assertEquals(join.toString(), true, join.isPipelinedEvaluation()); } { final PipelineJoin join = itr.next(); // Make sure this is the right join. assertEquals(Var.var("o"), join.getPredicate().get(2/* o */)); assertEquals(join.toString(), false, join.isPipelinedEvaluation()); } } } /** * Unit test for {@link QueryHints#AT_ONCE} when applied to a SERVICE node. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x
     * WHERE {
     *   
     *   SERVICE ?x {
     *      ?x rdf:type foaf:Person .
     *   }
     *   hint:Prior hint:atOnce true.
     * 
     * }
     * 
*/ public void test_query_hints_08() throws Exception { /* * The assumption is that operators are pipelined unless explicitly * overridden. */ assertTrue(PipelineOp.Annotations.DEFAULT_PIPELINED); final URI serviceURI = new URIImpl("http://www.bigdata.com/mockService"); final List serviceSolutions = new LinkedList(); { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Mike")); serviceSolutions.add(bset); } { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Bryan")); serviceSolutions.add(bset); } ServiceRegistry.getInstance().add(serviceURI, new OpenrdfNativeMockServiceFactory(serviceSolutions)); final ASTContainer astContainer; try { astContainer = new TestHelper("query-hints-08").runTest(); } finally { ServiceRegistry.getInstance().remove(serviceURI); } final PipelineOp queryPlan = astContainer.getQueryPlan(); { final Iterator itr = BOpUtility.visitAll( queryPlan, ServiceCallJoin.class); { final ServiceCallJoin join = itr.next(); assertFalse(itr.hasNext()); assertEquals(join.toString(), false, join.isPipelinedEvaluation()); } } } /** * Unit test for {@link QueryHints#AT_ONCE} when applied to a SERVICE node. * This verifies that we can turn off "at-once" evaluation for a SERVICE. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x
     * WHERE {
     *   
     *   SERVICE ?x {
     *      ?x rdf:type foaf:Person .
     *   }
     *   hint:Prior hint:atOnce false.
     * 
     * }
     * 
*/ public void test_query_hints_08b() throws Exception { /* * The assumption is that operators are pipelined unless explicitly * overridden. */ assertTrue(PipelineOp.Annotations.DEFAULT_PIPELINED); final URI serviceURI = new URIImpl("http://www.bigdata.com/mockService"); final List serviceSolutions = new LinkedList(); { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Mike")); serviceSolutions.add(bset); } { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Bryan")); serviceSolutions.add(bset); } ServiceRegistry.getInstance().add(serviceURI, new OpenrdfNativeMockServiceFactory(serviceSolutions)); final ASTContainer astContainer; try { astContainer = new TestHelper("query-hints-08b").runTest(); } finally { ServiceRegistry.getInstance().remove(serviceURI); } final PipelineOp queryPlan = astContainer.getQueryPlan(); { final Iterator itr = BOpUtility.visitAll( queryPlan, ServiceCallJoin.class); { final ServiceCallJoin join = itr.next(); assertFalse(itr.hasNext()); assertEquals(join.toString(), true, join.isPipelinedEvaluation()); } } } /** * Unit test for {@link QueryHints#CHUNK_SIZE} when applied to a SERVICE node. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x
     * WHERE {
     *   
     *   SERVICE ?x {
     *      ?x rdf:type foaf:Person .
     *   }
     *   hint:Prior hint:chunkSize 1001 .
     * 
     * }
     * 
*/ public void test_query_hints_09() throws Exception { final URI serviceURI = new URIImpl("http://www.bigdata.com/mockService"); final List serviceSolutions = new LinkedList(); { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Mike")); serviceSolutions.add(bset); } { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Bryan")); serviceSolutions.add(bset); } ServiceRegistry.getInstance().add(serviceURI, new OpenrdfNativeMockServiceFactory(serviceSolutions)); final ASTContainer astContainer; try { astContainer = new TestHelper("query-hints-09").runTest(); } finally { ServiceRegistry.getInstance().remove(serviceURI); } final PipelineOp queryPlan = astContainer.getQueryPlan(); { final Iterator itr = BOpUtility.visitAll( queryPlan, ServiceCallJoin.class); { final ServiceCallJoin join = itr.next(); assertFalse(itr.hasNext()); assertEquals(join.toString(), 1001, join.getChunkCapacity()); } } } /** * Unit test for {@link QueryHints#MAX_PARALLEL} when applied to a SERVICE node. * *
     * PREFIX rdf:  
     * PREFIX rdfs: 
     * PREFIX foaf: 
     * 
     * SELECT ?x
     * WHERE {
     *   
     *   SERVICE ?x {
     *      ?x rdf:type foaf:Person .
     *   }
     *   hint:Prior hint:maxParallel 1001 .
     * 
     * }
     * 
* * TODO Test for PipelineJoin and other things as well. */ public void test_query_hints_10() throws Exception { final URI serviceURI = new URIImpl("http://www.bigdata.com/mockService"); final List serviceSolutions = new LinkedList(); { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Mike")); serviceSolutions.add(bset); } { final MapBindingSet bset = new MapBindingSet(); bset.addBinding("x",new URIImpl("http://www.bigdata.com/Bryan")); serviceSolutions.add(bset); } ServiceRegistry.getInstance().add(serviceURI, new OpenrdfNativeMockServiceFactory(serviceSolutions)); final ASTContainer astContainer; try { astContainer = new TestHelper("query-hints-10").runTest(); } finally { ServiceRegistry.getInstance().remove(serviceURI); } final PipelineOp queryPlan = astContainer.getQueryPlan(); { final Iterator itr = BOpUtility.visitAll( queryPlan, ServiceCallJoin.class); { final ServiceCallJoin join = itr.next(); assertFalse(itr.hasNext()); assertEquals(join.toString(), 13, join.getMaxParallel()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy