/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
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;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.algebra.StatementPattern.Scope;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.Var;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.XSD;
import com.bigdata.rdf.sail.sparql.Bigdata2ASTSPARQLParser;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext;
import com.bigdata.rdf.sparql.ast.eval.ASTSearchOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.ASTBottomUpOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.ASTSetValueExpressionsOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.ASTSubGroupJoinVarOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.ASTWildcardProjectionOptimizer;
import com.bigdata.rdf.sparql.ast.optimizers.IASTOptimizer;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;
import com.bigdata.rdf.vocab.decls.FOAFVocabularyDecl;
/**
* Test suite for methods supporting static analysis of the variables, including
* whether a variable MUST be bound, MIGHT be bound, or is NOT bound. The static
* analysis centers around {@link GraphPatternGroup}s. Methods are provided for
* both the group itself, the group and its parent groups, and the group and its
* child groups.
* <p>
* Note: The static analysis depends on the AST as generated from the parse tree
* or as rewritten by one or more {@link IASTOptimizer}s. Most tests do not run
* the {@link IASTOptimizer}s because we want to analyze the AST as generated
* from the parse tree rather than the AST as optimized by the suite of
* optimizers. In many cases, actually running the suite of optimizers will
* cause some structures to be pruned from the AST. This is especially true of
* the unit tests for the static analysis of the TCK queries, many of which use
* variables in filters when the variables are not bound in that scope or use a
* "badly designed left join" pattern.
*
* TODO If we modify the methods which report on the must/may bindings to look
* for null IVs, then we will need to add the URIs appearing in the query to the
* database before we run the parser.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestStaticAnalysis extends AbstractASTEvaluationTestCase {
private final static Logger log = Logger
.getLogger(TestStaticAnalysis.class);
/**
*
*/
public TestStaticAnalysis() {
}
/**
* @param arg0
*/
public TestStaticAnalysis(String arg0) {
super(arg0);
}
private static final Set<IVariable<?>> EMPTY_SET = Collections.emptySet();
/**
* Unit test of static analysis for variables which must be bound by a
* query. This involves checking the WHERE clause and the projection for the
* query.
*
* @throws MalformedQueryException
*/
public void test_static_analysis01()
throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x where { ?x rdf:type foaf:Person }";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("x"));
assertEquals(expected, sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(
expected,
sa.getDefinitelyProducedBindings(queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/**
* Unit test of static analysis for variables which must be bound by a
* query. This involves checking the WHERE clause and the projection for the
* query. In this case, a constant is projected out of the query in addition
* to the bindings produced by the WHERE clause.
*
* @throws MalformedQueryException
*/
public void test_static_analysis02()
throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x (12 as ?y) where { ?x rdf:type foaf:Person }";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
expectedProjected.add(Var.var("y"));
assertEquals(expectedProjected, sa.getDefinitelyProducedBindings(queryRoot));
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("x"));
assertEquals(
expectedWhereClause,
sa.getDefinitelyProducedBindings(queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/**
* Unit test of static analysis for variables with a named subquery. This
* test verifies that we observe the variables projected by the subquery,
* but not those which it uses internally.
*
* @throws MalformedQueryException
*/
public void test_static_analysis03()
throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x (12 as ?y)\n" +
" with \n{" +
" select ?x {\n" +
" ?x rdf:type foaf:Person .\n" +
" ?x rdfs:label ?y .\n" +
" }\n" +
" } as %namedSet1 \n"+
" where {\n" +
" include %namedSet1\n" +
"}";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
expectedProjected.add(Var.var("y"));
assertEquals(expectedProjected, sa.getDefinitelyProducedBindings(queryRoot));
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("x"));
assertEquals(
expectedWhereClause,
sa.getDefinitelyProducedBindings(queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/**
* Unit test of static analysis for variables with a SPARQL 1.1 subquery.
* This test verifies that we observe the variables projected by the
* subquery, but not those which it uses internally.
*
* @throws MalformedQueryException
*/
public void test_static_analysis04()
throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x (12 as ?y)\n" +
" where {\n" +
" ?q foaf:knows ?p ." +
" {\n" +
" select ?x {\n" +
" ?x rdf:type foaf:Person .\n" +
" ?x rdfs:label ?z .\n" +
" }\n" +
" }\n" +
"}";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
expectedProjected.add(Var.var("y"));
assertEquals(expectedProjected, sa.getDefinitelyProducedBindings(queryRoot));
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("x"));
expectedWhereClause.add(Var.var("p"));
expectedWhereClause.add(Var.var("q"));
assertEquals(
expectedWhereClause,
sa.getDefinitelyProducedBindings(queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/**
* Unit test of static analysis for a SERVICE call.
*
* @see <a href="http://trac.blazegraph.com/ticket/816" > Wildcard projection
* ignores variables inside a SERVICE call </a>
*/
public void test_static_analysis05()
throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x (12 as ?y)\n" +
" where {\n" +
" service ?uri {\n" +
" ?x rdf:type foaf:Person .\n" +
" ?x rdfs:label ?z .\n" +
" }\n" +
"}";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
expectedProjected.add(Var.var("y"));
assertEquals(expectedProjected, sa.getDefinitelyProducedBindings(queryRoot));
// The spanned variables includes the SERVICE URI (if it is a variable).
{
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("uri"));
expectedWhereClause.add(Var.var("x"));
expectedWhereClause.add(Var.var("z"));
assertEquals(expectedWhereClause, sa.getSpannedVariables(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>()));
}
// The definitely bound variables does NOT include the SERVICE URI. When
// that is a variable it needs to become bound through other means.
{
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("x"));
expectedWhereClause.add(Var.var("z"));
assertEquals(expectedWhereClause, sa.getDefinitelyProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for computing the join variables for a named subquery based on
* the analysis of the bindings which MUST be produced by the subquery and
* those which MUST be bound on entry into the group in which the subquery
* solution set is included within the main query.
* <p>
* Note: The join should be on <code>?x</code> in this example.
*
* FIXME Write more unit tests for
* {@link StaticAnalysis#getJoinVars(SubqueryRoot, Set)} and friends.
*/
public void test_static_analysis_join_vars() throws MalformedQueryException {
final String queryStr = "" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+
"select ?x ?o \n"+
" with {"+
" select ?x where { ?x rdf:type foaf:Person }\n"+
" } AS %namedSet1 \n"+
"where { \n" +
" ?x rdfs:label ?o \n" +
" INCLUDE %namedSet1 \n"+
"}";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
// variables which must be bound in the top-level query's projection.
{
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
expectedProjected.add(Var.var("o"));
assertEquals(expectedProjected,
sa.getDefinitelyProducedBindings(queryRoot));
}
// variables which must be bound in the named subquery's projection.
{
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("x"));
final NamedSubqueryRoot namedSubquery = (NamedSubqueryRoot) queryRoot
.getNamedSubqueries().get(0);
assertEquals(expectedProjected,
sa.getDefinitelyProducedBindings(namedSubquery));
}
// variables which must be bound in the main query's where clause.
{
final Set<IVariable<?>> expectedWhereClause = new LinkedHashSet<IVariable<?>>();
expectedWhereClause.add(Var.var("x"));
expectedWhereClause.add(Var.var("o"));
assertEquals(expectedWhereClause, sa.getDefinitelyProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// the join variables as reported by static analysis.
{
final Set<IVariable<?>> expectedJoinVars = new LinkedHashSet<IVariable<?>>();
expectedJoinVars.add(Var.var("x"));
final NamedSubqueryRoot namedSubquery = (NamedSubqueryRoot) queryRoot
.getNamedSubqueries().get(0);
final NamedSubqueryInclude anInclude = BOpUtility.visitAll(
queryRoot, NamedSubqueryInclude.class).next();
final Set<IVariable<?>> vars = sa.getJoinVars(namedSubquery,
anInclude, new LinkedHashSet<IVariable<?>>());
assertEquals(expectedJoinVars, vars);
}
}
/**
* Static analysis of TCK query:
*
* <pre>
* PREFIX : <http://example/>
*
* SELECT ?v { :x :p ?v . FILTER(?v = 1)
* </pre>
*
* This query does not present a problem since <code>?v</code> is in scope
* when the filter is evaluated.
*/
public void test_static_analysis_filter_nested_1() throws MalformedQueryException {
final String queryStr = "" + //
"PREFIX : <http://example/>\n" + //
"SELECT ?v \n" +//
"{ :x :p ?v . FILTER(?v = 1) }";
final QueryRoot queryRoot = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI).getOriginalAST();
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
@SuppressWarnings("unchecked")
final GraphPatternGroup<IGroupMemberNode> whereClause = queryRoot
.getWhereClause();
final Set<IVariable<?>> expected = asSet(new String[]{"v"});
// Test "must" bound bindings for the query.
assertEquals(expected, sa.getDefinitelyProducedBindings(queryRoot));
// Test "must" bound bindings for the where clause.
assertEquals(expected, sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings for the where clause.
assertEquals(expected,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "incoming" bindings for the where clause.
assertEquals(EMPTY_SET, sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
}
/**
* Static analysis of TCK query:
*
* <pre>
* PREFIX : <http://example/>
*
* SELECT ?v
* { :x :p ?v . { FILTER(?v = 1) } }
* </pre>
*
* <code>?v</code> is not bound in the FILTER because it is evaluated with
* bottom up semantics and therefore the bindings from the parent group are
* not visible.
*/
public void test_static_analysis_filter_nested_2()
throws MalformedQueryException {
final String queryStr = "" + //
"PREFIX : <http://example/>\n" + //
"SELECT ?v \n" +//
"{ :x :p ?v . { FILTER(?v = 1) } }";
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser().parseQuery2(
queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// Set the IValueExpressions on the AST.
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
// Test "must" bound bindings for the query.
assertEquals(asSet(new String[] { "v" }),
sa.getDefinitelyProducedBindings(queryRoot));
// WHERE clause
{
@SuppressWarnings("unchecked")
final GraphPatternGroup<IGroupMemberNode> whereClause = queryRoot
.getWhereClause();
final Set<IVariable<?>> expected = asSet(new String[] { "v" });
// Test "incoming" bindings for the where clause.
assertEquals(EMPTY_SET, sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings for the where clause.
assertEquals(expected, sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings for the where clause.
assertEquals(expected, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// FILTER's group clause.
{
final JoinGroupNode filterClause = (JoinGroupNode) queryRoot
.getWhereClause().get(1);
// Test "incoming" bindings.
assertEquals(asSet(new String[] { "v" }), sa.getDefinitelyIncomingBindings(
filterClause, new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
EMPTY_SET,
sa.getDefinitelyProducedBindings(filterClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(EMPTY_SET, sa.getMaybeProducedBindings(filterClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// The FILTER node itself.
final FilterNode filter = BOpUtility.visitAll(queryRoot,
FilterNode.class).next();
assertEquals(Collections.singletonList(filter), sa.getPreFilters(filterClause));
assertEquals(Collections.emptyList(), sa.getJoinFilters(filterClause));
assertEquals(Collections.emptyList(), sa.getPostFilters(filterClause));
assertEquals(Collections.emptyList(), sa.getPruneFilters(filterClause));
}
}
/**
* Static analysis of TCK query:
*
* <pre>
* PREFIX : <http://example/>
*
* SELECT *
* {
* :x :p ?v .
* { :x :q ?w
* OPTIONAL { :x :p ?v2 FILTER(?v = 1) }
* }
* }
* </pre>
*
* <code>?v</code> is bound in the outer join group.
* <p>
* <code>?w</code> is bound in the child join group regardless of whether
* the embedded optional succeeds.
* <p>
* <code>?v</code> is not bound in the FILTER because it is evaluated with
* bottom up semantics and therefore the bindings from the outer parent
* group are not visible.
* <p>
* For reference, the AST for that SPARQL query is:
*
* <pre>
* PREFIX : <http://example/>
* QueryType: SELECT
* SELECT *
* JoinGroupNode {
* StatementPatternNode(ConstantNode(TermId(0U)[http://example/x]), ConstantNode(TermId(0U)[http://example/p]), VarNode(v), DEFAULT_CONTEXTS)
* JoinGroupNode {
* StatementPatternNode(ConstantNode(TermId(0U)[http://example/x]), ConstantNode(TermId(0U)[http://example/q]), VarNode(w), DEFAULT_CONTEXTS)
* JoinGroupNode [optional] {
* StatementPatternNode(ConstantNode(TermId(0U)[http://example/x]), ConstantNode(TermId(0U)[http://example/p]), VarNode(v2), DEFAULT_CONTEXTS)
* FILTER( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(v),ConstantNode(XSDInteger(1)))[ com.bigdata.rdf.sparql.ast.FunctionNode.functionURI=http://www.w3.org/2005/xpath-functions#equal-to] )
* }
* }
* }
* </pre>
*
* This can be reduced to a filter which does not bind anything. Since the
* FILTER can not succeed, it should logically be replaced by failing the
* group(s) within which it appears. This needs to be done recursively up to
* the parent, stopping at the first parent group which is optional. If all
* parents up to the WHERE clause are eliminated, then the WHERE clause
* itself can not succeed and the query should be replaced by a
* "DropSolutionsBOp". The DropSolutionsOp can be substituted in directly
* for a group which can not succeed and then we can work the pruning of the
* parents as an efficiency.
* <p>
* Note: An AST optimizer needs to recognize and transform this query by
* lifting out
*
* <pre>
* :x :q ?w OPTIONAL { :x :p ?v2 FILTER(?v = 1) }
* </pre>
*
* into a named subquery. Since the named subquery runs without any incoming
* bindings, the result will be as if we had used bottom up evaluation.
*
* @see ASTBottomUpOptimizer
*/
public void test_static_analysis_filter_scope_1()
throws MalformedQueryException {
final String queryStr = "" + //
"PREFIX : <http://example/>\n" + //
"SELECT * \n" + //
"{ \n" + //
" :x :p ?v . \n" + //
" { :x :q ?w \n" + //
" OPTIONAL { :x :p ?v2 FILTER(?v = 1) } \n" + //
" } \n" + //
"}"//
;
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// Set the IValueExpressions on the AST.
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
// Rewrite the wild card in the projection.
queryRoot = (QueryRoot) new ASTWildcardProjectionOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
// Test "must" bound bindings for the query.
assertEquals(asSet(new String[] { "v", "w" }),
sa.getDefinitelyProducedBindings(queryRoot));
// Outer group clause { :x :p ?v . }
{
final GraphPatternGroup<IGroupMemberNode> group = (JoinGroupNode) queryRoot
.getWhereClause();
// Test "incoming" bindings.
assertEquals(EMPTY_SET, sa.getDefinitelyIncomingBindings(group,
new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "v" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "v", "w" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "v" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "v", "w", "v2" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// Nested group clause { :x :q ?w }
{
final GraphPatternGroup<IGroupMemberNode> group = (JoinGroupNode) queryRoot
.getWhereClause().get(1);
// Test "incoming" bindings.
assertEquals(
asSet(new String[] { "v" }),
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "w" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "w" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "w" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "w", "v2" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// Optional group clause { :x :p ?v2 FILTER(?v = 1) }
{
final JoinGroupNode group = (JoinGroupNode) queryRoot
.getWhereClause().get(1).get(1);
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>());
// Test "incoming" bindings.
assertEquals(
asSet(new String[] { "v", "w" }),
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "v2" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "v2" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "v2" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "v2" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// The FILTER node itself.
final FilterNode filter = BOpUtility.visitAll(queryRoot,
FilterNode.class).next();
assertEquals(Collections.singletonList(filter), sa.getPreFilters(group));
assertEquals(Collections.emptyList(), sa.getJoinFilters(group));
assertEquals(Collections.emptyList(), sa.getPostFilters(group));
assertEquals(Collections.emptyList(), sa.getPruneFilters(group));
}
}
/**
* Join-scope - 1 (aka var-scope-join-1).
*
* <pre>
* PREFIX : <http://example/>
*
* SELECT *
* {
* ?X :name "paul"
* {?Y :name "george" . OPTIONAL { ?X :email ?Z } }
* }
* </pre>
* <p>
* Note: For this query, the bindings of <code>?X</code> in the outer group
* are not visible when the inner groups are evaluated. Because of this,
* <code>?X</code> in the optional clause binds for values which differ from
* the values bound on <code>?X</code> in the outer group. This means that
* the solutions from the middle group with bindings for <code>?Y</code> and
* <code>?X</code> fail to join with the solutions in the outer group. Note
* that there is no email address for "paul" for the data set used to run
* this query. If there were, then the query would have a result.
* <p>
* These group expressions need to be evaluated independently because they
* are not sharing a binding for <code>?X</code> until we join them together
* on <code>?X</code>.
* <p>
* In order for us to run this query correctly we need to run
*
* <pre>
* {?Y :name "george" . OPTIONAL { ?X :email ?Z } }
* </pre>
*
* BEFORE
*
* <pre>
* ?X :name "paul"
* </pre>
*
* This can be most easily achieved by lifting the former out into a named
* subquery.
*
* @see ASTBottomUpOptimizer
*/
public void test_static_analysis_join_scope_1()
throws MalformedQueryException {
final String queryStr = "" + //
"PREFIX : <http://example/>\n" + //
"SELECT * \n" + //
"{ \n" + //
" ?X :name \"paul\" . \n" + //
" {?Y :name \"george\" . OPTIONAL { ?X :email ?Z } } \n" + //
"}"//
;
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// Rewrite the wild card in the projection.
queryRoot = (QueryRoot) new ASTWildcardProjectionOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
// Test "must" bound bindings for the query.
assertEquals(asSet(new String[] { "X", "Y" }),
sa.getDefinitelyProducedBindings(queryRoot));
// Outer group clause { ?X :name "paul" }
{
final GraphPatternGroup<IGroupMemberNode> group = (JoinGroupNode) queryRoot
.getWhereClause();
// Test "incoming" bindings.
assertEquals(
EMPTY_SET,
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "X" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "X", "Y" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "X" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "X", "Y", "Z" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// Nested group clause { ?Y :name "george" }
{
final GraphPatternGroup<IGroupMemberNode> group = (JoinGroupNode) queryRoot
.getWhereClause().get(1);
// Test "incoming" bindings.
assertEquals(
asSet(new String[] { "X" }),
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "Y" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "Y" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "Y" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "Y", "X", "Z" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// Optional group clause OPTIONAL { ?X :email ?Z }
{
final JoinGroupNode group = (JoinGroupNode) queryRoot
.getWhereClause().get(1).get(1);
// Test "incoming" bindings.
assertEquals(
asSet(new String[] { "X", "Y" }),
sa.getDefinitelyIncomingBindings(group,new LinkedHashSet<IVariable<?>>()));
// Test "must" bound bindings.
assertEquals(
asSet(new String[] { "X", "Z" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "must" bound bindings (recursive).
assertEquals(
asSet(new String[] { "X", "Z" }),
sa.getDefinitelyProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
// Test "maybe" bound bindings.
assertEquals(
asSet(new String[] { "X", "Z" }),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), false/* recursive */));
// Test "maybe" bound bindings (recursive).
assertEquals(
asSet(new String[] { "X", "Z"}),
sa.getMaybeProducedBindings(group,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for analysis of a {@link ServiceNode}. The analysis of a
* {@link ServiceNode} is just the analysis of the graph pattern reported by
* {@link ServiceNode#getGraphPattern()}.
*
* <pre>
* PREFIX bd: <http://www.bigdata.com/rdf/search#>
* SELECT ?subj ?score
* WHERE {
* ?lit bd:search "mike" .
* ?lit bd:relevance ?score .
* ?subj ?p ?lit .
* }
* </pre>
*
* Note: The "be:search", "bd:relevance" and <code>?subj ?p ?lit</code>
* statement patterns below will be transformed into a {@link ServiceNode}.
* Since there is nothing else in the main query's WHERE clause, the
* {@link ServiceNode} is not lifted out into a named subquery.
*/
public void test_static_analysis_serviceCall() throws MalformedQueryException {
final String queryStr = "" + //
"PREFIX bd: <http://www.bigdata.com/rdf/search#>\n" + //
"SELECT ?subj ?score\n" + //
"WHERE {\n" + //
" ?lit bd:search \"mike\" .\n" + //
" ?lit bd:relevance ?score .\n" + //
" ?subj ?p ?lit .\n" + //
"}";
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// rewrite the search predicates as an AST ServiceNode.
queryRoot = (QueryRoot) new ASTSearchOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
{
// variables which must be bound in the top-level query's
// projection.
{
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("subj"));
expectedProjected.add(Var.var("score"));
assertEquals(expectedProjected,
sa.getDefinitelyProducedBindings(queryRoot));
}
// variables which must be bound in the main query's where clause.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("subj"));
expected.add(Var.var("score"));
expected.add(Var.var("p"));
expected.add(Var.var("lit"));
assertEquals(expected, sa.getDefinitelyProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// variables which may be bound in the main query's where clause.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("subj"));
expected.add(Var.var("score"));
expected.add(Var.var("p"));
expected.add(Var.var("lit"));
assertEquals(expected, sa.getMaybeProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
}
/**
* Test suite for predicting the join variables for a SERVICE call.
*
* <pre>
* SELECT ?s ?o1 ?o2
* {
* SERVICE <http://localhost:18080/openrdf/repositories/endpoint1> {
* ?s ?p ?o1 . }
* OPTIONAL {
* SERVICE <http://localhost:18080/openrdf/repositories/endpoint2> {
* ?s ?p2 ?o2 }
* }
* }
* </pre>
*/
public void test_static_analysis_serviceCall2() throws MalformedQueryException {
final String queryStr = "SELECT ?s ?o1 ?o2\n"//
+ "{\n"//
+ " SERVICE <http://localhost:18080/openrdf/repositories/endpoint1> {\n"//
+ " ?s ?p ?o1 . }\n"//
+ " OPTIONAL {\n"//
+ " SERVICE <http://localhost:18080/openrdf/repositories/endpoint2> {\n"//
+ " ?s ?p2 ?o2 }\n"//
+ " }\n"//
+ "}";
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// Assign join variables to join groups.
queryRoot = (QueryRoot) new ASTSubGroupJoinVarOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
/**
* Locate the various pieces of the AST.
*
* <pre>
*
* QueryType: SELECT
* SELECT VarNode(s) VarNode(o1) VarNode(o2)
* JoinGroupNode {
* SERVICE <ConstantNode(TermId(0U)[http://localhost:18080/openrdf/repositories/endpoint1])> {
* JoinGroupNode {
* StatementPatternNode(VarNode(s), VarNode(p), VarNode(o1), DEFAULT_CONTEXTS)
* }
* }
* JoinGroupNode [optional] {
* SERVICE <ConstantNode(TermId(0U)[http://localhost:18080/openrdf/repositories/endpoint2])> {
* JoinGroupNode {
* StatementPatternNode(VarNode(s), VarNode(p2), VarNode(o2), DEFAULT_CONTEXTS)
* }
* }
* }
* }
* </pre>
*/
final GraphPatternGroup<?> whereClause = queryRoot.getWhereClause();
final ServiceNode endpoint1;
final JoinGroupNode optionalGroup;
final ServiceNode endpoint2;
{
endpoint1 = (ServiceNode) whereClause.get(0);
assertEquals(endpoint1.getServiceRef().getValue(), new URIImpl(
"http://localhost:18080/openrdf/repositories/endpoint1"));
optionalGroup = (JoinGroupNode) whereClause.get(1);
assertTrue(optionalGroup.isOptional());
endpoint2 = (ServiceNode) optionalGroup.get(0);
assertEquals(endpoint2.getServiceRef().getValue(), new URIImpl(
"http://localhost:18080/openrdf/repositories/endpoint2"));
// final Iterator<ServiceNode> itr = BOpUtility.visitAll(
// whereClause, ServiceNode.class);
//
// endpoint1 = itr.next();
//
// assertEquals(endpoint1.getServiceRef().getValue(), new URIImpl(
// "http://localhost:18080/openrdf/repositories/endpoint1"));
//
// endpoint2 = itr.next();
//
// assertEquals(endpoint2.getServiceRef().getValue(), new URIImpl(
// "http://localhost:18080/openrdf/repositories/endpoint2"));
//
// assertFalse(itr.hasNext());
}
{
// variables which must be bound in the top-level query's
// projection.
{
final Set<IVariable<?>> expectedProjected = new LinkedHashSet<IVariable<?>>();
expectedProjected.add(Var.var("s"));
expectedProjected.add(Var.var("o1"));
assertEquals(expectedProjected,
sa.getDefinitelyProducedBindings(queryRoot));
}
// variables which must be bound in the main query's where clause.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
expected.add(Var.var("p"));
expected.add(Var.var("o1"));
assertEquals(expected, sa.getDefinitelyProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// variables which may be bound in the main query's where clause.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
expected.add(Var.var("p"));
expected.add(Var.var("o1"));
expected.add(Var.var("p2"));
expected.add(Var.var("o2"));
assertEquals(expected, sa.getMaybeProducedBindings(
queryRoot.getWhereClause(),
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
// variables which must be bound by endpoint1.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
expected.add(Var.var("p"));
expected.add(Var.var("o1"));
assertEquals(expected, sa.getDefinitelyProducedBindings(endpoint1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// variables which must be bound by endpoint2.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
expected.add(Var.var("p2"));
expected.add(Var.var("o2"));
assertEquals(expected, sa.getDefinitelyProducedBindings(endpoint2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// ServiceCallJoin variables for endpoint1
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
assertEquals(expected, sa.getJoinVars(endpoint1,
new LinkedHashSet<IVariable<?>>()));
}
// ServiceCallJoin variables for endpoint2
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
assertEquals(expected, sa.getJoinVars(endpoint2,
new LinkedHashSet<IVariable<?>>()));
}
// Join with the OPTIONAL group.
{
final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
expected.add(Var.var("s"));
final Set<IVariable<?>> actual = new HashSet<IVariable<?>>();
for (IVariable<?> v : optionalGroup.getJoinVars()) {
actual.add(v);
}
assertEquals(expected, actual);
}
}
/**
* Unit test(s) for the correct identification of pre-, join-, post-, and
* prune- filters.
*/
public void test_static_analysis_filters() throws MalformedQueryException {
final String queryStr = ""+//
"PREFIX : <http://www.bigdata.com/>\n" +//
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"+//
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n"+//
"PREFIX foaf: <http://xmlns.com/foaf/0.1/> \n"+//
"SELECT ?a ?b \n" +//
" WHERE {\n" +//
" ?a rdf:type foaf:Person . \n" +//
" ?a foaf:knows ?b .\n"+//
" { \n"+//
" ?a :age ?ageA .\n"+//
" ?b :age ?ageB .\n"+//
" FILTER ( ?a != ?b ) .\n"+// pre-filter (can be lifted)
" FILTER ( ?ageA > ?ageB) .\n"+// join-filter
" FILTER ( ?x < 100 ) .\n"+// prune-filter (can be pruned)
" }\n"+//
" FILTER ( ?ageA > 20 ) .\n"+//post-filter (depends on subgroup)
" "+//
"}";
final ASTContainer astContainer = new Bigdata2ASTSPARQLParser()
.parseQuery2(queryStr, baseURI);
final AST2BOpContext context = new AST2BOpContext(astContainer, store);
QueryRoot queryRoot = astContainer.getOriginalAST();
// Set the IValueExpressions on the AST.
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer().optimize(
context, new QueryNodeWithBindingSet(queryRoot, null)).getQueryNode();
if (log.isInfoEnabled())
log.info("\nqueryStr=\n" + queryStr + "\nAST:\n"
+ BOpUtility.toString(queryRoot));
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
/*
* Locate the outer join group and the inner join group.
*/
final JoinGroupNode outerGroup;
final JoinGroupNode innerGroup;
{
final Iterator<JoinGroupNode> itr = BOpUtility.visitAll(
queryRoot.getWhereClause(), JoinGroupNode.class);
outerGroup = itr.next();
innerGroup = itr.next();
}
/*
* Locate the different filters in the query.
*/
final FilterNode ageA_GT_ageB;
final FilterNode a_NE_b;
final FilterNode x_LT_10;
final FilterNode ageA_GT_20;
// Filters in the outer group.
{
FilterNode tmp = null;
for(IGroupMemberNode child : outerGroup) {
if(child instanceof FilterNode) {
tmp = (FilterNode)child;
break;
}
}
ageA_GT_20 = tmp;
}
assertNotNull(ageA_GT_20);
// Filters in the inner group.
// " FILTER ( ?ageA > ?ageB) .\n"+// join-filter
// " FILTER ( ?a != ?b ) .\n"+// pre-filter
// " FILTER ( ?x < 100 ) .\n"+// prune-filter
{
FilterNode GT = null;
FilterNode NE = null;
FilterNode LT = null;
for (IGroupMemberNode child : innerGroup) {
if (child instanceof FilterNode) {
final FilterNode tmp = (FilterNode) child;
final FunctionNode expr = (FunctionNode) tmp
.getValueExpressionNode();
if (expr.getFunctionURI().equals(FunctionRegistry.GT)) {
GT = tmp;
} else if (expr.getFunctionURI()
.equals(FunctionRegistry.NE)) {
NE = tmp;
} else if (expr.getFunctionURI()
.equals(FunctionRegistry.LT)) {
LT = tmp;
} else
throw new AssertionError();
}
}
ageA_GT_ageB = GT;
a_NE_b = NE;
x_LT_10 = LT;
}
assertNotNull(ageA_GT_ageB);
assertNotNull(a_NE_b);
assertNotNull(x_LT_10);
/*
* Now verify the static analysis of the filters.
*/
// " FILTER ( ?ageA > ?ageB) .\n"+// join-filter
// " FILTER ( ?a != ?b ) .\n"+// pre-filter
// " FILTER ( ?x < 100 ) .\n"+// prune-filter
// " }\n"+//
// " FILTER ( ?ageA > 20 ) .\n"+//post-filter
// Inner group.
assertEquals("pre-filters", Collections.singletonList(a_NE_b),
sa.getPreFilters(innerGroup));
assertEquals("join-filters", Collections.singletonList(ageA_GT_ageB),
sa.getJoinFilters(innerGroup));
assertEquals("post-filters", Collections.singletonList(x_LT_10),
sa.getPostFilters(innerGroup));
assertEquals("prune-filters", Collections.singletonList(x_LT_10),
sa.getPruneFilters(innerGroup));
assertEquals("definitely-and-filters", asSet(new String[] { "a", "b",
"ageA", "ageB", "x" }),
sa.getDefinitelyProducedBindingsAndFilterVariables(innerGroup,
new LinkedHashSet<IVariable<?>>()));
// Outer group.
assertEquals("pre-filters", Collections.emptyList(),
sa.getPreFilters(outerGroup));
assertEquals("join-filters", Collections.emptyList(),
sa.getJoinFilters(outerGroup));
assertEquals("post-filters", Collections.singletonList(ageA_GT_20),
sa.getPostFilters(outerGroup));
assertEquals("prune-filters", Collections.emptyList(),
sa.getPruneFilters(outerGroup));
assertEquals("definitely-and-filters", asSet(new String[] { "a", "b",
"ageA" }),
sa.getDefinitelyProducedBindingsAndFilterVariables(outerGroup,
new LinkedHashSet<IVariable<?>>()));
}
/**
* Unit test focused on required and optional {@link StatementPatternNode}
* s.
*/
public void test_static_analysis_getMaybeProducedBindings() {
final IV<?, ?> p = makeIV(new URIImpl("http://example/p"));
final IV<?, ?> q = makeIV(new URIImpl("http://example/q"));
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause;
final StatementPatternNode sp1, sp2;
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("n"));
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
sp1 = new StatementPatternNode(new VarNode("a"),
new ConstantNode(p), new VarNode("n"));
whereClause.addChild(sp1);
sp2 = new StatementPatternNode(new VarNode("a"),
new ConstantNode(q), new VarNode("m"));
whereClause.addChild(sp2);
sp2.setOptional(true);
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
// Where clause.
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("m"));
assertEquals(expectedVars, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// sp1
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(sp1,
new LinkedHashSet<IVariable<?>>()));
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyProducedBindings(sp1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(sp1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// sp2
{
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
sp2, new LinkedHashSet<IVariable<?>>()));
}
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("m"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(sp2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(sp2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
}
/**
* Unit test of static analysis methods as they pertain to a MINUS group.
* Unlike OPTIONAL, the variables in the left and right hand side of a MINUS
* operator are not visible to one another. This has implications for (a)
* MINUS operators where the right hand side does not share any variables
* (such cases should be pruned); and (b) FILTERS in the right hand side
* will fail in they assume visibility of variables in the left hand side.
*
* <pre>
* PREFIX : <http://example/>
* SELECT ?a ?n
* WHERE {
* ?a :p ?n
* MINUS {
* ?a :q ?n .
* }
* }
* </pre>
*/
public void test_static_analysis_minus_sharedVariables() {
final IV<?,?> p = makeIV(new URIImpl("http://example/p"));
final IV<?,?> q = makeIV(new URIImpl("http://example/q"));
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, minusGroup;
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("n"));
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(p), new VarNode("n")));
minusGroup = new JoinGroupNode();
whereClause.addChild(minusGroup);
minusGroup.setMinus(true);
minusGroup.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(q), new VarNode("n")));
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
/*
* First, the QueryRoot.
*/
{
assertEquals(expectedVars, sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(expectedVars, sa.getMaybeProducedBindings(queryRoot));
}
/*
* Now the whereClause.
*/
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(Collections.emptySet(),
sa.getMaybeIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* Finally, the MINUS group.
*/
{
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Variant test for a MINUS operator without shared variables.
*
* <pre>
* PREFIX : <http://example/>
* SELECT ?s ?p ?o
* WHERE {
* ?s ?p ?o
* MINUS {
* ?x ?y ?z .
* }
* }
* </pre>
*/
public void test_static_analysis_minus_nothingShared() {
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, minusGroup;
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("s"));
projection.addProjectionVar(new VarNode("p"));
projection.addProjectionVar(new VarNode("o"));
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause.addChild(new StatementPatternNode(new VarNode("s"),
new VarNode("p"), new VarNode("o")));
minusGroup = new JoinGroupNode();
whereClause.addChild(minusGroup);
minusGroup.setMinus(true);
minusGroup.addChild(new StatementPatternNode(new VarNode("x"),
new VarNode("y"), new VarNode("z")));
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("s"));
expectedVars.add(Var.var("p"));
expectedVars.add(Var.var("o"));
final Set<IVariable<?>> otherVars = new LinkedHashSet<IVariable<?>>();
otherVars.add(Var.var("x"));
otherVars.add(Var.var("y"));
otherVars.add(Var.var("z"));
/*
* First, the QueryRoot.
*/
{
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(expectedVars, sa.getMaybeProducedBindings(queryRoot));
}
/*
* Now the whereClause.
*/
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(Collections.emptySet(),
sa.getMaybeIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* Finally, the MINUS group.
*/
{
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
assertEquals(
otherVars,
sa.getDefinitelyProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(otherVars, sa.getMaybeProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Variant test for query mixing MINUS and OPTIONAL groups.
*
* <pre>
* PREFIX : <http://example/>
* SELECT ?a ?n ?b
* WHERE {
* ?a :p ?n
* OPTIONAL {
* ?b :p 3.0
* }
* MINUS {
* ?a :q ?n .
* OPTIONAL {
* ?c :q 3.0
* }
* }
* }
* </pre>
*/
public void test_static_analysis_minus_and_optional() {
final IV<?,?> p = makeIV(new URIImpl("http://example/p"));
final IV<?,?> q = makeIV(new URIImpl("http://example/q"));
final IV<?, ?> three = makeIV(new LiteralImpl("3.0", XSD.DECIMAL));
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, optGroup1, minusGroup, optGroup2;
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("n"));
projection.addProjectionVar(new VarNode("b"));
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(p), new VarNode("n")));
optGroup1 = new JoinGroupNode(true/*optional*/);
whereClause.addChild(optGroup1);
optGroup1.addChild(new StatementPatternNode(new VarNode("b"),
new ConstantNode(p), new ConstantNode(three)));
minusGroup = new JoinGroupNode();
whereClause.addChild(minusGroup);
minusGroup.setMinus(true);
minusGroup.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(q), new VarNode("n")));
optGroup2 = new JoinGroupNode(true/*optional*/);
minusGroup.addChild(optGroup2);
optGroup2.addChild(new StatementPatternNode(new VarNode("c"),
new ConstantNode(q), new ConstantNode(three)));
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
/*
* First, the QueryRoot.
*/
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyProducedBindings(queryRoot));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars, sa.getMaybeProducedBindings(queryRoot));
}
/*
* Now the whereClause.
*/
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(Collections.emptySet(),
sa.getMaybeIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* Optional group1.
*/
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars,
sa.getDefinitelyIncomingBindings(optGroup1,
new LinkedHashSet<IVariable<?>>()));
assertEquals(expectedVars,
sa.getMaybeIncomingBindings(optGroup1,
new LinkedHashSet<IVariable<?>>()));
expectedVars.clear();
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optGroup1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(optGroup1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* The MINUS group.
*/
{
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
minusGroup, new LinkedHashSet<IVariable<?>>()));
}
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("c"));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(minusGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/*
* Optional group 2.
*/
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars,
sa.getDefinitelyIncomingBindings(optGroup2,
new LinkedHashSet<IVariable<?>>()));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars,
sa.getMaybeIncomingBindings(optGroup2,
new LinkedHashSet<IVariable<?>>()));
expectedVars.clear();
expectedVars.add(Var.var("c"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optGroup2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(expectedVars, sa.getMaybeProducedBindings(optGroup2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Variant test for query mixing normal child join groups and OPTIONAL
* join groups.
*
* <pre>
* PREFIX : <http://example/>
* SELECT ?a ?n ?b ?c
* WHERE {
* ?a :p ?n
* {
* ?a :q ?n .
* OPTIONAL {
* ?c :q 3.0
* }
* }
* OPTIONAL {
* ?b :p 3.0
* }
* }
* </pre>
*/
public void test_static_analysis_subGroups_and_optional() {
final IV<?,?> p = makeIV(new URIImpl("http://example/p"));
final IV<?,?> q = makeIV(new URIImpl("http://example/q"));
final IV<?, ?> three = makeIV(new LiteralImpl("3.0", XSD.DECIMAL));
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, optGroup1, childGroup, optGroup2;
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("n"));
projection.addProjectionVar(new VarNode("b"));
projection.addProjectionVar(new VarNode("c"));
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(p), new VarNode("n")));
{
childGroup = new JoinGroupNode();
whereClause.addChild(childGroup);
childGroup.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(q), new VarNode("n")));
optGroup2 = new JoinGroupNode(true/* optional */);
childGroup.addChild(optGroup2);
optGroup2.addChild(new StatementPatternNode(new VarNode("c"),
new ConstantNode(q), new ConstantNode(three)));
}
{
optGroup1 = new JoinGroupNode(true/* optional */);
whereClause.addChild(optGroup1);
optGroup1.addChild(new StatementPatternNode(new VarNode("b"),
new ConstantNode(p), new ConstantNode(three)));
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
/*
* First, the QueryRoot.
*/
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyProducedBindings(queryRoot));
expectedVars.add(Var.var("b"));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars, sa.getMaybeProducedBindings(queryRoot));
}
/*
* Now the whereClause.
*/
{
assertEquals(Collections.emptySet(),
sa.getDefinitelyIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
assertEquals(Collections.emptySet(),
sa.getMaybeIncomingBindings(whereClause,
new LinkedHashSet<IVariable<?>>()));
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("b"));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars, sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* The child join group.
*/
{
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
childGroup, new LinkedHashSet<IVariable<?>>()));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
childGroup, new LinkedHashSet<IVariable<?>>()));
}
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(childGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("c"));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(childGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
/*
* Optional group 2 (embedded in the child join group).
*/
{
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars,
sa.getDefinitelyIncomingBindings(optGroup2,
new LinkedHashSet<IVariable<?>>()));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
optGroup2, new LinkedHashSet<IVariable<?>>()));
}
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("c"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optGroup2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(optGroup2,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
}
/*
* Optional group1.
*/
{
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("n"));
assertEquals(expectedVars, sa.getDefinitelyIncomingBindings(
optGroup1, new LinkedHashSet<IVariable<?>>()));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars, sa.getMaybeIncomingBindings(
optGroup1, new LinkedHashSet<IVariable<?>>()));
}
{
final Set<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optGroup1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(optGroup1,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
}
/**
* Unit test for
* {@link StaticAnalysis#getProjectedVars(IGroupMemberNode, GraphPatternGroup, QueryBase, Set, Set)}
* . This unit test is a based on
* <code>bigdata-perf/CI/govtrack/queries/query10.rq</code>
* <p>
* Given:
*
* <pre>
* SELECT ?var1 ?var6 ?var4 ?var10
* WHERE {
* ?var1 a <http://www.rdfabout.com/rdf/schema/politico/Politician>
* OPTIONAL {
* ?var1 <http://www.rdfabout.com/rdf/schema/usgovt/name> ?var6
* }.
* OPTIONAL {
* ?var12 <http://www.rdfabout.com/rdf/schema/usbill/sponsor> ?var1.
* ?var12 <http://www.rdfabout.com/rdf/schema/usbill/title> ?var4
* }.
* OPTIONAL {
* ?var1 <http://www.w3.org/2001/vcard-rdf/3.0#N> ?var13.
* ?var13 <http://www.w3.org/2001/vcard-rdf/3.0#Family> ?var10
* }
* }
* </pre>
*
* This test verifies that the correct projection is computed for each of
* the sub-groups in the query. Those projections are as follows:
*
* <pre>
* SELECT ?var1 ?var6
* WHERE {
* ?var1 a <http://www.rdfabout.com/rdf/schema/politico/Politician>
* OPTIONAL {
* ?var1 <http://www.rdfabout.com/rdf/schema/usgovt/name> ?var6
* }.
* }
* </pre>
*
* <pre>
* SELECT ?var1 ?var4
* WHERE {
* INCLUDE %_set1
* OPTIONAL {
* ?var12 <http://www.rdfabout.com/rdf/schema/usbill/sponsor> ?var1.
* ?var12 <http://www.rdfabout.com/rdf/schema/usbill/title> ?var4
* }.
* }
* </pre>
*
* <pre>
* SELECT ?var1 ?var10
* WHERE {
* INCLUDE %_set1
* OPTIONAL {
* ?var1 <http://www.w3.org/2001/vcard-rdf/3.0#N> ?var13.
* ?var13 <http://www.w3.org/2001/vcard-rdf/3.0#Family> ?var10
* }
* }
* </pre>
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/397
*/
public void test_static_analysis_getProjectedVars() {
@SuppressWarnings("rawtypes")
final IV a = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV polititian = makeIV(new URIImpl("http://www.rdfabout.com/rdf/schema/politico/Politician"));
@SuppressWarnings("rawtypes")
final IV name = makeIV(new URIImpl("http://www.rdfabout.com/rdf/schema/usgovt/name"));
@SuppressWarnings("rawtypes")
final IV sponsor = makeIV(new URIImpl("http://www.rdfabout.com/rdf/schema/usgovt/sponsor"));
@SuppressWarnings("rawtypes")
final IV title = makeIV(new URIImpl("http://www.rdfabout.com/rdf/schema/usgovt/title"));
@SuppressWarnings("rawtypes")
final IV N = makeIV(new URIImpl("http://www.w3.org/2001/vcard-rdf/3.0#N"));
@SuppressWarnings("rawtypes")
final IV family = makeIV(new URIImpl("http://www.w3.org/2001/vcard-rdf/3.0#Family"));
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, optionalGroup1, optionalGroup2, optionalGroup3;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("var1"));
projection.addProjectionVar(new VarNode("var6"));
projection.addProjectionVar(new VarNode("var4"));
projection.addProjectionVar(new VarNode("var10"));
}
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
// ?_var1 a <http://www.rdfabout.com/rdf/schema/politico/Politician>
whereClause.addChild(new StatementPatternNode(new VarNode("var1"),
new ConstantNode(a), new ConstantNode(polititian),
null/* c */, Scope.DEFAULT_CONTEXTS));
// ?_var1 <http://www.rdfabout.com/rdf/schema/usgovt/name> ?_var6
{
optionalGroup1 = new JoinGroupNode(true/* optional */);
whereClause.addChild(optionalGroup1);
optionalGroup1.addChild(new StatementPatternNode(
new VarNode("var1"), new ConstantNode(name),
new VarNode("var6"), null/* c */,
Scope.DEFAULT_CONTEXTS));
}
{
optionalGroup2 = new JoinGroupNode(true/* optional */);
whereClause.addChild(optionalGroup2);
// ?_var12 <http://www.rdfabout.com/rdf/schema/usbill/sponsor> ?_var1.
optionalGroup2.addChild(new StatementPatternNode(new VarNode(
"var12"), new ConstantNode(sponsor), new VarNode(
"var1"), null/* c */, Scope.DEFAULT_CONTEXTS));
// ?_var12 <http://www.rdfabout.com/rdf/schema/usbill/title> ?_var4
optionalGroup2.addChild(new StatementPatternNode(new VarNode(
"var12"), new ConstantNode(title), new VarNode(
"var4"), null/* c */, Scope.DEFAULT_CONTEXTS));
}
{
optionalGroup3 = new JoinGroupNode(true/* optional */);
whereClause.addChild(optionalGroup3);
// ?_var1 <http://www.w3.org/2001/vcard-rdf/3.0#N> ?_var13.
optionalGroup3.addChild(new StatementPatternNode(new VarNode(
"var1"), new ConstantNode(N), new VarNode(
"var13"), null/* c */, Scope.DEFAULT_CONTEXTS));
// ?_var13 <http://www.w3.org/2001/vcard-rdf/3.0#Family> ?_var10
optionalGroup3.addChild(new StatementPatternNode(new VarNode(
"var13"), new ConstantNode(family), new VarNode(
"var10"), null/* c */, Scope.DEFAULT_CONTEXTS));
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// Optional group 1.
if(true){
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("var1"));
expectedVars.add(Var.var("var6"));
assertEquals(expectedVars, sa.getProjectedVars(optionalGroup1,
optionalGroup1, queryRoot, exogenousVars,
new LinkedHashSet<IVariable<?>>()// projectedVars
));
}
// Optional group 2.
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("var1"));
expectedVars.add(Var.var("var4"));
assertEquals(expectedVars, sa.getProjectedVars(optionalGroup2,
optionalGroup2, queryRoot, exogenousVars,
new LinkedHashSet<IVariable<?>>()// projectedVars
));
}
// Optional group 3.
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("var1"));
expectedVars.add(Var.var("var10"));
assertEquals(expectedVars, sa.getProjectedVars(optionalGroup3,
optionalGroup3, queryRoot, exogenousVars,
new LinkedHashSet<IVariable<?>>()// projectedVars
));
}
}
/**
* Unit test for static analysis of the MUST and MIGHT bound variables for a
* subquery.
*
* <pre>
* SELECT (?a as ?b) {
* ?a rdf:type foaf:Person
* }
* </pre>
*
* Should report that <code>?b</code> must be bound
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/430
*
* TODO Extend this test to cover the case where <code>b</code> is an
* exogenously bound variable. This does not change the MUST/MIGHT
* analysis of the query.
*/
public void test_static_analysis_projection_01() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionExpression(new AssignmentNode(
new VarNode("b"), new VarNode("a")));
}
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(rdfType), new ConstantNode(foafPerson)));
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("b"));
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Variant with optional group binding <code>?a</code> (so <code>?b</code>
* is MIGHT be bound).
*
* <pre>
* SELECT (?a as ?b) {
* OPTIONAL {
* ?a rdf:type foaf:Person
* }
* }
* </pre>
*
* Should report that <code>?b</code> might be bound
*
* TODO Extend this test to also cover the case where <code>b</code> is an
* exogenously bound variable. In that case, it MUST be bound.
*
* TODO Extend this test to also cover the case where <code>a</code> is an
* exogenously bound variable. Since <code>a</code> is not projected, it's
* bound value SHOULD NOT be visible inside of the query (variables with
* exogenous variables must obey the same scoping rules as variables which
* only become bound during query evaluation).
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/430
*/
public void test_static_analysis_projection_02() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, optionalGroup;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionExpression(new AssignmentNode(
new VarNode("b"), new VarNode("a")));
}
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
optionalGroup = new JoinGroupNode(true/*optional*/);
whereClause.addChild(optionalGroup);
optionalGroup.addChild(new StatementPatternNode(new VarNode("a"),
new ConstantNode(rdfType), new ConstantNode(foafPerson)));
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("a"));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// optionalGroup
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optionalGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(optionalGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test where the SELECT expression includes the bind of a constant
* onto a variable.
*/
public void test_static_analysis_projection_03() {
// @SuppressWarnings("rawtypes")
// final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
// The source AST.
final QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionExpression(new AssignmentNode(
new VarNode("b"), new ConstantNode(foafPerson)));
}
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("b"));
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for static analysis of the MUST and MIGHT bound variables for a
* query involving a select expression which could result in an error. Note
* that this query is NOT an aggregation query so the error will fail the
* solution.
*
* <pre>
* SELECT ?a ?b (?a/?b as ?c) {
* ?x rdf:type foaf:Person .
* ?x :age ?a .
* ?x :grade ?b .
* }
* </pre>
*
* In this query, it is presumed that <code>?b</code> is the number of years
* of school and will be ZERO in the data for people who have not yet begun
* formal schooling. Hence, the select expression can evaluate to an error
* (divide by zero). However, since this is NOT an aggregation query, the
* error will cause the solution to be dropped. Hence, <code>?c</code> is
* definitely bound for this query.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/430
*
* TODO Extend this test to cover the case where <code>b</code> is an
* exogenously bound variable. This does not change the MUST/MIGHT
* analysis of the query.
*/
public void test_static_analysis_projection_04() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
projection.addProjectionExpression(new AssignmentNode(
new VarNode("c"), FunctionNode.binary(
FunctionRegistry.DIVIDE, new VarNode("a"),
new VarNode("b"))));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
whereClause.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(age), new VarNode("a")));
whereClause.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(grade), new VarNode("b")));
}
}
/*
* Note: Since it involves function nodes, we need to generate the value
* expressions in order to run this test.
*/
{
final IBindingSet[] bindingSets = new IBindingSet[] {};
final ASTContainer astContainer = new ASTContainer(queryRoot);
final AST2BOpContext context = new AST2BOpContext(astContainer,
store);
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer()
.optimize(context, new QueryNodeWithBindingSet(queryRoot, bindingSets))
.getQueryNode();
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("b"));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("x"));
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for static analysis of the MUST and MIGHT bound variables for a
* query involving a select expression which depends on a variable which is
* not definitely bound.
*
* <pre>
* SELECT ?a ?b (?a + ?b as ?c) {
* ?x rdf:type foaf:Person .
* ?x :age ?a .
* OPTIONAL {
* ?x :grade ?b .
* }
* }
* </pre>
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/430
*
* TODO Extend this test to cover the case where <code>b</code> is an
* exogenously bound variable. This does not change the MUST/MIGHT
* analysis of the query.
*/
public void test_static_analysis_projection_05() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, optionalGroup;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
projection.addProjectionExpression(new AssignmentNode(
new VarNode("c"), FunctionNode.binary(
FunctionRegistry.DIVIDE, new VarNode("a"),
new VarNode("b"))));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
whereClause.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(age), new VarNode("a")));
optionalGroup = new JoinGroupNode(true/* optional */);
whereClause.addChild(optionalGroup);
optionalGroup.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(grade), new VarNode(
"b")));
}
}
/*
* Note: Since it involves function nodes, we need to generate the value
* expressions in order to run this test.
*/
{
final IBindingSet[] bindingSets = new IBindingSet[] {};
final ASTContainer astContainer = new ASTContainer(queryRoot);
final AST2BOpContext context = new AST2BOpContext(astContainer,
store);
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer()
.optimize(context, new QueryNodeWithBindingSet(queryRoot, bindingSets))
.getQueryNode();
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
expectedVars.add(Var.var("b"));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("x"));
expectedVars.add(Var.var("a"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
// optionalGroup
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("x"));
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(optionalGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(optionalGroup,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for static analysis of the MUST and MIGHT bound variables for a
* subquery involving aggregation and a select expression ("The result of an
* Aggregate which returns an error, is an error, but the SELECT expression
* result of projecting an error is unbound").
*
* <pre>
* SELECT ?a ?b (?a/?b as ?c) {
* ?x rdf:type foaf:Person .
* ?x :age ?a .
* ?x :grade ?b .
* }
* GROUP BY ?b
* </pre>
*
* In this query, it is presumed that <code>?b</code> is the number of years
* of school and will be ZERO in the data for people who have not yet begun
* formal schooling. Hence, the select expression can evaluate to an error
* (divide by zero).
* <p>
* Since this is an aggregation query, the solution will still be reported
* with an unbound value for <code>?c</code>. This illustrates the general
* principle that we can not assume that a select expression based on
* definitely bound variables will itself be definitely bound.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/430
*
* TODO Extend this test to cover the case where <code>b</code> is an
* exogenously bound variable. Does this change the MUST/MIGHT analysis
* of the query?
*/
public void test_static_analysis_projection_06() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
projection.addProjectionExpression(new AssignmentNode(
new VarNode("c"), FunctionNode.binary(
FunctionRegistry.DIVIDE, new VarNode("a"),
new VarNode("b"))));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
whereClause.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(age), new VarNode("a")));
whereClause.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(grade), new VarNode("b")));
}
// GROUP BY
{
final GroupByNode groupBy = new GroupByNode();
queryRoot.setGroupBy(groupBy);
groupBy.addGroupByVar(new VarNode("b"));
}
}
/*
* Note: Since it involves function nodes, we need to generate the value
* expressions in order to run this test.
*/
{
final IBindingSet[] bindingSets = new IBindingSet[] {};
final ASTContainer astContainer = new ASTContainer(queryRoot);
final AST2BOpContext context = new AST2BOpContext(astContainer,
store);
queryRoot = (QueryRoot) new ASTSetValueExpressionsOptimizer()
.optimize(context, new QueryNodeWithBindingSet(queryRoot, bindingSets))
.getQueryNode();
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
final Set<IVariable<?>> exogenousVars = new LinkedHashSet<IVariable<?>>();
// QueryRoot
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("b"));
assertEquals(expectedVars,
sa.getDefinitelyProducedBindings(queryRoot));
expectedVars.add(Var.var("c"));
assertEquals(expectedVars,
sa.getMaybeProducedBindings(queryRoot));
}
// whereClause
{
final LinkedHashSet<IVariable<?>> expectedVars = new LinkedHashSet<IVariable<?>>();
expectedVars.add(Var.var("x"));
expectedVars.add(Var.var("a"));
expectedVars.add(Var.var("b"));
assertEquals(
expectedVars,
sa.getDefinitelyProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
assertEquals(
expectedVars,
sa.getMaybeProducedBindings(whereClause,
new LinkedHashSet<IVariable<?>>(), true/* recursive */));
}
}
/**
* Unit test for locating a reference go a {@link GraphPatternGroup} which
* appears as an annotation of another node. This covers {@link UnionNode}
* and {@link JoinGroupNode} when appearing as children of a
* {@link QueryRoot}'s whereClause.
*
* <pre>
* SELECT ?a ?b {
* ?x rdf:type foaf:Person .
* {
* ?x :age ?a
* } UNION {
* ?x :grade ?b
* }
* }
* </pre>
*/
public void test_findParent_01() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, joinGroup1, joinGroup2;
final UnionNode unionNode;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
unionNode = new UnionNode();
whereClause.addChild(unionNode);
joinGroup1 = new JoinGroupNode();
unionNode.addChild(joinGroup1);
joinGroup1.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(age), new VarNode("a")));
joinGroup2 = new JoinGroupNode();
unionNode.addChild(joinGroup2);
joinGroup2.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(grade), new VarNode("b")));
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
assertTrue(unionNode == sa.findParent(joinGroup1));
assertTrue(unionNode == sa.findParent(joinGroup2));
assertTrue(whereClause == sa.findParent(unionNode));
assertTrue(queryRoot == sa.findParent(whereClause));
}
/**
* This verifies the ability to locate the parent of a graph group in a
* {@link SubqueryRoot}.
*
* <pre>
* SELECT ?a ?b {
* ?x rdf:type foaf:Person .
* {
* SELECT ?x ?a ?b {
* ?x :age ?a
* ?x :grade ?b
* }
* }
* }
* </pre>
*/
public void test_findParent_02() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, subqueryWhereClause;
final SubqueryRoot subqueryRoot;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
subqueryRoot = new SubqueryRoot(QueryType.SELECT);
whereClause.addChild(subqueryRoot);
subqueryWhereClause = new JoinGroupNode();
subqueryRoot.setWhereClause(subqueryWhereClause);
subqueryWhereClause.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(age), new VarNode(
"a")));
subqueryWhereClause.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(grade), new VarNode(
"b")));
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
assertTrue(subqueryRoot == sa.findParent(subqueryWhereClause));
assertTrue(whereClause == subqueryRoot.getParent());
}
/**
* This verifies the ability to locate the parent of a graph group in a
* {@link NamedSubqueryRoot}.
*
* <pre>
* SELECT ?a ?b {
* WITH {
* SELECT ?x ?a ?b {
* ?x :age ?a
* ?x :grade ?b
* }
* } as %set1
* ?x rdf:type foaf:Person .
* INCLUDE %set1 .
* }
* </pre>
*/
public void test_findParent_03() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, subqueryWhereClause;
final NamedSubqueryRoot subqueryRoot;
final NamedSubqueryInclude include;
final String namedSet = "set1";
{
// Named subquery.
{
subqueryRoot = new NamedSubqueryRoot(QueryType.SELECT, namedSet);
subqueryWhereClause = new JoinGroupNode();
subqueryRoot.setWhereClause(subqueryWhereClause);
subqueryWhereClause.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(age), new VarNode(
"a")));
subqueryWhereClause.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(grade), new VarNode(
"b")));
queryRoot.getNamedSubqueriesNotNull().add(subqueryRoot);
}
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
whereClause.addChild(include = new NamedSubqueryInclude(
namedSet));
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
assertTrue(subqueryRoot == sa.findParent(subqueryWhereClause));
assertTrue(whereClause == include.getParent());
}
/**
* Unit test for locating a reference go a {@link GraphPatternGroup} which
* appears as an annotation of another node. This covers {@link UnionNode}
* and {@link JoinGroupNode} when appearing as children of a
* {@link QueryRoot}'s whereClause.
*
* <pre>
* SELECT ?a ?b {
* ?x rdf:type foaf:Person .
* SERVICE {
* ?x :age ?a
* }
* FILTER {
* EXISTS {?x :grade ?b}
* }
* }
* </pre>
*/
public void test_findParent_04() {
@SuppressWarnings("rawtypes")
final IV rdfType = makeIV(RDF.TYPE);
@SuppressWarnings("rawtypes")
final IV foafPerson = makeIV(FOAFVocabularyDecl.Person);
@SuppressWarnings("rawtypes")
final IV age = makeIV(new URIImpl("http://example.org/age"));
@SuppressWarnings("rawtypes")
final IV grade = makeIV(new URIImpl("http://example.org/grade"));
@SuppressWarnings("rawtypes")
final IV serviceUri = makeIV(new URIImpl("http://example.org/service"));
// The source AST.
QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
final JoinGroupNode whereClause, serviceGroup, existsGroup;
final ServiceNode serviceNode;
final FilterNode filterNode;
{
// Top-level projection
{
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
projection.addProjectionVar(new VarNode("a"));
projection.addProjectionVar(new VarNode("b"));
}
// WHERE clause.
{
whereClause = new JoinGroupNode();
queryRoot.setWhereClause(whereClause);
whereClause
.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(rdfType), new ConstantNode(
foafPerson)));
serviceGroup = new JoinGroupNode();
serviceGroup.addChild(new StatementPatternNode(
new VarNode("x"), new ConstantNode(age), new VarNode(
"a")));
serviceNode = new ServiceNode(new ConstantNode(serviceUri),
serviceGroup);
whereClause.addChild(serviceNode);
existsGroup = new JoinGroupNode();
existsGroup.addChild(new StatementPatternNode(new VarNode("x"),
new ConstantNode(grade), new VarNode("b")));
filterNode = new FilterNode(new ExistsNode(new VarNode(
"anon-var-1"), existsGroup));
whereClause.addChild(filterNode);
}
}
final StaticAnalysis sa = new StaticAnalysis(queryRoot);
assertTrue(serviceNode == sa.findParent(serviceGroup));
assertTrue(filterNode == sa.findParent(existsGroup));
assertTrue(queryRoot == sa.findParent(whereClause));
}
// /**
// * Unit test for whether or not a variable is "in-scope" in some part of the
// * AST.
// *
// * <pre>
// * PREFIX : <http://example/>
// * SELECT ?v
// * {
// * :x :p ?v .
// * FILTER(?v = 1) .
// * }
// * </pre>
// *
// * The variable <code>?v</code> is in scope in the outer group and the
// * filter.
// *
// * @see http://www.w3.org/TR/sparql11-query/#variableScope
// */
// public void test_static_analysis_inScope_01() {
//
// @SuppressWarnings("rawtypes")
// final IV x = makeIV(new URIImpl("http://example.org/x"));
// @SuppressWarnings("rawtypes")
// final IV p = makeIV(new URIImpl("http://example.org/p"));
// @SuppressWarnings("rawtypes")
// final IV one = makeIV(new LiteralImpl("1", XSD.INTEGER));
//
// // The source AST.
// QueryRoot queryRoot = new QueryRoot(QueryType.SELECT);
// final JoinGroupNode whereClause;
// final FilterNode filterNode;
// {
//
// // Top-level projection
// {
// final ProjectionNode projection = new ProjectionNode();
// queryRoot.setProjection(projection);
//
// projection.addProjectionVar(new VarNode("v"));
// }
//
// // WHERE clause.
// {
// whereClause = new JoinGroupNode();
// queryRoot.setWhereClause(whereClause);
//
// whereClause.addChild(new StatementPatternNode(new ConstantNode(
// x), new ConstantNode(p), new VarNode("v")));
//
// filterNode = new FilterNode(FunctionNode.binary(
// FunctionRegistry.EQ, new VarNode("v"),
// new ConstantNode(one)));
// whereClause.addChild(filterNode);
//
// }
//
// }
//
// final StaticAnalysis sa = new StaticAnalysis(queryRoot);
//
// {
//
// final Set<IVariable<?>> expected = new LinkedHashSet<IVariable<?>>();
//
// expected.add(Var.var("v"));
//
// assertEquals(expected, sa.getInScopeVariables(filterNode,
// new LinkedHashSet<IVariable<?>>()));
//
// }
//
// }
//
// /**
// * <code>?v</code> is also in scope when it appears in an OPTIONAL group
// *
// * <pre>
// * PREFIX : <http://example/>
// * SELECT ?v
// * {
// * :x :p ?v .
// * OPTIONAL {
// * FILTER(?v = 1) .
// * }
// * }
// * </pre>
// */
// public void test_static_analysis_inScope_02() {
//
// }
//
// /**
// * This test examines a DAWG/TCK query based on a badly formed left join
// * pattern:
// *
// * <pre>
// * PREFIX : <http://example/>
// * SELECT ?v ?w ?v2
// * {
// * :x :p ?v .
// * { :x :q ?w
// * OPTIONAL { :x :p ?v2 FILTER(?v = 1) }
// * }
// * }
// * </pre>
// *
// * The variable <code>?v</code> is in scope in the outer group, but it is
// * not in scope in the filter.
// *
// * TODO What about when the optional group has ?v in the BPG?
// *
// * TODO What about when the optional group has ?v and the parent group
// * also has ?v?
// *
// * TODO Examples with more variables.
// */
// public void test_static_analysis_inScope_03() {
//
// fail("write tests");
//
// }
//
// /**
// *
// * TODO The test suite should cover all of the bottom-up examples so we can
// * work out what the right behavior of this method is in each case. It
// * should also cover the EXISTS graph pattern, SERVICE graph pattern, and
// * subquery graph patterns as those are not linked in the standard
// * parent-child hierarchy (or write a separate test suite for finding those
// * things, maybe for BOpUtility).
// */
// public void test_static_analysis_inScope_xxx() {
//
// fail("write tests");
//
// }
}