/**
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);
}
}
}
}