/** 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 */ /* * Created on Sep 10, 2011 */ package com.bigdata.rdf.sparql.ast.optimizers; import java.util.Collection; import java.util.LinkedList; import com.bigdata.bop.BOp; import com.bigdata.bop.BOpUtility; import com.bigdata.bop.IBindingSet; import com.bigdata.rdf.sparql.ast.FilterNode; import com.bigdata.rdf.sparql.ast.GraphPatternGroup; import com.bigdata.rdf.sparql.ast.IGroupMemberNode; import com.bigdata.rdf.sparql.ast.IQueryNode; import com.bigdata.rdf.sparql.ast.JoinGroupNode; import com.bigdata.rdf.sparql.ast.NamedSubqueriesNode; import com.bigdata.rdf.sparql.ast.NamedSubqueryRoot; import com.bigdata.rdf.sparql.ast.QueryBase; import com.bigdata.rdf.sparql.ast.QueryNodeWithBindingSet; import com.bigdata.rdf.sparql.ast.QueryRoot; import com.bigdata.rdf.sparql.ast.StaticAnalysis; import com.bigdata.rdf.sparql.ast.UnionNode; import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext; import com.bigdata.rdf.sparql.ast.eval.IEvaluationContext; public class ASTUnionFiltersOptimizer implements IASTOptimizer { @Override public QueryNodeWithBindingSet optimize( final AST2BOpContext context, final QueryNodeWithBindingSet input) { final IQueryNode queryNode = input.getQueryNode(); final IBindingSet[] bindingSets = input.getBindingSets(); if (!(queryNode instanceof QueryRoot)) return new QueryNodeWithBindingSet(queryNode, bindingSets); final QueryRoot queryRoot = (QueryRoot) queryNode; final StaticAnalysis sa = new StaticAnalysis(queryRoot, context); // Main WHERE clause { @SuppressWarnings("unchecked") final GraphPatternGroup<IGroupMemberNode> whereClause = (GraphPatternGroup<IGroupMemberNode>) queryRoot.getWhereClause(); if (whereClause != null) { optimize(context, sa, whereClause); } } // Named subqueries if (queryRoot.getNamedSubqueries() != null) { final NamedSubqueriesNode namedSubqueries = queryRoot .getNamedSubqueries(); /* * Note: This loop uses the current size() and get(i) to avoid * problems with concurrent modification during visitation. */ for (NamedSubqueryRoot namedSubquery : namedSubqueries) { @SuppressWarnings("unchecked") final GraphPatternGroup<IGroupMemberNode> whereClause = (GraphPatternGroup<IGroupMemberNode>) namedSubquery.getWhereClause(); if (whereClause != null) { optimize(context, sa, whereClause); } } } // log.error("\nafter rewrite:\n" + queryNode); return new QueryNodeWithBindingSet(queryNode, bindingSets); } /** * Look for a join group that has only one union and some filters. Lift * the filters into all children of the union and remove the filters from * the group. */ private void optimize(final IEvaluationContext ctx, final StaticAnalysis sa, final GraphPatternGroup<?> op) { if (op instanceof JoinGroupNode) { final JoinGroupNode joinGroup = (JoinGroupNode) op; UnionNode union = null; Collection<FilterNode> filters = null; boolean canOptimize = false; for (IGroupMemberNode child : joinGroup) { if (child instanceof UnionNode) { // more than one union if (union != null) { canOptimize = false; break; } else { union = (UnionNode) child; canOptimize = true; } } else if (child instanceof FilterNode) { if (filters == null) { filters = new LinkedList<FilterNode>(); } filters.add((FilterNode) child); } else { // something else in the group other than a union and filters canOptimize = false; break; } } if (canOptimize && filters != null) { for (JoinGroupNode child : union) { for (FilterNode filter : filters) { child.addChild(BOpUtility.deepCopy(filter)); } } for (FilterNode filter : filters) { joinGroup.removeChild(filter); } } } /* * Recursion, but only into group nodes (including within subqueries). */ for (int i = 0; i < op.arity(); i++) { final BOp child = op.get(i); if (child instanceof GraphPatternGroup<?>) { @SuppressWarnings("unchecked") final GraphPatternGroup<IGroupMemberNode> childGroup = (GraphPatternGroup<IGroupMemberNode>) child; optimize(ctx, sa, childGroup); } else if (child instanceof QueryBase) { final QueryBase subquery = (QueryBase) child; @SuppressWarnings("unchecked") final GraphPatternGroup<IGroupMemberNode> childGroup = (GraphPatternGroup<IGroupMemberNode>) subquery .getWhereClause(); optimize(ctx, sa, childGroup); } } } }