![JAR search and dependency download from the Maven repository](/logo.png)
com.bigdata.rdf.sail.sparql.BigdataExprBuilder 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
*/
/* Portions of this code are:
*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
*
* Licensed under the Aduna BSD-style license.
*/
/*
* Created on Aug 20, 2011
*/
package com.bigdata.rdf.sail.sparql;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.openrdf.query.algebra.StatementPattern.Scope;
import com.bigdata.bop.BOp;
import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IConstraint;
import com.bigdata.bop.IValueExpression;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.bindingSet.ListBindingSet;
import com.bigdata.bop.solutions.GroupByState;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.sail.sparql.ast.ASTAskQuery;
import com.bigdata.rdf.sail.sparql.ast.ASTBaseDecl;
import com.bigdata.rdf.sail.sparql.ast.ASTBindingSet;
import com.bigdata.rdf.sail.sparql.ast.ASTBindingValue;
import com.bigdata.rdf.sail.sparql.ast.ASTBindingsClause;
import com.bigdata.rdf.sail.sparql.ast.ASTConstruct;
import com.bigdata.rdf.sail.sparql.ast.ASTConstructQuery;
import com.bigdata.rdf.sail.sparql.ast.ASTDescribe;
import com.bigdata.rdf.sail.sparql.ast.ASTDescribeQuery;
import com.bigdata.rdf.sail.sparql.ast.ASTGraphGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTGraphPatternGroup;
import com.bigdata.rdf.sail.sparql.ast.ASTGroupClause;
import com.bigdata.rdf.sail.sparql.ast.ASTHavingClause;
import com.bigdata.rdf.sail.sparql.ast.ASTLimit;
import com.bigdata.rdf.sail.sparql.ast.ASTNamedSubquery;
import com.bigdata.rdf.sail.sparql.ast.ASTOffset;
import com.bigdata.rdf.sail.sparql.ast.ASTOrderClause;
import com.bigdata.rdf.sail.sparql.ast.ASTOrderCondition;
import com.bigdata.rdf.sail.sparql.ast.ASTPrefixDecl;
import com.bigdata.rdf.sail.sparql.ast.ASTProjectionElem;
import com.bigdata.rdf.sail.sparql.ast.ASTQuery;
import com.bigdata.rdf.sail.sparql.ast.ASTQueryContainer;
import com.bigdata.rdf.sail.sparql.ast.ASTSelect;
import com.bigdata.rdf.sail.sparql.ast.ASTSelectQuery;
import com.bigdata.rdf.sail.sparql.ast.ASTVar;
import com.bigdata.rdf.sail.sparql.ast.ASTWhereClause;
import com.bigdata.rdf.sail.sparql.ast.Node;
import com.bigdata.rdf.sail.sparql.ast.SimpleNode;
import com.bigdata.rdf.sail.sparql.ast.VisitorException;
import com.bigdata.rdf.sparql.ast.AssignmentNode;
import com.bigdata.rdf.sparql.ast.BindingsClause;
import com.bigdata.rdf.sparql.ast.ConstantNode;
import com.bigdata.rdf.sparql.ast.ConstructNode;
import com.bigdata.rdf.sparql.ast.GraphPatternGroup;
import com.bigdata.rdf.sparql.ast.GroupByNode;
import com.bigdata.rdf.sparql.ast.HavingNode;
import com.bigdata.rdf.sparql.ast.IGroupMemberNode;
import com.bigdata.rdf.sparql.ast.IProjectionDecl;
import com.bigdata.rdf.sparql.ast.IValueExpressionNode;
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.OrderByExpr;
import com.bigdata.rdf.sparql.ast.OrderByNode;
import com.bigdata.rdf.sparql.ast.ProjectionNode;
import com.bigdata.rdf.sparql.ast.QueryBase;
import com.bigdata.rdf.sparql.ast.QueryRoot;
import com.bigdata.rdf.sparql.ast.QueryType;
import com.bigdata.rdf.sparql.ast.SliceNode;
import com.bigdata.rdf.sparql.ast.StatementPatternNode;
import com.bigdata.rdf.sparql.ast.StaticAnalysis;
import com.bigdata.rdf.sparql.ast.SubqueryRoot;
import com.bigdata.rdf.sparql.ast.TermNode;
import com.bigdata.rdf.sparql.ast.ValueExpressionNode;
import com.bigdata.rdf.sparql.ast.VarNode;
import com.bigdata.rdf.sparql.ast.optimizers.IASTOptimizer;
/**
* Top-level expression builder for SPARQL.
*
* @author Bryan Thompson
* @version $Id$
* @openrdf
*/
public class BigdataExprBuilder extends GroupGraphPatternBuilder {
private static final Logger log = Logger.getLogger(BigdataExprBuilder.class);
public BigdataExprBuilder(final BigdataASTContext context) {
super(context);
}
/**
* This is the top-level entry point for a SPARQL query.
*
* Note: {@link ASTBaseDecl} and {@link ASTPrefixDecl}s are available from
* the {@link ASTQueryContainer} node. They are being ignored here as they
* should have been processed before running the {@link BigdataExprBuilder}.
*
* Note: {@link ASTQuery} is an abstract type. The concrete classes are:
* {@link ASTAskQuery}, {@link ASTConstructQuery}, {@link ASTDescribeQuery},
* and {@link ASTSelectQuery}. This method will wind up delegating to the
* visitor method for the appropriate concrete {@link ASTQuery} instance.
*/
@Override
public QueryRoot visit(final ASTQueryContainer node, final Object data)
throws VisitorException {
if (log.isInfoEnabled())
log.info("\n" + node.dump(">"));
final QueryRoot query = (QueryRoot) node.getQuery().jjtAccept(this, null);
return query;
}
//
// ASTQuery visitor methods for SELECT, ASK, DESCRIBE and CONSTRUCT.
//
/**
* This is the entry point for both a top-level SELECT and a SubSelect.
*
* @return The method returns either a {@link QueryRoot} or a
* {@link SubqueryRoot} depending on whether or not the
* {@link ASTSelectQuery} appears as a top-level query or a
* subquery.
*/
@Override
public QueryBase visit(final ASTSelectQuery astQuery, Object data)
throws VisitorException {
final QueryBase queryRoot = getQueryBase(astQuery, data,
QueryType.SELECT);
// Accept optional NamedSubquery(s).
handleNamedSubqueryClause(astQuery, queryRoot);
handleSelect(astQuery.getSelect(), queryRoot);
handleWhereClause(astQuery, queryRoot);
handleGroupBy(astQuery, queryRoot);
handleHaving(astQuery, queryRoot);
handleOrderBy(astQuery, queryRoot);
handleSlice(astQuery, queryRoot);
handleBindings(astQuery, queryRoot);
// verifyAggregate(queryRoot);
return queryRoot;
}
/**
* ASK query.
*/
@Override
public QueryBase visit(final ASTAskQuery node, Object data)
throws VisitorException {
final QueryBase queryRoot = getQueryBase(node, data, QueryType.ASK);
// Accept optional NamedSubquery(s).
handleNamedSubqueryClause(node, queryRoot);
/*
* Note: Nothing is projected.
*/
handleWhereClause(node, queryRoot);
/*
* Note: GROUP BY and HAVING are not currently permitted by the SPARQL
* 1.1 grammar. This means that you can not place a constraint on
* aggregates in an ASK query.
*/
// handleGroupBy(node, queryRoot);
//
// handleHaving(node, queryRoot);
/*
* Note: At most one solution.
*/
final SliceNode slice = new SliceNode(0L/* offset */, 1L/* limit */);
queryRoot.setSlice(slice);
handleBindings(node, queryRoot);
return queryRoot;
}
/**
* DESCRIBE query.
*
* Note: The openrdf parser has a "Describe" production which is not in the
* SPARQL 1.1 grammar (it is an equivalent grammar in that it accepts the
* same inputs). This means that the "projection" part of the DESCRIBE query
* is visited on the {@link ASTDescribe} node.
*/
@Override
public QueryBase visit(final ASTDescribeQuery node, Object data)
throws VisitorException {
final QueryBase queryRoot = getQueryBase(node, data, QueryType.DESCRIBE);
// Accept optional NamedSubquery(s).
handleNamedSubqueryClause(node, queryRoot);
// Process describe clause
node.getDescribe().jjtAccept(this, queryRoot);
handleWhereClause(node, queryRoot);
handleGroupBy(node, queryRoot);
handleHaving(node, queryRoot);
handleOrderBy(node, queryRoot);
handleSlice(node, queryRoot);
handleBindings(node, queryRoot);
// verifyAggregate(queryRoot);
return queryRoot;
}
/**
* This is the "projection" part of the DESCRIBE query. This code marks the
* query as a "DESCRIBE" and generates and attaches a {@link ProjectionNode}
* to the {@link QueryBase}. The {@link ProjectionNode} uses an assignment
* node to bind an anonymous variable for a IRI. Variables are projected
* normally. *
is projected as the variable named "*".
*
* Note: This {@link ProjectionNode} models the "DESCRIBE" but does not
* provide a query plan. The {@link QueryBase} MUST be further rewritten in
* order to supply an appropriate query plan. This should be done using the
* appropriate {@link IASTOptimizer}.
*
* @param data
* The {@link QueryBase}.
*/
@Override
public Void visit(final ASTDescribe node, Object data)
throws VisitorException {
final QueryBase queryRoot = (QueryBase) data;
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
if (node.isWildcard()) {
projection.addProjectionVar(new VarNode("*"));
} else {
final int nchildren = node.jjtGetNumChildren();
for (int i = 0; i < nchildren; i++) {
/*
* Note: Delegates to the ValueExprBuilder. Can visit VarNode or
* ConstantNode(IV).
*/
final TermNode resource = (TermNode) node.jjtGetChild(i)
.jjtAccept(this, null);
if (resource instanceof VarNode) {
projection.addProjectionVar((VarNode) resource);
} else {
final VarNode anonVar = context.createAnonVar("-iri-");
projection.addExpr(new AssignmentNode(anonVar, resource));
}
}
}
return null;
}
/**
* Handle a CONSTRUCT query.
*
* This builds a {@link ConstructNode} which is a model of the statement
* patterns in the construct template and attaches the where clause and
* solution modifiers. However, this is NOT a complete description of the
* semantics of the query. An {@link IASTOptimizer} will intercept the
* CONSTRUCT query and rewrite it before it is executed in order to provide
* an appropriate {@link ProjectionNode}.
*/
@Override
public QueryBase visit(final ASTConstructQuery node, final Object data)
throws VisitorException {
final QueryBase queryRoot = getQueryBase(node, data,
QueryType.CONSTRUCT);
// Accept optional NamedSubquery(s).
handleNamedSubqueryClause(node, queryRoot);
/*
* Process construct clause.
*
* Note: The children of the ASTConstruct is a TriplesBlock in
* sparql.jjt. This delegates to the GroupGraphPatternBuilder, which
* handles the TriplesBlock.
*/
final ASTConstruct constructNode = node.getConstruct();
final ConstructNode tmp = (ConstructNode) constructNode.jjtAccept(
this, null/* data */);
// Accept the WHERE clause.
handleWhereClause(node, queryRoot);
if (tmp.isEmpty()) {
/*
* This is the CONSTRUCT WHERE shortcut. We create the CONSTRUCT
* node now from the WHERE clause. The WHERE clause must consist of
* only simple statement patterns (no graphs, no filters, no
* sub-groups, sub-selects, etc).
*/
final JoinGroupNode whereClause = (JoinGroupNode) queryRoot
.getWhereClause();
for (IGroupMemberNode child : whereClause) {
if (!(child instanceof StatementPatternNode)) {
throw new VisitorException(
"CONSTRUCT WHERE only permits statement patterns in the WHERE clause.");
}
/*
* Add a copy of each statement pattern into the CONSTRUCT node.
*/
final StatementPatternNode sp = (StatementPatternNode) ((StatementPatternNode) child)
.clone();
tmp.addChild(sp);
}
}
// Set the construct template.
queryRoot.setConstruct(tmp);
handleGroupBy(node, queryRoot);
handleHaving(node, queryRoot);
handleOrderBy(node, queryRoot);
handleSlice(node, queryRoot);
handleBindings(node, queryRoot);
// verifyAggregate(queryRoot);
return queryRoot;
}
//
// Grammar constructions below the ASTQuery node.
//
@Override
final public GroupByNode visit(final ASTGroupClause node, Object data)
throws VisitorException {
final GroupByNode groupBy = new GroupByNode();
final int childCount = node.jjtGetNumChildren();
for (int i = 0; i < childCount; i++) {
/*
* Delegate to the value expression builder.
*/
final AssignmentNode expr = (AssignmentNode) node.jjtGetChild(i)
.jjtAccept(this, null/* data */);
groupBy.addExpr((AssignmentNode) expr);
}
return groupBy;
}
@Override
final public List visit(final ASTOrderClause node, Object data)
throws VisitorException {
final int childCount = node.jjtGetNumChildren();
final List elements = new ArrayList(
childCount);
for (int i = 0; i < childCount; i++) {
/*
* Note: OrderByExpr will delegate to the ValueExprBuilder.
*/
elements.add((OrderByExpr) node.jjtGetChild(i)
.jjtAccept(this, null));
}
return elements;
}
/**
* Note: Delegate to the {@link ValueExprBuilder}.
*/
@Override
final public OrderByExpr visit(final ASTOrderCondition node, Object data)
throws VisitorException
{
final ValueExpressionNode valueExpr = (ValueExpressionNode) node
.jjtGetChild(0).jjtAccept(this, null);
return new OrderByExpr(valueExpr, node.isAscending());
}
@Override
final public Long visit(ASTLimit node, Object data) throws VisitorException {
return node.getValue();
}
@Override
final public Long visit(ASTOffset node, Object data) throws VisitorException {
return node.getValue();
}
//
// private helper methods.
//
/**
* Handle the optional WITH SelectQuery AS NAME clause(s). These are a
* SPARQL 1.1 extension for named temporary solution sets.
*
* Note: This delegates the translation to a helper visitor. A SubSelect
* will wind up delegated back to an instance of this visitor.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
private void handleNamedSubqueryClause(final ASTQuery astQuery,
final QueryBase queryRoot) throws VisitorException {
{
// Check for any instances of this child.
final ASTNamedSubquery aNamedSubquery = (ASTNamedSubquery) astQuery
.jjtGetChild(ASTNamedSubquery.class);
if (aNamedSubquery == null) {
// No instances.
return;
}
}
if (!(queryRoot instanceof QueryRoot))
throw new VisitorException(
"WITH SubSelect AS %namedSet only allowed for top-level query.");
/*
* Setup the AST node for the named subqueries.
*/
final NamedSubqueriesNode namedSubqueries = new NamedSubqueriesNode();
((QueryRoot) queryRoot).setNamedSubqueries(namedSubqueries);
final int nchildren = astQuery.jjtGetNumChildren();
for (int i = 0; i < nchildren; i++) {
final Node aChild = astQuery.jjtGetChild(i);
if (!(aChild instanceof ASTNamedSubquery))
continue;
// Found a named subquery.
final ASTNamedSubquery aNamedSubquery = (ASTNamedSubquery) aChild;
/*
* Note: This visitor will wind up accepting and returning a
* SubqueryRoot rather than a NamedSubqueryRoot, so we have to copy
* the data into an appropriate object.
*/
// Accept the Subquery
final SubqueryRoot subquery = (SubqueryRoot) aNamedSubquery
.jjtAccept(this, queryRoot/* data */);
// Create instance of the right class.
final NamedSubqueryRoot namedSubquery = new NamedSubqueryRoot(
subquery.getQueryType(), aNamedSubquery.getName());
// Add to the container.
namedSubqueries.add(namedSubquery);
// Copy all args.
for (BOp arg : subquery.args()) {
namedSubquery.addArg(arg);
}
// Copy all annotations.
namedSubquery.copyAll(subquery.annotations());
}
}
/**
* Return the appropriate {@link QueryBase} instance. If the parent of the
* {@link ASTQuery} is an {@link ASTQueryContainer} then this will return
* {@link QueryRoot}. Otherwise it will return {@link SubqueryRoot}.
*
* @param node
* The {@link ASTQuery} node.
* @param data
* The data. This can be bound to some things other than a
* {@link QueryBase}. Such bindings are ignored.
* @param queryType
* The type of the {@link ASTQuery}.
*
* @return Some kind of {@link QueryBase} object.
*/
private QueryBase getQueryBase(final ASTQuery node, final Object data,
final QueryType queryType) {
final Node p = node.jjtGetParent();
if (log.isInfoEnabled())
log.info("parent=" + p + ", data="
+ (data == null ? "null" : data.getClass().getSimpleName()));
if (p instanceof ASTQueryContainer) {
return new QueryRoot(queryType);
}
return new SubqueryRoot(queryType);
}
/**
* Handle a SELECT clause. The {@link ProjectionNode} will be attached to
* the {@link IProjectionDecl}.
*
* @param select
* The {@link ASTSelect} node.
* @param queryRoot
* The {@link QueryBase}.
*
* @throws VisitorException
*/
protected void handleSelect(final ASTSelect select,
final IProjectionDecl queryRoot) throws VisitorException {
final ProjectionNode projection = new ProjectionNode();
queryRoot.setProjection(projection);
if (select.isDistinct())
projection.setDistinct(true);
if (select.isReduced())
projection.setReduced(true);
if (select.isWildcard()) {
projection.addProjectionVar(new VarNode("*"));
} else {
// Used to detect duplicate variables in a projection.
final Set vars = new HashSet();
final Iterator itr = select
.getProjectionElemList().iterator();
while (itr.hasNext()) {
/*
* The last argument of the children is the Var. Anything before
* that is an ArgList which must be interpreted in its own
* right.
*
* (Var | ArgList Var)
*/
final ASTProjectionElem e = itr.next();
final String varname;
if (!e.hasAlias()) {
// Var
final ASTVar aVar = (ASTVar) e.jjtGetChild(0/* index */);
varname = aVar.getName();
projection.addProjectionVar(new VarNode(varname));
} else {
// Expression AS Var
final SimpleNode expr = (SimpleNode) e.jjtGetChild(0);
final IValueExpressionNode ve = (IValueExpressionNode) expr
.jjtAccept(this, null/* data */);
varname = e.getAlias();
projection.addProjectionExpression(new AssignmentNode(
new VarNode(varname), ve));
}
if (!vars.add(varname))
throw new VisitorException(
"duplicate variable in projection: " + varname);
}
}
}
/**
* Handle the optional WHERE clause. (For example, DESCRIBE may be used
* without a WHERE clause.)
*
* Note: This delegates the translation to a helper visitor. A SubSelect
* will wind up delegated back to an instance of this visitor.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
@SuppressWarnings("unchecked")
private void handleWhereClause(final ASTQuery astQuery,
final QueryBase queryRoot) throws VisitorException {
final ASTWhereClause whereClause = astQuery.getWhereClause();
if (whereClause != null) {
final ASTGraphPatternGroup graphPatternGroup = whereClause
.getGraphPatternGroup();
graphPattern = scopedGroupGraphPattern(astQuery);
final GraphPatternGroup ret =
(GraphPatternGroup) graphPatternGroup
.jjtAccept(this, null/* data */);
queryRoot.setWhereClause(ret);
}
}
/**
* Handle an optional GROUP BY clause.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
private void handleGroupBy(final ASTQuery astQuery, final QueryBase queryRoot)
throws VisitorException {
final ASTGroupClause groupNode = astQuery.getGroupClause();
if (groupNode == null)
return;
final GroupByNode groupBy = (GroupByNode) groupNode.jjtAccept(this,
null);
queryRoot.setGroupBy(groupBy);
}
/**
* Handle an optional HAVING clause.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
private void handleHaving(final ASTQuery astQuery, final QueryBase queryRoot)
throws VisitorException {
final ASTHavingClause havingClause = astQuery.getHavingClause();
if (havingClause != null) {
final HavingNode havingNode = new HavingNode();
final int nchildren = havingClause.jjtGetNumChildren();
for (int i = 0; i < nchildren; i++) {
final IValueExpressionNode ve = (IValueExpressionNode) havingClause
.jjtGetChild(i).jjtAccept(this, null/* data */);
havingNode.addExpr(ve);
}
queryRoot.setHaving(havingNode);
}
}
/**
* Handle an optional ORDER BY clause.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
private void handleOrderBy(final ASTQuery astQuery,
final QueryBase queryRoot) throws VisitorException {
final ASTOrderClause orderNode = astQuery.getOrderClause();
if (orderNode != null) {
final OrderByNode orderBy = new OrderByNode();
@SuppressWarnings("unchecked")
final List orderElemements = (List) orderNode
.jjtAccept(this, null);
for (OrderByExpr orderByExpr : orderElemements)
orderBy.addExpr(orderByExpr);
queryRoot.setOrderBy(orderBy);
}
}
/**
* Handle an optional LIMIT/OFFSET.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*/
private void handleSlice(final ASTQuery astQuery,
final QueryBase queryRoot) {
final ASTLimit theLimit = astQuery.getLimit();
final ASTOffset theOffset = astQuery.getOffset();
if (theLimit != null || theOffset != null) {
final SliceNode theSlice = new SliceNode();
if (theLimit != null)
theSlice.setLimit(theLimit.getValue());
if (theOffset != null)
theSlice.setOffset(theOffset.getValue());
queryRoot.setSlice(theSlice);
}
}
/**
* Handle an optional BINDINGS clause.
*
* @param astQuery
* The AST query node. This is an abstract base class. There are
* concrete instances for SELECT, ASK, DESCRIBE, and CONSTRUCT.
* @param queryRoot
* The bigdata query root.
*
* @throws VisitorException
*/
private void handleBindings(final ASTQuery astQuery,
final QueryBase queryRoot) throws VisitorException {
/*
* VALUES clause is now allowed on subqueries.
*/
// if(!(queryRoot instanceof QueryRoot)) {
// // Only allowed on the QueryRoot
// return;
// }
final ASTBindingsClause bindingsClause = astQuery.getBindingsClause();
if (bindingsClause == null)
return;
final BindingsClause bindingSets = visit(bindingsClause, null/* data */);
if (bindingSets.getBindingSetsCount() > 0) {
// Attach the binding sets (if any).
queryRoot.setBindingsClause(bindingSets);
}
}
/**
* @return An object which encapsulates both the ordered set of variables
* for which bindings exist and the set of binding sets. The
* bindings are {@link BigdataValue}s. They must be translated into
* {@link IV} through a batch resolution process before they can be
* passed into the query engine.
*/
@Override
final public BindingsClause visit(final ASTBindingsClause node, Object data)
throws VisitorException
{
// The ordered list of variable bindings.
final List varNodes = node.jjtGetChildren(ASTVar.class);
final int nvars = varNodes.size();
final LinkedHashSet> vars = new LinkedHashSet>(nvars);
{
for (ASTVar varNode : varNodes) {
final VarNode var = (VarNode) varNode.jjtAccept(this, data);
final IVariable> v = var.getValueExpression();
if (!vars.add(v)) {
throw new VisitorException("duplicate variable in BINDINGS");
}
}
}
/*
* Collect binding sets.
*/
{
final List bindingNodes = node.jjtGetChildren(ASTBindingSet.class);
final List bindingSets = new LinkedList();
final IVariable>[] declaredVars = vars
.toArray(new IVariable[nvars]);
for (ASTBindingSet bindingNode : bindingNodes) {
final IBindingSet bindingSet = (IBindingSet) bindingNode
.jjtAccept(this, declaredVars);
bindingSets.add(bindingSet);
}
return new BindingsClause(vars, bindingSets);
}
}
/**
* Note: {@link BigdataValue}s have already been resolved to {@link IV}s.
*
* @param data
* The ordered variables for which bindings must be given.
*
* @return The binding set. Variables will be bound unless UNDEF was
* specified at the index corresponding to a given variable.
*/
@Override
public IBindingSet visit(final ASTBindingSet node, final Object data)
throws VisitorException
{
final IVariable>[] vars = (IVariable[]) data;
final int numberOfBindingValues = node.jjtGetNumChildren();
if (numberOfBindingValues != vars.length) {
throw new VisitorException(
"number of values in bindingset does not match variables in BINDINGS clause");
}
final ListBindingSet bset = new ListBindingSet();
for (int i = 0; i < numberOfBindingValues; i++) {
final ConstantNode ve = (ConstantNode) node.jjtGetChild(i)
.jjtAccept(this, null);
if (ve == null) {
/*
* No binding for the current variable in this binding set.
*/
continue;
}
// valueExpr2Var(ve).getValue();
bset.set(vars[i], ve.getValueExpression());
}
return bset;
}
@Override
public ValueExpressionNode visit(final ASTBindingValue node, final Object data)
throws VisitorException
{
if (node.jjtGetNumChildren() > 0) {
return (ValueExpressionNode) node.jjtGetChild(0).jjtAccept(this,
data);
}
else {
// UNDEF
return null;
}
}
/**
* Verify the various conditions that must be met when a query uses GROUP BY
* or when a query uses aggregates in a PROJECTION.
*
* @param queryBase
* The query.
*
* @throws VisitorException
*/
public static void verifyAggregate(final QueryBase queryBase)
throws VisitorException {
// if(true)
// return;
/*
* The following code has some dependencies on whether or not the
* value expressions have been cached. That is not done until we get
* into AST2BOpUtility. I have worked some hacks to support this in
* FunctionRegistry.isAggregate() and StaticAnalysis.isAggregate().
* However, the code is still hitting some edge cases.
*
* MP: I fixed this by running the ASTSetValueOptimizer earlier in the
* parsing process - ie. in Bigdata2ASTSPARQLParser.parseQuery2.
*
* There is some commented out code from openrdf that depends on setting
* a flag for the expression if an AggregationCollector reports at least
* one aggregation in a projection element. We could do this same thing
* here but we still need to have the logic to figure out what is an
* invalid aggregate.
*
* MP: I think the place to go look for reference is Sesame's
* TupleExprBuilder, especially:
*
* public TupleExpr visit(ASTSelect node, Object data)
*
* And also look at the AggregateCollector.
*/
final ProjectionNode projection = queryBase.getProjection() == null ? null
: queryBase.getProjection().isEmpty() ? null : queryBase
.getProjection();
final GroupByNode groupBy = queryBase.getGroupBy() == null ? null
: queryBase.getGroupBy().isEmpty() ? null : queryBase
.getGroupBy();
final HavingNode having = queryBase.getHaving() == null ? null
: queryBase.getHaving().isEmpty() ? null : queryBase
.getHaving();
// true if this is an aggregation query.
final boolean isAggregate = StaticAnalysis.isAggregate(projection,
groupBy, having);
if (isAggregate) {
if (projection.isWildcard())
throw new VisitorException(
"Wildcard not allowed with aggregate.");
final IValueExpression>[] projectExprs = projection
.getValueExpressions();
final IValueExpression>[] groupByExprs = groupBy == null ? null
: groupBy.getValueExpressions();
final IConstraint[] havingExprs = having == null ? null
: having.getConstraints();
try {
/*
* Delegate logic to validate the aggregate query.
*/
new GroupByState(projectExprs, groupByExprs, havingExprs);
} catch (IllegalArgumentException ex) {
throw new VisitorException("Bad aggregate", ex);
}
}
// /*
// * Note: The following code is modeled after the openrdf code that
// * checks the constraints on legal aggregations.
// */
//
// final ProjectionNode projection = queryBase.getProjection() == null ? null
// : queryBase.getProjection().isEmpty() ? null : queryBase
// .getProjection();
//
// final GroupByNode groupBy = queryBase.getGroupBy() == null ? null
// : queryBase.getGroupBy().isEmpty() ? null : queryBase
// .getGroupBy();
//
// final HavingNode having = queryBase.getHaving() == null ? null
// : queryBase.getHaving().isEmpty() ? null : queryBase
// .getHaving();
//
// if (groupBy != null) {
//
// if (projection.isWildcard())
// throw new VisitorException(
// "Wildcard not permitted with GROUP BY");
//
// /*
// * TODO Recursive search of a ValueExpression for anything that
// * would be mapped onto an IAggregate.
// */
//// AggregateCollector collector = new AggregateCollector();
//// valueExpr.visit(collector);
////
//// if (collector.getOperators().size() > 0) {
//// elem.setAggregateOperatorInExpression(true);
//
// for (AssignmentNode elem : projection) {
//
//// if (!elem.hasAggregateOperatorInExpression()) {
////
//// final Set groupNames = group.getBindingNames();
////
//// ExtensionElem extElem = elem.getSourceExpression();
//// if (extElem != null) {
//// ValueExpr expr = extElem.getExpr();
////
//// VarCollector collector = new VarCollector();
//// expr.visit(collector);
////
//// for (Var var : collector.getCollectedVars()) {
//// if (!groupNames.contains(var.getName())) {
//// throw new VisitorException("variable '" + var.getName()
//// + "' in projection not present in GROUP BY.");
////
//// }
//// }
//// }
//// else {
//// if (!groupNames.contains(elem.getTargetName())) {
//// throw new VisitorException("variable '" + elem.getTargetName()
//// + "' in projection not present in GROUP BY.");
//// }
//// else if (!groupNames.contains(elem.getSourceName())) {
//// throw new VisitorException("variable '" + elem.getSourceName()
//// + "' in projection not present in GROUP BY.");
////
//// }
//// }
//// }
// }
//
// }
}
}