All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bigdata.rdf.sail.sparql.GroupGraphPatternBuilder 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 21, 2011
 */

package com.bigdata.rdf.sail.sparql;

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.openrdf.query.algebra.StatementPattern.Scope;

import com.bigdata.bop.IBindingSet;
import com.bigdata.bop.IVariable;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.sail.sparql.ast.ASTBasicGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTBind;
import com.bigdata.rdf.sail.sparql.ast.ASTBindingSet;
import com.bigdata.rdf.sail.sparql.ast.ASTConstraint;
import com.bigdata.rdf.sail.sparql.ast.ASTConstruct;
import com.bigdata.rdf.sail.sparql.ast.ASTGraphGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTGraphPatternGroup;
import com.bigdata.rdf.sail.sparql.ast.ASTHavingClause;
import com.bigdata.rdf.sail.sparql.ast.ASTInlineData;
import com.bigdata.rdf.sail.sparql.ast.ASTLet;
import com.bigdata.rdf.sail.sparql.ast.ASTMinusGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTNamedSubqueryInclude;
import com.bigdata.rdf.sail.sparql.ast.ASTOptionalGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTServiceGraphPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTTRefPattern;
import com.bigdata.rdf.sail.sparql.ast.ASTUnionGraphPattern;
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.VisitorException;
import com.bigdata.rdf.sparql.ast.AssignmentNode;
import com.bigdata.rdf.sparql.ast.BindingsClause;
import com.bigdata.rdf.sparql.ast.ConstructNode;
import com.bigdata.rdf.sparql.ast.GroupNodeBase;
import com.bigdata.rdf.sparql.ast.JoinGroupNode;
import com.bigdata.rdf.sparql.ast.NamedSubqueryInclude;
import com.bigdata.rdf.sparql.ast.QuadsOperationInTriplesModeException;
import com.bigdata.rdf.sparql.ast.SubqueryRoot;
import com.bigdata.rdf.sparql.ast.TermNode;
import com.bigdata.rdf.sparql.ast.UnionNode;
import com.bigdata.rdf.sparql.ast.ValueExpressionNode;
import com.bigdata.rdf.sparql.ast.VarNode;
import com.bigdata.rdf.sparql.ast.service.ServiceNode;

/**
 * Visitor handles the GroupGraphPattern production (aka the
 * "WHERE" clause). This includes SubSelect and
 * GraphPatternNotTriples. ASTWhereClause has GroupGraphPattern
 * child which is a (SelectQuery (aka subquery)), GraphPattern
 * (BasicGraphPattern aka JoinGroup or GraphPatternNotTriples). The
 * TriplesBlock is handled by the {@link TriplePatternExprBuilder}.
 * 
 * @author Bryan Thompson
 * @version $Id: GroupGraphPatternBuilder.java 5064 2011-08-21 22:50:55Z
 *          thompsonbry $
 * @openrdf
 */
public class GroupGraphPatternBuilder extends TriplePatternExprBuilder {

    private static final Logger log = Logger.getLogger(GroupGraphPatternBuilder.class);
    
    public GroupGraphPatternBuilder(final BigdataASTContext context) {

        super(context);

        this.graphPattern = new GroupGraphPattern();

    }

    /**
     * CONSTRUCT (handled as a TriplesBlock).
     */
    @Override
    final public ConstructNode visit(final ASTConstruct node, final Object data)
            throws VisitorException {

        final GroupGraphPattern parentGP = graphPattern;
        
        graphPattern = new GroupGraphPattern();//parentGP); Note: No parent.

        // visit the children of the node (default behavior).
        super.visit(node, null);

        final ConstructNode group = graphPattern.buildGroup(new ConstructNode());
        
        graphPattern = parentGP;

        return group;
        
    }

    /**
     * ( SelectQuery | GraphPattern ) - this is the common path for
     * SubSelect and graph patterns.
     * 

* Note: (NOT) EXISTS uses a temporary graph pattern in order to avoid the * side-effect on the parent's graph pattern. Other value functions with * inner graph patterns should do this as well. * * @return The {@link GroupNodeBase}. This return value is used by the * visitor method for the {@link ASTWhereClause} and also in SPARQL * UPDATE. * * @see * Sub-select in INSERT cause NPE in UpdateExprBuilder */ @Override final public GroupNodeBase visit(final ASTGraphPatternGroup node, Object data) throws VisitorException { if (log.isInfoEnabled()) { log.info("\ndepth=" + depth(node) + ", parentGP(in)=" + graphPattern + "\n" + node.dump(indent(node))); } final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(parentGP); /* Visit the children of the node (default behavior). Note: [ret] will be null in many cases with a side effect on [graphPattern]. */ final Object ret = super.visit(node, null); final GroupNodeBase ret2; if (ret instanceof SubqueryRoot) { final SubqueryRoot subqueryRoot = (SubqueryRoot) ret; if (node.jjtGetParent() instanceof ASTWhereClause) { /** * SubSelect as the WHERE clause. In this case the outer graph * pattern group is a JoinGroupNode the SubSelect is embedded * within an inner JoinGroupNode. * *

                 * SELECT * { SELECT * { ?s ?p ?o } }
                 * 
*/ graphPattern = new GroupGraphPattern(parentGP); graphPattern.add(new JoinGroupNode(subqueryRoot)); @SuppressWarnings("rawtypes") final GroupNodeBase group = graphPattern .buildGroup(new JoinGroupNode()); ret2 = group; } else { /** * SubSelect embedded under the WHERE clause within its own * graph pattern group by the { SELECT ... } syntax. For example * *
                 * SELECT ?s { ?s ?x ?o . {SELECT ?x where {?x ?p ?x}}}
                 * 
* * Note: This no longer returns [null] due to the problem * cited in the ticket below. * * @see * Sub-select in INSERT cause NPE in UpdateExprBuilder */ @SuppressWarnings("rawtypes") final GroupNodeBase group = new JoinGroupNode(subqueryRoot); parentGP.add(group); ret2 = group; } } else { // if (node.jjtGetParent() instanceof ASTWhereClause // && graphPattern.isSimpleJoinGroup()) { // // /* // * If the sole child is a non-optional join group without a // * graph context, then it is elimated. This handles cases like a // * top-level UNION which would otherwise be nested as // * JoinGroupNode(JoinGroupNode(UnionNode(...))). // */ // // final JoinGroupNode group = (JoinGroupNode) graphPattern.get(0); // // parentGP.add(group); // // ret2 = group; // // } else { final JoinGroupNode joinGroup = new JoinGroupNode(); /* * We look up to the first ASTGraphGraphPattern ancestor, * which defines the context for the given node */ ASTGraphGraphPattern scopePattern = firstASTGraphGraphAncestor(node.jjtGetParent()); if (scopePattern!=null) { Node child = scopePattern.jjtGetChild(0); if (child!=null) { final TermNode s = (TermNode) scopePattern.jjtGetChild(0).jjtAccept(this, data); if (s!=null) joinGroup.setContext(s); } } @SuppressWarnings("rawtypes") final GroupNodeBase group = graphPattern.buildGroup(joinGroup); parentGP.add(group); ret2 = group; // } } if (log.isInfoEnabled()) log.info("\ndepth=" + depth(node) + ", graphPattern(out)=" + graphPattern); graphPattern = parentGP; return ret2; } /** * Note: while openrdf lifts the filters out of the optional, we do not need * to do this per MikeP. */ @Override final public Void visit(final ASTOptionalGraphPattern node, Object data) throws VisitorException { final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(parentGP); // visit the children. final Object tmp = super.visit(node, null); final JoinGroupNode joinGroup = new JoinGroupNode(); joinGroup.setOptional(true); if (tmp instanceof SubqueryRoot) { /** * Sub-Select * * @see * Note: The grammar file treats OPTIONAL somewhat differently than MINUS * due to the scope of the filters. For OPTIONAL, the child group can see * the variable bindings in the parent group. That is not true for MINUS. * Therefore the code in this method only sets the MINUS attribute on the * group and does not need to create a new group. */ @Override public Object visit(final ASTMinusGraphPattern node, Object data) throws VisitorException { final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(parentGP); // visit the children. super.visit(node, null); // final JoinGroupNode joinGroup = new JoinGroupNode(); // // joinGroup.setMinus(true); // // @SuppressWarnings("rawtypes") // final GroupNodeBase group = graphPattern.buildGroup(joinGroup); /* * The child is already wrapped up as a JoinGroupNode and we need to set * the MINUS annotation on that child. This takes a lazy approach and, * rather than reaching into the graphPattern data structure, it builds * a group which will contain just the desired child join group and then * pulls it out. */ final JoinGroupNode group = (JoinGroupNode) graphPattern.buildGroup( new JoinGroupNode()).get(0); // Annotate this group to give it negation semantics. group.setMinus(true); parentGP.add(group); graphPattern = parentGP; return null; } @Override final public Void visit(final ASTGraphGraphPattern node, Object data) throws VisitorException { final TermNode oldContext = graphPattern.getContext(); final Scope oldScope = graphPattern.getStatementPatternScope(); final TermNode newContext = (TermNode) node.jjtGetChild(0).jjtAccept( this, null); // @see https://jira.blazegraph.com/browse/BLZG-1176 // moved to ASTDeferredIVResolution.fillInIV(AST2BOpContext, BOp) // if (!context.tripleStore.isQuads()) { // if (newContext!=null) { // throw new QuadsOperationInTriplesModeException( // "Use of GRAPH construct in query body is not supported " // + "in triples mode."); // } // } graphPattern.setContextVar(newContext); graphPattern.setStatementPatternScope(Scope.NAMED_CONTEXTS); node.jjtGetChild(1).jjtAccept(this, null); graphPattern.setContextVar(oldContext); graphPattern.setStatementPatternScope(oldScope); return null; } /** * Given * *
     * select ?s where { { ?s ?p1 ?o } UNION  { ?s ?p2 ?o } UNION  { ?s ?p3 ?o } }
     * 
* * The parse tree looks like: * *
     * QueryContainer
     *  SelectQuery
     *   Select
     *    ProjectionElem
     *     Var (s)
     *   WhereClause
     *    GraphPatternGroup
     *     UnionGraphPattern
     *      GraphPatternGroup
     *       BasicGraphPattern
     *        TriplesSameSubjectPath
     *         Var (s)
     *         PropertyListPath
     *          Var (p1)
     *          ObjectList
     *           Var (o)
     *      UnionGraphPattern
     *       GraphPatternGroup
     *        BasicGraphPattern
     *         TriplesSameSubjectPath
     *          Var (s)
     *          PropertyListPath
     *           Var (p2)
     *           ObjectList
     *            Var (o)
     *       GraphPatternGroup
     *        BasicGraphPattern
     *         TriplesSameSubjectPath
     *          Var (s)
     *          PropertyListPath
     *           Var (p3)
     *           ObjectList
     *            Var (o)
     * 
* * That is, UNION in a binary operator in the parse tree. If the right hand * argument is also a UNION, then you are looking at a sequence of UNIONs at * the same level. Such sequences should be turned into a single * {@link UnionNode} in the bigdata AST. * * In contrast, this is a UNION of two groups with another UNION embedded in * the 2nd group. * *
     * select ?s where { { ?s ?p1 ?o } UNION  { { ?s ?p2 ?o } UNION  { ?s ?p3 ?o } }  }
     * 
* * The 2nd child of the first union is a GraphPatternGroup, not a * UnionGraphPattern. * *
     * QueryContainer
     *  SelectQuery
     *   Select
     *    ProjectionElem
     *     Var (s)
     *   WhereClause
     *    GraphPatternGroup
     *     UnionGraphPattern
     *      GraphPatternGroup
     *       BasicGraphPattern
     *        TriplesSameSubjectPath
     *         Var (s)
     *         PropertyListPath
     *          Var (p1)
     *          ObjectList
     *           Var (o)
     *      GraphPatternGroup
     *       UnionGraphPattern
     *        GraphPatternGroup
     *         BasicGraphPattern
     *          TriplesSameSubjectPath
     *           Var (s)
     *           PropertyListPath
     *            Var (p2)
     *            ObjectList
     *             Var (o)
     *        GraphPatternGroup
     *         BasicGraphPattern
     *          TriplesSameSubjectPath
     *           Var (s)
     *           PropertyListPath
     *            Var (p3)
     *            ObjectList
     *             Var (o)
     * 
*/ @Override final public Void visit(final ASTUnionGraphPattern node, Object data) throws VisitorException { final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(parentGP); // left arg is a some kind of group. node.jjtGetChild(0).jjtAccept(this, null); /* * The right arg is also a kind of group. If it is a ASTUnion, then this * is really a chain of UNIONs and they can all be flattened out. */ node.jjtGetChild(1).jjtAccept(this, null); // Build a union of those patterns. final UnionNode union = graphPattern.buildGroup(new UnionNode()); parentGP.add(union); graphPattern = parentGP; return null; } /** * A BIND (or FILTER) can appear in an {@link ASTBasicGraphPattern}. * * @return The {@link AssignmentNode} for the BIND -or- the * null if this is BIND(tripleRef AS var). */ @Override final public Object visit(final ASTBind node, final Object data) throws VisitorException { if (node.jjtGetNumChildren() != 2) throw new AssertionError("Expecting two children, not " + node.jjtGetNumChildren() + ", node=" + node.dump(">>>")); if (node.jjtGetChild(0) instanceof ASTTRefPattern) { /* * Note: This case is handled by visit(ASTTRefPattern,Object) when * we invoke jjtAccept() for the expression node. That method has * already added the appropriate SP to the group. We do not want to * do anything more here. * * Note: The caller does not actually do anything with the return * value from this method, so returning a [null] here is Ok. * * @see
* Reification Done Right */ node.jjtGetChild(0).jjtAccept(this, data); return null; } final Object expr = node.jjtGetChild(0).jjtAccept(this, data); final ValueExpressionNode ve = (ValueExpressionNode) expr; final Node aliasNode = node.jjtGetChild(1); final String alias = ((ASTVar) aliasNode).getName(); final AssignmentNode bind = new AssignmentNode(new VarNode(alias), ve); graphPattern.add(bind); return bind; } /** * @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 ASTInlineData 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); } final BindingsClause bind = new BindingsClause(vars, bindingSets); graphPattern.add(bind); return bind; } } /** * A LET is just an alternative syntax for BIND * * @return The {@link AssignmentNode}. */ @Override final public AssignmentNode visit(final ASTLet node, final Object data) throws VisitorException { if (node.jjtGetNumChildren() != 2) throw new AssertionError("Expecting two children, not " + node.jjtGetNumChildren() + ", node=" + node.dump(">>>")); final ValueExpressionNode ve = (ValueExpressionNode) node .jjtGetChild(1).jjtAccept(this, data); final Node aliasNode = node.jjtGetChild(0); final String alias = ((ASTVar) aliasNode).getName(); final AssignmentNode bind = new AssignmentNode(new VarNode(alias), ve); graphPattern.add(bind); return bind; } /** * A FILTER. The filter is attached to the {@link #graphPattern}. However, * it is also returned from this method. The {@link ASTHavingClause} uses * the return value. * * @return The constraint. */ @Override final public ValueExpressionNode visit(final ASTConstraint node, final Object data) throws VisitorException { final ValueExpressionNode valueExpr = (ValueExpressionNode) node .jjtGetChild(0).jjtAccept(this, null); graphPattern.addConstraint(valueExpr); return valueExpr; } /** * INCLUDE for a named subquery result set. */ @Override final public Void visit(final ASTNamedSubqueryInclude node, final Object data) throws VisitorException { final NamedSubqueryInclude includeNode = new NamedSubqueryInclude( node.getName()); // final int nargs = node.jjtGetNumChildren(); // // if (nargs > 0 || node.isQueryHint()) { // // /* // * Query hint for the join variables. This query hint may be used if // * static analysis of the context in which the INCLUDE appears fails // * to predict the correct join variables. (In the worst case it will // * fail to identify ANY join variables, which will cause the join to // * consider N x M solutions, rather than selectively probing a hash // * index to find just those solutions which could match.) // * // * Note: We should recognize the syntax () as explicitly requesting // * a join without join variables. This would have to be done as an // * action in [sparql.jjt]. // */ // // final VarNode[] joinvars = new VarNode[nargs]; // // for (int i = 0; i < nargs; i++) { // // final Node argNode = node.jjtGetChild(i); // // joinvars[i] = (VarNode) argNode.jjtAccept(this, null); // // } // // includeNode.setJoinVars(joinvars); // // } graphPattern.add(includeNode); return null; } // public Object visit(ASTServiceGraphPattern node, Object data) // throws VisitorException // { // GraphPattern parentGP = graphPattern; // // ValueExpr serviceRef = (ValueExpr)node.jjtGetChild(0).jjtAccept(this, null); // // graphPattern = new GraphPattern(parentGP); // node.jjtGetChild(1).jjtAccept(this, null); // TupleExpr serviceExpr = graphPattern.buildTupleExpr(); // // if (serviceExpr instanceof SingletonSet) // return null; // do not add an empty service block // // String serviceExpressionString = node.getPatternString(); // // parentGP.addRequiredTE(new Service(valueExpr2Var(serviceRef), serviceExpr, serviceExpressionString, // node.getPrefixDeclarations(), node.getBaseURI(), node.isSilent())); // // graphPattern = parentGP; // // return null; // } /** * SPARQL 1.1 SERVICE. *

* Note: The prefix declarations are attached to the * {@link ASTServiceGraphPattern} by the {@link PrefixDeclProcessor}. *

* TODO Do we need to pass through the baseURI? Can this be used to * abbreviate serialized IRIs? (I would think that we would use the prefix * declarations for that.) */ @Override final public Void visit(final ASTServiceGraphPattern node, final Object data) throws VisitorException { // left arg is the service reference (a value expression). final TermNode serviceRef = (TermNode) node.jjtGetChild(0).jjtAccept( this, null); final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(parentGP); // right arg is a some kind of group. node.jjtGetChild(1).jjtAccept(this, null); // Extract the service's join group. // final JoinGroupNode graphNode = graphPattern.buildGroup(new JoinGroupNode()); final JoinGroupNode graphNode = graphPattern.getSingletonGroup(); graphPattern = parentGP; final ServiceNode serviceNode = new ServiceNode(serviceRef, graphNode); /* * Note: This "image" of the original group graph pattern is what gets * sent to a remote SPARQL end point when we evaluate the SERVICE node. * Because the original "image" of the graph pattern is being used, we * also need to have the prefix declarations so we can generate a valid * SPARQL request. */ serviceNode.setExprImage(node.getPatternString()); final Map prefixDecls = node.getPrefixDeclarations(); if (prefixDecls != null && !prefixDecls.isEmpty()) { /* * Set the prefix declarations which go with that expression image. */ serviceNode.setPrefixDecls(prefixDecls); } if (node.isSilent()) { serviceNode.setSilent(true); } graphPattern.add(serviceNode); return (Void) null; } }