/* * Copyright (C) 2016 SYSTAP, LLC DBA Blazegraph * * 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; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.bigdata.rdf.sparql.ast.optimizers; import com.bigdata.bop.Bind; import com.bigdata.bop.Constant; import com.bigdata.bop.IBindingSet; import com.bigdata.bop.IValueExpression; import com.bigdata.bop.IVariable; import com.bigdata.bop.Var; import com.bigdata.bop.rdf.aggregate.COUNT; import com.bigdata.bop.rdf.aggregate.MAX; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.internal.impl.TermId; import com.bigdata.rdf.sail.sparql.Bigdata2ASTSPARQLParser; import com.bigdata.rdf.sparql.ast.ASTContainer; import com.bigdata.rdf.sparql.ast.AbstractASTEvaluationTestCase; import com.bigdata.rdf.sparql.ast.ConstantNode; import com.bigdata.rdf.sparql.ast.GraphPatternGroup; import com.bigdata.rdf.sparql.ast.GroupByNode; import com.bigdata.rdf.sparql.ast.JoinGroupNode; import com.bigdata.rdf.sparql.ast.NamedSubqueriesNode; import com.bigdata.rdf.sparql.ast.OrderByExpr; import com.bigdata.rdf.sparql.ast.OrderByNode; import com.bigdata.rdf.sparql.ast.ProjectionNode; import com.bigdata.rdf.sparql.ast.QueryNodeWithBindingSet; import com.bigdata.rdf.sparql.ast.QueryRoot; import com.bigdata.rdf.sparql.ast.QueryType; import com.bigdata.rdf.sparql.ast.StatementPatternNode; import com.bigdata.rdf.sparql.ast.VarNode; import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext; import com.bigdata.rdf.sparql.ast.eval.ASTDeferredIVResolution; import java.util.Iterator; import java.util.List; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertSame; import static junit.framework.TestCase.assertTrue; import org.openrdf.query.MalformedQueryException; /** * Test suite for {@link ASTOrderByAggregateFlatteningOptimizer}. All test * queries in the suite contain ORDER BY with one or more aggregates, and the * tested optimizer simplifies them by introducing aliases for the aggregates. * * @see ASTOrderByAggregateFlatteningOptimizer * * @author <a href="mailto:ariazanov@blazegraph.com">Alexandre Riazanov</a> */ public class TestASTOrderByAggregateFlatteningOptimizer extends AbstractASTEvaluationTestCase { public TestASTOrderByAggregateFlatteningOptimizer() { super(); } public TestASTOrderByAggregateFlatteningOptimizer(final String name) { super(name); } public void test_orderByAggregateFlatteningOptimizer_simple_case_1() throws MalformedQueryException { // //SELECT ?o //WHERE { ?s :p ?o } //GROUP BY ?o //ORDER BY (count(?s)) final String queryStr = "" + // "PREFIX : <http://example/>\n" + // "SELECT ?o \n" + // "WHERE { ?s :p ?o } \n" + // "GROUP BY ?o \n" + // "ORDER BY (count(?s))" // ; final ASTContainer astContainer = new Bigdata2ASTSPARQLParser() .parseQuery2(queryStr, baseURI); ASTDeferredIVResolution.resolveQuery(store, astContainer); final AST2BOpContext context = new AST2BOpContext(astContainer, store); QueryRoot given = astContainer.getOriginalAST(); final IASTOptimizer rewriter = new ASTOrderByAggregateFlatteningOptimizer(); final QueryRoot actual = (QueryRoot) rewriter.optimize(context, new QueryNodeWithBindingSet(given, new IBindingSet[]{})). getQueryNode(); // Check that the rewriter has produced something like this: //QueryType: SELECT //SELECT VarNode(o) ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(s))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(s)] AS VarNode(d999c13a-6aea-4c0d-ae37-cf99a1a04d46) )[excludeFromProjection] // JoinGroupNode { // StatementPatternNode(VarNode(s), ConstantNode(TermId(0U)[http://example/p]), VarNode(o)) [scope=DEFAULT_CONTEXTS] // } //group by VarNode(o) //ORDER BY com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(d999c13a-6aea-4c0d-ae37-cf99a1a04d46))[ ascending=true] final NamedSubqueriesNode namedSubqueries = actual.getNamedSubqueries(); assertNull(namedSubqueries); assertSame(QueryType.SELECT, actual.getQueryType()); // Check // SELECT VarNode(o) ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(s))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(s)] AS VarNode(d999c13a-6aea-4c0d-ae37-cf99a1a04d46) )[excludeFromProjection] final ProjectionNode projection = actual.getProjection(); assertNotNull(projection); final IValueExpression[] assignments = projection.getValueExpressions(); assertEquals(2, assignments.length); // Check VarNode(o) ( assertNotNull(assignments[0]); assertTrue(assignments[0] instanceof Bind); final IVariable<IV> var1 = ((Bind) assignments[0]).getVar(); final IValueExpression<IV> expr1 = ((Bind) assignments[0]).getExpr(); assertSame(var1, expr1); assertEquals("o", var1.getName()); assertFalse(projection.excludeFromProjection(var1)); // Check ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(s))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(s)] AS VarNode(d999c13a-6aea-4c0d-ae37-cf99a1a04d46) )[excludeFromProjection] assertNotNull(assignments[1]); assertTrue(assignments[1] instanceof Bind); final IVariable<IV> var2 = ((Bind) assignments[1]).getVar(); assertFalse("s".equals(var2.getName())); assertFalse("o".equals(var2.getName())); assertTrue(projection.excludeFromProjection(var2)); // [excludeFromProjection] final IValueExpression<IV> expr2 = ((Bind) assignments[1]).getExpr(); assertTrue(expr2 instanceof COUNT); assertTrue(((COUNT) expr2).get(0) instanceof Var); assertEquals("s", ((Var) ((COUNT) expr2).get(0)).getName()); // Check // group by VarNode(o) final GroupByNode groupBy = actual.getGroupBy(); assertNotNull(groupBy); final IValueExpression[] groupByArgs = groupBy.getValueExpressions(); assertNotNull(groupByArgs); assertEquals(1, groupByArgs.length); assertNotNull(groupByArgs[0]); assertTrue(groupByArgs[0] instanceof Bind); assertEquals("o", ((Bind) groupByArgs[0]).getVar().getName()); // Check // ORDER BY com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(d999c13a-6aea-4c0d-ae37-cf99a1a04d46))[ ascending=true] final OrderByNode orderBy = actual.getOrderBy(); final Iterator<OrderByExpr> orderByArgs = orderBy.iterator(); assertTrue(orderByArgs.hasNext()); final OrderByExpr orderByArg = orderByArgs.next(); assertFalse(orderByArgs.hasNext()); assertTrue(orderByArg.getValueExpression() instanceof Var); assertEquals(var2.getName(), ((Var) orderByArg.getValueExpression()).getName()); // same as in SELECT assertTrue(orderByArg.isAscending()); // [ ascending=true] // Check // JoinGroupNode { // StatementPatternNode(VarNode(s), ConstantNode(TermId(0U)[http://example/p]), VarNode(o)) [scope=DEFAULT_CONTEXTS] // } final GraphPatternGroup whereClause = actual.getWhereClause(); assertNotNull(whereClause); assertTrue(whereClause instanceof JoinGroupNode); final List<StatementPatternNode> patterns = ((JoinGroupNode) whereClause).getStatementPatterns(); assertNotNull(patterns); assertEquals(1, patterns.size()); final StatementPatternNode pattern = patterns.get(0); assertTrue(pattern.s() instanceof VarNode); assertEquals("s", ((VarNode) pattern.s()).getValueExpression().getName()); assertTrue(pattern.p() instanceof ConstantNode); assertTrue(((ConstantNode) pattern.p()).getValueExpression() instanceof Constant); assertTrue(((Constant) ((ConstantNode) pattern.p()).getValueExpression()).get() instanceof TermId); assertEquals("http://example/p", ((TermId) ((Constant) ((ConstantNode) pattern.p()).getValueExpression()).get()).getValue().stringValue()); assertTrue(pattern.o() instanceof VarNode); assertEquals("o", ((VarNode) pattern.o()).getValueExpression().getName()); } // test_orderByAggregateFlatteningOptimizer_simple_case_1() /** * Covers DESC and ASC, mixing aggregates and plain variables in ORDER BY, * multiple aggregates in ORDER BY, aggregates before plain variables in * ORDER BY, multiple projection variables. */ public void test_orderByAggregateFlatteningOptimizer_high_coverage_case_1() throws MalformedQueryException { //PREFIX ex: <http://example.org/> //SELECT ?x ?y //WHERE //{ // ?x ex:r ?y . // ?y ex:q ?z //} //GROUP BY ?x ?y //ORDER BY DESC(max(?z)) ?x (count(?z)) DESC(?y) final String queryStr = "" + // "PREFIX ex: <http://example.org/>\n" + // "SELECT ?x ?y\n" + // "WHERE {\n" + // " ?x ex:r ?y .\n" + // " ?y ex:q ?z }\n" + // "GROUP BY ?x ?y\n" + // "ORDER BY DESC(max(?z)) ?x (count(?z)) DESC(?y)" // ; final ASTContainer astContainer = new Bigdata2ASTSPARQLParser() .parseQuery2(queryStr, baseURI); ASTDeferredIVResolution.resolveQuery(store, astContainer); final AST2BOpContext context = new AST2BOpContext(astContainer, store); QueryRoot given = astContainer.getOriginalAST(); final IASTOptimizer rewriter = new ASTOrderByAggregateFlatteningOptimizer(); final QueryRoot actual = (QueryRoot) rewriter.optimize(context, new QueryNodeWithBindingSet(given, new IBindingSet[]{})). getQueryNode(); // Check that the rewriter has produced something like this: //QueryType: SELECT //SELECT VarNode(x) VarNode(y) ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#max, valueExpr=com.bigdata.bop.rdf.aggregate.MAX(z)] AS VarNode(e6501b59-cd5b-4fbf-9587-1797b79464b2) )[excludeFromProjection] ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(z)] AS VarNode(086758ec-366a-4b33-86da-e958744d6650) )[excludeFromProjection] // JoinGroupNode { // StatementPatternNode(VarNode(x), ConstantNode(TermId(0U)[http://example.org/r]), VarNode(y)) [scope=DEFAULT_CONTEXTS] // StatementPatternNode(VarNode(y), ConstantNode(TermId(0U)[http://example.org/q]), VarNode(z)) [scope=DEFAULT_CONTEXTS] // } //group by VarNode(x) VarNode(y) //ORDER BY com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(e6501b59-cd5b-4fbf-9587-1797b79464b2))[ ascending=false] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(x))[ ascending=true] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(086758ec-366a-4b33-86da-e958744d6650))[ ascending=true] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(y))[ ascending=false] final NamedSubqueriesNode namedSubqueries = actual.getNamedSubqueries(); assertNull(namedSubqueries); assertSame(QueryType.SELECT, actual.getQueryType()); // Check // SELECT VarNode(x) VarNode(y) ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#max, valueExpr=com.bigdata.bop.rdf.aggregate.MAX(z)] AS VarNode(e6501b59-cd5b-4fbf-9587-1797b79464b2) )[excludeFromProjection] ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(z)] AS VarNode(086758ec-366a-4b33-86da-e958744d6650) )[excludeFromProjection] final ProjectionNode projection = actual.getProjection(); assertNotNull(projection); final IValueExpression[] assignments = projection.getValueExpressions(); assertEquals(4, assignments.length); // Check VarNode(x) ( assertNotNull(assignments[0]); assertTrue(assignments[0] instanceof Bind); final IVariable<IV> var1 = ((Bind) assignments[0]).getVar(); final IValueExpression<IV> expr1 = ((Bind) assignments[0]).getExpr(); assertSame(var1, expr1); assertEquals("x", var1.getName()); assertFalse(projection.excludeFromProjection(var1)); // Check VarNode(y) ( assertNotNull(assignments[1]); assertTrue(assignments[1] instanceof Bind); final IVariable<IV> var2 = ((Bind) assignments[1]).getVar(); final IValueExpression<IV> expr2 = ((Bind) assignments[1]).getExpr(); assertSame(var2, expr2); assertEquals("y", var2.getName()); assertFalse(projection.excludeFromProjection(var2)); // Check ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#max, valueExpr=com.bigdata.bop.rdf.aggregate.MAX(z)] AS VarNode(e6501b59-cd5b-4fbf-9587-1797b79464b2) )[excludeFromProjection] assertNotNull(assignments[2]); assertTrue(assignments[2] instanceof Bind); final IVariable<IV> var3 = ((Bind) assignments[2]).getVar(); assertFalse("x".equals(var3.getName())); assertFalse("y".equals(var3.getName())); assertFalse("z".equals(var3.getName())); assertTrue(projection.excludeFromProjection(var3)); // [excludeFromProjection] final IValueExpression<IV> expr3 = ((Bind) assignments[2]).getExpr(); assertTrue(expr3 instanceof MAX); assertTrue(((MAX) expr3).get(0) instanceof Var); assertEquals("z", ((Var) ((MAX) expr3).get(0)).getName()); // Check ( com.bigdata.rdf.sparql.ast.FunctionNode(VarNode(z))[ FunctionNode.scalarVals=null, FunctionNode.functionURI=http://www.w3.org/2006/sparql-functions#count, valueExpr=com.bigdata.bop.rdf.aggregate.COUNT(z)] AS VarNode(086758ec-366a-4b33-86da-e958744d6650) )[excludeFromProjection] assertNotNull(assignments[3]); assertTrue(assignments[3] instanceof Bind); final IVariable<IV> var4 = ((Bind) assignments[3]).getVar(); assertFalse("x".equals(var4.getName())); assertFalse("y".equals(var4.getName())); assertFalse("z".equals(var4.getName())); assertFalse(var3.getName().equals(var4.getName())); assertTrue(projection.excludeFromProjection(var4)); // [excludeFromProjection] final IValueExpression<IV> expr4 = ((Bind) assignments[3]).getExpr(); assertTrue(expr4 instanceof COUNT); assertTrue(((COUNT) expr4).get(0) instanceof Var); assertEquals("z", ((Var) ((COUNT) expr4).get(0)).getName()); // Check // group by VarNode(x) VarNode(y) final GroupByNode groupBy = actual.getGroupBy(); assertNotNull(groupBy); final IValueExpression[] groupByArgs = groupBy.getValueExpressions(); assertNotNull(groupByArgs); assertEquals(2, groupByArgs.length); assertNotNull(groupByArgs[0]); assertTrue(groupByArgs[0] instanceof Bind); assertEquals("x", ((Bind) groupByArgs[0]).getVar().getName()); assertNotNull(groupByArgs[1]); assertTrue(groupByArgs[1] instanceof Bind); assertEquals("y", ((Bind) groupByArgs[1]).getVar().getName()); // Check // ORDER BY com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(e6501b59-cd5b-4fbf-9587-1797b79464b2))[ ascending=false] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(x))[ ascending=true] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(086758ec-366a-4b33-86da-e958744d6650))[ ascending=true] com.bigdata.rdf.sparql.ast.OrderByExpr(VarNode(y))[ ascending=false] final OrderByNode orderBy = actual.getOrderBy(); final Iterator<OrderByExpr> orderByArgs = orderBy.iterator(); assertTrue(orderByArgs.hasNext()); final OrderByExpr orderByArg1 = orderByArgs.next(); assertTrue(orderByArg1.getValueExpression() instanceof Var); assertEquals(var3.getName(), ((Var) orderByArg1.getValueExpression()).getName()); assertFalse(orderByArg1.isAscending()); // [ ascending=false] assertTrue(orderByArgs.hasNext()); final OrderByExpr orderByArg2 = orderByArgs.next(); assertTrue(orderByArg2.getValueExpression() instanceof Var); assertEquals("x", ((Var) orderByArg2.getValueExpression()).getName()); assertTrue(orderByArg2.isAscending()); // [ ascending=true] assertTrue(orderByArgs.hasNext()); final OrderByExpr orderByArg3 = orderByArgs.next(); assertTrue(orderByArg3.getValueExpression() instanceof Var); assertEquals(var4.getName(), ((Var) orderByArg3.getValueExpression()).getName()); assertTrue(orderByArg3.isAscending()); // [ ascending=true] assertTrue(orderByArgs.hasNext()); final OrderByExpr orderByArg4 = orderByArgs.next(); assertTrue(orderByArg4.getValueExpression() instanceof Var); assertEquals("y", ((Var) orderByArg4.getValueExpression()).getName()); assertFalse(orderByArg4.isAscending()); // [ ascending=false] assertFalse(orderByArgs.hasNext()); // Check // JoinGroupNode { // StatementPatternNode(VarNode(x), ConstantNode(TermId(0U)[http://example.org/r]), VarNode(y)) [scope=DEFAULT_CONTEXTS] // StatementPatternNode(VarNode(y), ConstantNode(TermId(0U)[http://example.org/q]), VarNode(z)) [scope=DEFAULT_CONTEXTS] // } final GraphPatternGroup whereClause = actual.getWhereClause(); assertNotNull(whereClause); assertTrue(whereClause instanceof JoinGroupNode); final List<StatementPatternNode> patterns = ((JoinGroupNode) whereClause).getStatementPatterns(); assertNotNull(patterns); assertEquals(2, patterns.size()); // Check // StatementPatternNode(VarNode(x), ConstantNode(TermId(0U)[http://example.org/r]), VarNode(y)) [scope=DEFAULT_CONTEXTS] final StatementPatternNode pattern1 = patterns.get(0); assertTrue(pattern1.s() instanceof VarNode); assertEquals("x", ((VarNode) pattern1.s()).getValueExpression().getName()); assertTrue(pattern1.p() instanceof ConstantNode); assertTrue(((ConstantNode) pattern1.p()).getValueExpression() instanceof Constant); assertTrue(((Constant) ((ConstantNode) pattern1.p()).getValueExpression()).get() instanceof TermId); assertEquals("http://example.org/r", ((TermId) ((Constant) ((ConstantNode) pattern1.p()).getValueExpression()).get()).getValue().stringValue()); assertTrue(pattern1.o() instanceof VarNode); assertEquals("y", ((VarNode) pattern1.o()).getValueExpression().getName()); // Check // StatementPatternNode(VarNode(y), ConstantNode(TermId(0U)[http://example.org/q]), VarNode(z)) [scope=DEFAULT_CONTEXTS] final StatementPatternNode pattern2 = patterns.get(1); assertTrue(pattern2.s() instanceof VarNode); assertEquals("y", ((VarNode) pattern2.s()).getValueExpression().getName()); assertTrue(pattern2.p() instanceof ConstantNode); assertTrue(((ConstantNode) pattern2.p()).getValueExpression() instanceof Constant); assertTrue(((Constant) ((ConstantNode) pattern2.p()).getValueExpression()).get() instanceof TermId); assertEquals("http://example.org/q", ((TermId) ((Constant) ((ConstantNode) pattern2.p()).getValueExpression()).get()).getValue().stringValue()); assertTrue(pattern2.o() instanceof VarNode); assertEquals("z", ((VarNode) pattern2.o()).getValueExpression().getName()); } // test_orderByAggregateFlatteningOptimizer_high_coverage_case_1() } // class TestASTOrderByAggregateFlatteningOptimizer