/**
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 June 20, 2015
*/
package com.bigdata.rdf.sparql.ast.optimizers;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.bigdata.bop.IVariable;
import com.bigdata.rdf.sparql.ast.GroupNodeVarBindingInfoMap;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.StaticAnalysis;
/**
* Class representing an ordered list of {@link ASTJoinGroupPartition}s.
*
* @author <a href="mailto:ms@metaphacts.com">Michael Schmidt</a>
* @version $Id$
*/
public class ASTJoinGroupPartitions {
/**
* The ordered list (main data structure).
*/
List<ASTJoinGroupPartition> partitions;
/**
* Constructor, creating partitions for a list of {@link IGroupMemberNode}
* objects along OPTIONAL and MINUS nodes. The idea of this partitioning
* is that we are not freely allowed to reorder nodes across partitions.
*
* @param nodes the list of nodes to partition
* @param bindingInfo the bindingInfo for these nodes
* @param externallyKnownProduced variables that are known to be bound when
* starting processing the join group defined by the node list
*/
public ASTJoinGroupPartitions(
final List<IGroupMemberNode> nodes,
final GroupNodeVarBindingInfoMap bindingInfo,
final Set<IVariable<?>> externallyKnownProduced) {
partitions = new ArrayList<ASTJoinGroupPartition>();
final Set<IVariable<?>> tmpKnownProduced =
new HashSet<IVariable<?>>(externallyKnownProduced);
final List<IGroupMemberNode> tmpNonOptionalOrMinusNodes =
new ArrayList<IGroupMemberNode>();
IGroupMemberNode tmpOptionalOrMinus = null;
for (int i = 0; i < nodes.size(); i++) {
final IGroupMemberNode node = nodes.get(i);
final Boolean isOptionalOrMinus =
StaticAnalysis.isMinusOrOptional(node);
/**
* Either add the node to the non-optional non-minus node list or
* assign it to the temporary tmpOptionalOrMinus variable.
*/
if (!isOptionalOrMinus) {
tmpNonOptionalOrMinusNodes.add(node);
} else {
tmpOptionalOrMinus = node;
}
/**
* If the tmpOptionalOrMinus node has been set or we reached the end,
* we build and record the partition and reset the temporary variables.
*/
if (tmpOptionalOrMinus != null || i + 1 == nodes.size()) {
// create partition
final ASTJoinGroupPartition partition = new ASTJoinGroupPartition(
new LinkedList<IGroupMemberNode>(tmpNonOptionalOrMinusNodes),
tmpOptionalOrMinus /* may be null */, bindingInfo,
new HashSet<IVariable<?>>(tmpKnownProduced));
// record partition
partitions.add(partition);
// re-initialize the tmp arrays for future iterations
tmpKnownProduced.addAll(partition.definitelyProduced);
tmpNonOptionalOrMinusNodes.clear();
tmpOptionalOrMinus = null;
}
}
// special handling: there's no node -> create a dummy partition
if (partitions.isEmpty()) {
final ASTJoinGroupPartition partition = new ASTJoinGroupPartition(
new LinkedList<IGroupMemberNode>(tmpNonOptionalOrMinusNodes),
null, bindingInfo, new HashSet<IVariable<?>>(tmpKnownProduced));
partitions.add(partition);
}
}
/**
* Return the inner list of partitions.
*/
public List<ASTJoinGroupPartition> getPartitionList() {
return partitions;
}
/**
* Extracts all nodes in all partitions, in order.
*/
public LinkedList<IGroupMemberNode> extractNodeList(
final boolean includeOptionalOrMinusNode) {
LinkedList<IGroupMemberNode> res = new LinkedList<IGroupMemberNode>();
for (int i=0; i<partitions.size(); i++) {
res.addAll(partitions.get(i).extractNodeList(includeOptionalOrMinusNode));
}
return res;
}
/**
* Places the node at the first possible position across all partitions.
*
* @param node
*/
public void placeAtFirstPossiblePosition(IGroupMemberNode node) {
final Set<IVariable<?>> knownBoundFromPrevPartitions =
new HashSet<IVariable<?>>();
for (int i=0; i<partitions.size(); i++) {
final ASTJoinGroupPartition partition = partitions.get(i);
/**
* Try to place the node in the partition. As long as we do not reach
* the last partition (i<partitions.size()-1), we must require that
* all required variables of the node are bound, i.e. the "right"
* position might be in a later partition.
*/
final Integer position =
partition.getFirstPossiblePosition(
node, knownBoundFromPrevPartitions,
i<partitions.size()-1 /* requireAllBound */);
// if position found:
if (position!=null) {
partition.placeAtPosition(node,position);
return;
// if reached the end:
} else if (i+1==partitions.size()) {
if (partition.optionalOrMinus==null) {
// in this case it is safe to place the node at the end
partition.placeAtPosition(node,null);
} else {
// append a dummy partition containing the node
final LinkedList<IGroupMemberNode> listWithNode =
new LinkedList<IGroupMemberNode>();
listWithNode.add(node);
final ASTJoinGroupPartition dummyPartition =
new ASTJoinGroupPartition(listWithNode, null,
partition.bindingInfoMap, partition.externallyBound);
partitions.add(dummyPartition);
}
return;
}
knownBoundFromPrevPartitions.addAll(partition.getDefinitelyProduced());
}
// if the node has not been succesfully placed, at it into a new
// partition at the end
}
}