/**
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 8, 2011
*/
package com.bigdata.rdf.sparql.ast.optimizers;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import com.bigdata.bop.BOp;
import com.bigdata.bop.BOpUtility;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.Var;
import com.bigdata.rdf.sparql.ast.BindingsClause;
import com.bigdata.rdf.sparql.ast.GraphPatternGroup;
import com.bigdata.rdf.sparql.ast.GroupNodeBase;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.IQueryNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueriesNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueryInclude;
import com.bigdata.rdf.sparql.ast.NamedSubqueryRoot;
import com.bigdata.rdf.sparql.ast.ProjectionNode;
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.VarNode;
import com.bigdata.rdf.sparql.ast.eval.AST2BOpContext;
import cutthecrap.utils.striterators.Striterator;
/**
* Rewrites any {@link ProjectionNode} with a wild card into the set of
* variables visible to the {@link QueryBase} having that projection. This is
* done first for the {@link NamedSubqueriesNode} and then depth-first for the
* WHERE clause. Only variables projected by a subquery will be projected by the
* parent query.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class ASTWildcardProjectionOptimizer 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);
// collect named subquery includes that have been resolved already
final Set<String> resolvedNSIs = new HashSet<String>();
/*
* NAMED SUBQUERIES
*
* Rewrite the named subquery projections before the where clause.
*/
if (queryRoot.getNamedSubqueries() != null) {
for (NamedSubqueryRoot subqueryRoot : queryRoot
.getNamedSubqueries()) {
@SuppressWarnings("unchecked")
final Iterator<QueryBase> itr = (Iterator<QueryBase>) new Striterator(
BOpUtility.postOrderIteratorWithAnnotations((BOp) subqueryRoot
.getWhereClause())).addTypeFilter(QueryBase.class);
while (itr.hasNext()) {
final QueryBase queryBase = itr.next();
rewriteProjection(sa, queryBase, null, resolvedNSIs);
}
rewriteProjection(sa, subqueryRoot, null, resolvedNSIs);
}
}
/*
* WHERE CLAUSE
*
* Bottom up visitation so we can get rewrite the projections of
* subqueries before we rewrite the projections of the parent query.
*
* @see <a href="http://trac.bigdata.com/ticket/757" > Wildcard projection
* was not rewritten. </a>
*/
if (queryRoot.getWhereClause() != null) {
@SuppressWarnings("unchecked")
final Iterator<QueryBase> itr = (Iterator<QueryBase>) new Striterator(
BOpUtility.postOrderIteratorWithAnnotations((BOp) queryRoot
.getWhereClause())).addTypeFilter(QueryBase.class);
while (itr.hasNext()) {
final QueryBase queryBase = itr.next();
rewriteProjection(sa, queryBase, null, resolvedNSIs);
}
}
// Rewrite the projection on the QueryRoot last.
rewriteProjection(
sa, queryRoot, context.getSolutionSetStats().getUsedVars(), resolvedNSIs);
return new QueryNodeWithBindingSet(queryRoot, bindingSets);
}
/**
* Rewrite the projection for the {@link QueryBase}.
*
* @param sa
* {@link StaticAnalysis} helper.
* @param queryBase
* The {@link QueryBase} whose {@link ProjectionNode} will be
* rewritten.
*/
private void rewriteProjection(final StaticAnalysis sa,
final QueryBase queryBase, Set<IVariable<?>> exogeneousVars,
final Set<String> resolvedNSIs /* to break infinite loops */) {
/**
* BLZG-1763: recurse into named subquery includes.
*/
if (queryBase instanceof NamedSubqueryRoot) {
final NamedSubqueryRoot queryBaseAsNsr = (NamedSubqueryRoot)queryBase;
final GraphPatternGroup<?> gpg = queryBaseAsNsr.getGraphPattern();
@SuppressWarnings("unchecked")
final Iterator<NamedSubqueryInclude> itr =
(Iterator<NamedSubqueryInclude>) new Striterator(
BOpUtility.postOrderIteratorWithAnnotations((BOp) gpg)).
addTypeFilter(NamedSubqueryInclude.class);
while (itr.hasNext()) {
final NamedSubqueryInclude nsi = itr.next();
final String name = nsi.getName();
final NamedSubqueryRoot nsr = sa.getNamedSubqueryRoot(name);
if (!resolvedNSIs.contains(name)) { // otherwise: already rewritten
resolvedNSIs.add(name); // -> do not process again, will be resolved
rewriteProjection(sa, nsr, exogeneousVars, resolvedNSIs);
}
}
}
final ProjectionNode projection = queryBase.getProjection();
if (projection != null && projection.isWildcard()) {
final GroupNodeBase<IGroupMemberNode> whereClause =
(GroupNodeBase<IGroupMemberNode>) queryBase.getWhereClause();
final ProjectionNode p2 = new ProjectionNode();
queryBase.setProjection(p2);
if(projection.isDistinct())
p2.setDistinct(true);
if(projection.isReduced())
p2.setReduced(true);
final Set<IVariable<?>> varSet = sa.getSpannedVariables(
whereClause, new LinkedHashSet<IVariable<?>>());
if (exogeneousVars!=null) {
varSet.addAll(exogeneousVars);
}
if (queryBase.getBindingsClause()!=null) {
final BindingsClause bc = queryBase.getBindingsClause();
varSet.addAll(bc.getDeclaredVariables());
}
for(IVariable<?> var : varSet) {
if (!((Var) var).isAnonymous())
p2.addProjectionVar(new VarNode(var.getName()));
}
}
}
}