![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.rdf.sparql.ast.JoinGroupNode Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
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
*/
package com.bigdata.rdf.sparql.ast;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import com.bigdata.bop.BOp;
import com.bigdata.bop.IVariable;
import com.bigdata.rdf.internal.constraints.InBOp;
import com.bigdata.rdf.sparql.ast.hints.OptimizerQueryHint;
import com.bigdata.rdf.sparql.ast.optimizers.ASTStaticJoinOptimizer;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;
/**
* An optional or non-optional collection of query nodes that run together in
* a group.
*/
public class JoinGroupNode extends GraphPatternGroup {
/**
*
*/
private static final long serialVersionUID = 1L;
interface Annotations extends GroupNodeBase.Annotations {
/**
* The graph variable or constant iff this {@link JoinGroupNode} models
* a GraphPatternGroup. When not present, your have to read up the
* parent chain to locate the dominating graph context.
*/
String CONTEXT = "context";
/**
* A property that may be set by a query hint. When set, the value is a
* {@link QueryOptimizerEnum}.
*
* @see QueryHints#OPTIMIZER
* @see OptimizerQueryHint
*/
String OPTIMIZER = QueryHints.OPTIMIZER;
/**
* An optional boolean that indicates whether this {@link JoinGroupNode}
* is the subgroup of an {@link ArbitraryLengthPathNode}. The default of
* this annotation is {@value #DEFAULT_IS_SUBGROUP_OF_ALP_NODE} (see:
* {@link #DEFAULT_IS_SUBGROUP_OF_ALP_NODE}).
*/
String IS_SUBGROUP_OF_ALP_NODE = "isSubgroupOfArbitraryLengthPathNode";
boolean DEFAULT_IS_SUBGROUP_OF_ALP_NODE = false;
}
/**
* Constructor required for {@link com.bigdata.bop.BOpUtility#deepCopy(FilterNode)}.
*
* Note: There is a nasty potential for a conflict here. This constructor is part
* of the deep copy semantics. The JoinGroupNode(IGroupMemberNode child)
* This means that the following code DOES NOT create a nested structure of a JoinGroupNode in a JoinGroupNode.
*
* new JoinGroupNode(new JoinGroupNode); // COPY CONSTRUCTOR PATTERN.
*
* Instead you MUST do this incrementally
*
* final JoinGroupNode parent = new JoinGroupNode();
* parent.addChild(new JoinGroupNode());
*
* and if you need to add things to the child, then the pattern becomes:
*
* final JoinGroupNode parent = new JoinGroupNode();
* final JoinGroupNode child1 = new JoinGroupNode()
* parent.addChild(child1);
* child1.addChild(...);
*
*/
public JoinGroupNode(final JoinGroupNode op) {
super(op);
}
/**
* Required shallow copy constructor.
*/
public JoinGroupNode(final BOp[] args, final Map anns) {
super(args, anns);
}
/**
* Construct a non-optional join group.
*/
public JoinGroupNode() {
super();
}
/**
* Construct a non-optional join group having the specified child as its
* initial member.
*/
public JoinGroupNode(final IGroupMemberNode child) {
super();
addChild(child);
}
public JoinGroupNode(final boolean optional) {
super();
setOptional(optional);
}
/**
* Construct a possibly optional group having the specified child as its
* initial member.
*
* @param optional
* true
iff the group is optional.
* @param child
* The initial child.
*/
public JoinGroupNode(final boolean optional, final IGroupMemberNode child) {
super();
setOptional(optional);
addChild(child);
}
/**
* Construct a GRAPH group having the specified child as its initial member.
*
* @param context
* The variable or constant for the GRAPH group.
* @param child
* The initial child.
*/
public JoinGroupNode(final TermNode context, final IGroupMemberNode child) {
super();
setContext(context);
addChild(child);
}
/**
* Set the context for a GroupGraphPattern.
*
* @param context
* The context (may be null
).
*/
public void setContext(final TermNode context) {
setProperty(Annotations.CONTEXT, context);
}
/**
* {@inheritDoc}
*
* Overridden to return the context associated with this
* {@link JoinGroupNode} if it is defined and otherwise read up the parent
* chain.
*/
@Override
public TermNode getContext() {
final TermNode context = (TermNode) getProperty(Annotations.CONTEXT);
if (context != null)
return context;
// Note: the base class will test the parent.
return super.getContext();
}
@Override
final public boolean isOptional() {
return getProperty(Annotations.OPTIONAL, Annotations.DEFAULT_OPTIONAL);
}
final public void setOptional(final boolean optional) {
setProperty(Annotations.OPTIONAL, optional);
}
@Override
final public boolean isMinus() {
return getProperty(Annotations.MINUS, Annotations.DEFAULT_MINUS);
}
public void setMinus(final boolean minus) {
setProperty(Annotations.MINUS, minus);
}
/**
* Returns the property {@link Annotations#IS_SUBGROUP_OF_ALP_NODE} of this
* {@link JoinGroupNode}.
*/
final public boolean isSubgroupOfALPNode() {
return getProperty(Annotations.IS_SUBGROUP_OF_ALP_NODE,
Annotations.DEFAULT_IS_SUBGROUP_OF_ALP_NODE);
}
final public void setSubgroupOfALPNode( final boolean isSubgroup ) {
setProperty(Annotations.IS_SUBGROUP_OF_ALP_NODE, isSubgroup);
}
/**
* Return the {@link QueryOptimizerEnum} that is in effect for this
* {@link JoinGroupNode}. This will be the value specified through
* {@link QueryHints#OPTIMIZER} and otherwise the default value given by
* {@link QueryHints#DEFAULT_OPTIMIZER}.
*
* @return The effective query optimizer for this join group.
*/
final public QueryOptimizerEnum getQueryOptimizer() {
return getProperty(Annotations.OPTIMIZER, QueryHints.DEFAULT_OPTIMIZER);
}
/**
* Return only the statement pattern child nodes in this group.
*/
public List getStatementPatterns() {
return getChildren(StatementPatternNode.class);
}
/**
* Return the #of statement patterns.
*/
public int getStatementPatternCount() {
int n = 0;
for (IQueryNode node : this) {
if (node instanceof StatementPatternNode) {
n++;
}
}
return n;
}
/**
* Return the #of required statement patterns (does not include those
* flagged as OPTIONAL).
*/
public int getRequiredStatementPatternCount() {
int n = 0;
for (IQueryNode node : this) {
if (node instanceof StatementPatternNode) {
if(!((StatementPatternNode)node).isOptional()) {
n++;
}
}
}
return n;
}
/**
* Return only the {@link ServiceNode} child nodes in this group.
*/
public List getServiceNodes() {
return getChildren(ServiceNode.class);
}
/**
* Return only the {@link NamedSubqueryInclude} child nodes in this group.
*/
public List getNamedSubqueryIncludes() {
return getChildren(NamedSubqueryInclude.class);
}
/**
* Return any LET x:= expr
or (expr AS ?x)
nodes
* in this group (these are modeled in exactly the same way by the
* AST {@link AssignmentNode}).
*
* Note: {@link AssignmentNode}s MUST NOT be reordered. They MUST be
* evaluated left-to-right in the order given in the original query.
*/
public List getAssignments(){
return getChildren(AssignmentNode.class);
}
/**
* Return only the filter child nodes in this group.
*/
public List getAllFiltersInGroup() {
return getChildren(FilterNode.class);
}
/**
* Return the set of IN filters for this group.
*
* FIXME We need to move away from the DataSetJoin class and replace it with
* an IPredicate to which we have attached an inline access path. That
* transformation needs to happen in a rewrite rule, which means that we
* will wind up removing the IN filter and replacing it with an AST node for
* that inline AP (something conceptually similar to a statement pattern but
* for a column projection of the variable for the IN expression). That way
* we do not have to magically "subtract" the known "IN" filters out of the
* join- and post- filters.
*
* @see https://sourceforge.net/apps/trac/bigdata/ticket/233 (Replace
* DataSetJoin with an "inline" access path.)
*/
public List getInFilters() {
final List filters = new LinkedList();
for (IQueryNode node : this) {
if (!(node instanceof FilterNode))
continue;
/*
* FIXME The "data set join" hack can be enabled by making this
* [true]. I have it disabled for the moment since I am in the
* middle of changing all of this static analysis stuff.
*
* Note: You will also have to modify the join- and post- filter
* methods in the StaticAnalysis class for this to work. They need
* to subtract out the IN filters if they have already been applied.
*/
if (false) {
final FilterNode filter = (FilterNode) node;
if (filter.getValueExpression() instanceof InBOp) {
if (((InBOp) filter.getValueExpression())
.getValueExpression() instanceof IVariable) {
filters.add(filter);
}
}
}
}
return filters;
}
public List getReorderableChildren() {
final List nodes = getChildren(IReorderableNode.class);
final Iterator it = nodes.iterator();
while (it.hasNext()) {
final IReorderableNode node = it.next();
if (ASTStaticJoinOptimizer.log.isDebugEnabled()) {
ASTStaticJoinOptimizer.log.debug(node);
ASTStaticJoinOptimizer.log.debug(node.isReorderable());
}
if (!node.isReorderable()) {
it.remove();
}
}
return nodes;
}
/*
* Note: I took this out and put a simpler version of toString(indent) into
* the base class. The multiple passes over the AST when rendering it into
* a String were hiding order differences within the group node which were
* making it difficult to identify test failures. The filters also make it
* likely that we were failing to show some children of the AST because they
* did not match any of the tested interfaces.
*
* BBT 8/30/2011
*/
// public String toString(final int indent) {
// ....
// }
@Override
public Set> getRequiredBound(StaticAnalysis sa) {
final Set> requiredBound = new HashSet>();
for (IGroupMemberNode child : getChildren()) {
requiredBound.addAll(child.getRequiredBound(sa));
}
return requiredBound;
}
@Override
public Set> getDesiredBound(StaticAnalysis sa) {
final Set> desiredBound = new HashSet>();
for (IGroupMemberNode child : getChildren()) {
desiredBound.addAll(child.getDesiredBound(sa));
}
return desiredBound;
}
}