/** 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 Mar 10, 2012 */ /* * Copyright Aduna (http://www.aduna-software.com/) (c) 2011. * * Licensed under the Aduna BSD-style license. */ package com.bigdata.rdf.sail.sparql; import java.io.IOException; import java.io.StringReader; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.openrdf.model.BNode; import org.openrdf.model.Statement; import org.openrdf.model.Value; import org.openrdf.query.algebra.StatementPattern.Scope; import org.openrdf.repository.sail.helpers.SPARQLUpdateDataBlockParser; import org.openrdf.rio.RDFHandlerException; import org.openrdf.rio.RDFParseException; import org.openrdf.rio.helpers.BasicParserSettings; import org.openrdf.rio.helpers.StatementCollector; import com.bigdata.bop.BOpUtility; import com.bigdata.rdf.internal.IV; import com.bigdata.rdf.model.BigdataBNode; import com.bigdata.rdf.model.BigdataResource; import com.bigdata.rdf.model.BigdataStatement; import com.bigdata.rdf.model.BigdataURI; import com.bigdata.rdf.model.BigdataValue; import com.bigdata.rdf.model.StatementEnum; import com.bigdata.rdf.sail.sparql.ast.ASTAdd; import com.bigdata.rdf.sail.sparql.ast.ASTClear; import com.bigdata.rdf.sail.sparql.ast.ASTCopy; import com.bigdata.rdf.sail.sparql.ast.ASTCreate; import com.bigdata.rdf.sail.sparql.ast.ASTCreateEntailments; import com.bigdata.rdf.sail.sparql.ast.ASTDeleteClause; import com.bigdata.rdf.sail.sparql.ast.ASTDeleteData; import com.bigdata.rdf.sail.sparql.ast.ASTDeleteWhere; import com.bigdata.rdf.sail.sparql.ast.ASTDisableEntailments; import com.bigdata.rdf.sail.sparql.ast.ASTDrop; import com.bigdata.rdf.sail.sparql.ast.ASTDropEntailments; import com.bigdata.rdf.sail.sparql.ast.ASTEnableEntailments; import com.bigdata.rdf.sail.sparql.ast.ASTGraphOrDefault; import com.bigdata.rdf.sail.sparql.ast.ASTGraphPatternGroup; import com.bigdata.rdf.sail.sparql.ast.ASTGraphRefAll; import com.bigdata.rdf.sail.sparql.ast.ASTIRI; import com.bigdata.rdf.sail.sparql.ast.ASTInsertClause; import com.bigdata.rdf.sail.sparql.ast.ASTInsertData; import com.bigdata.rdf.sail.sparql.ast.ASTLoad; import com.bigdata.rdf.sail.sparql.ast.ASTModify; import com.bigdata.rdf.sail.sparql.ast.ASTMove; import com.bigdata.rdf.sail.sparql.ast.ASTQuadsNotTriples; import com.bigdata.rdf.sail.sparql.ast.ASTSolutionsRef; import com.bigdata.rdf.sail.sparql.ast.ASTUnparsedQuadDataBlock; import com.bigdata.rdf.sail.sparql.ast.ASTUpdate; import com.bigdata.rdf.sail.sparql.ast.Node; import com.bigdata.rdf.sail.sparql.ast.VisitorException; import com.bigdata.rdf.sparql.ast.AddGraph; import com.bigdata.rdf.sparql.ast.ClearGraph; import com.bigdata.rdf.sparql.ast.ConstantNode; import com.bigdata.rdf.sparql.ast.CopyGraph; import com.bigdata.rdf.sparql.ast.CreateEntailments; import com.bigdata.rdf.sparql.ast.CreateGraph; import com.bigdata.rdf.sparql.ast.DeleteData; import com.bigdata.rdf.sparql.ast.DeleteInsertGraph; import com.bigdata.rdf.sparql.ast.DisableEntailments; import com.bigdata.rdf.sparql.ast.DropEntailments; import com.bigdata.rdf.sparql.ast.DropGraph; import com.bigdata.rdf.sparql.ast.EnableEntailments; import com.bigdata.rdf.sparql.ast.GraphPatternGroup; import com.bigdata.rdf.sparql.ast.IGroupMemberNode; import com.bigdata.rdf.sparql.ast.InsertData; import com.bigdata.rdf.sparql.ast.JoinGroupNode; import com.bigdata.rdf.sparql.ast.LoadGraph; import com.bigdata.rdf.sparql.ast.MoveGraph; import com.bigdata.rdf.sparql.ast.QuadData; import com.bigdata.rdf.sparql.ast.QuadsDataOrNamedSolutionSet; import com.bigdata.rdf.sparql.ast.StatementPatternNode; import com.bigdata.rdf.sparql.ast.TermNode; import com.bigdata.rdf.sparql.ast.VarNode; import com.bigdata.rdf.spo.ISPO; import com.bigdata.rdf.spo.SPO; /** * Extension of {@link BigdataExprBuilder} that builds Update Expressions. * * @author Jeen Broekstra * @author Bryan Thompson * @openrdf */ public class UpdateExprBuilder extends BigdataExprBuilder { // private static final Logger log = Logger.getLogger(UpdateExprBuilder.class); public UpdateExprBuilder(final BigdataASTContext context) { super(context); } // TODO This is not required as far as I can tell. // @Override // public Update visit(final ASTUpdate node, final Object data) // throws VisitorException { // // if (node instanceof ASTModify) { // // return this.visit((ASTModify) node, data); // // } else if (node instanceof ASTInsertData) { // // return this.visit((ASTInsertData) node, data); // // } // // return null; // // } /** * Note: Variables in QuadDatas are disallowed in INSERT DATA requests (see * Notes 8 in the grammar). That is, the INSERT DATA statement only allows * to insert ground triples. Blank nodes in QuadDatas are assumed to be * disjoint from the blank nodes in the Graph Store, i.e., will be inserted * with "fresh" blank nodes. */ @Override public InsertData visit(final ASTInsertData node, final Object data) throws VisitorException { final InsertData op = new InsertData(); // variables are disallowed within DELETE DATA (per the spec). op.setData(doUnparsedQuadsDataBlock(node, data, false/* allowVars */, true/* allowBlankNodes */)); return op; } /** * Note: For DELETE DATA, QuadData denotes triples to be removed and is as * described in INSERT DATA, with the difference that in a DELETE DATA * operation neither variables nor blank nodes are allowed (see Notes 8+9 in * the grammar). */ @Override public DeleteData visit(final ASTDeleteData node, final Object data) throws VisitorException { final DeleteData op = new DeleteData(); // variables are disallowed within DELETE DATA (per the spec). // blank nodes are disallowed within DELETE DATA (per the spec). op.setData(doUnparsedQuadsDataBlock(node, data, false/* allowVars */, false/* allowBlankNodes */)); return op; } @Override public QuadData visit(final ASTQuadsNotTriples node, final Object data) throws VisitorException { final GroupGraphPattern parentGP = graphPattern; graphPattern = scopedGroupGraphPattern(node); final TermNode contextNode = (TermNode) node.jjtGetChild(0).jjtAccept( this, data); graphPattern.setContextVar(contextNode); graphPattern.setStatementPatternScope(Scope.NAMED_CONTEXTS); for (int i = 1; i < node.jjtGetNumChildren(); i++) { node.jjtGetChild(i).jjtAccept(this, data); } final QuadData group = graphPattern.buildGroup(new QuadData()); parentGP.add(group); graphPattern = parentGP; return group; } /** * This handles the "DELETE WHERE" syntax short hand. * * <pre> * DELETE WHERE QuadPattern * </pre> * * This is similar to a CONSTRUCT without an explicit template. The WHERE * clause provides both the pattern to match and the template for the quads * to be removed. * <p> * Note: The grammar production for the WHERE clause for the DELETE WHERE * shortcut form is the same production that is used for the DELETE and * INSERT clauses. This results in a {@link QuadData} object containing * {@link StatementPatternNode}s. That object must be transformed into a * {@link JoinGroupNode} in order to be valid as a WHERE clause. * * @see https://sourceforge.net/apps/trac/bigdata/ticket/568 (DELETE WHERE * fails with Java AssertionError) */ @Override public DeleteInsertGraph visit(final ASTDeleteWhere node, final Object data) throws VisitorException { // Collect construct triples final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(); // inherit scope & context graphPattern.setStatementPatternScope(parentGP.getStatementPatternScope()); graphPattern.setContextVar(parentGP.getContext()); for (int i = 0; i < node.jjtGetNumChildren(); i++) { node.jjtGetChild(i).jjtAccept(this, data); } final JoinGroupNode whereClause = graphPattern.buildGroup(new JoinGroupNode()); graphPattern = parentGP; // no blank nodes in DELETE WHERE statement patterns final Iterator<StatementPatternNode> itr = BOpUtility.visitAll( whereClause, StatementPatternNode.class); while (itr.hasNext()) { final StatementPatternNode sp = itr.next(); // Blank nodes are not permitted in DELETE WHERE. // Note: predicate can never be a blank node (always URI) assertNotAnonymousVariable(sp.s()); assertNotAnonymousVariable(sp.o()); } final DeleteInsertGraph op = new DeleteInsertGraph(); op.setWhereClause(whereClause); return op; } @Override public LoadGraph visit(final ASTLoad node, final Object data) throws VisitorException { final ConstantNode sourceGraph = (ConstantNode) node.jjtGetChild(0) .jjtAccept(this, data); final LoadGraph op = new LoadGraph(); op.setSourceGraph(sourceGraph); if (node.isSilent()) op.setSilent(true); if (node.jjtGetNumChildren() > 1) { final ConstantNode targetGraph = (ConstantNode) node.jjtGetChild(1) .jjtAccept(this, data); op.setTargetGraph(targetGraph); } return op; } /** * Note: DROP and CLEAR have the identical semantics for bigdata since it * does not support empty graphs. */ @Override public ClearGraph visit(final ASTClear node, final Object data) throws VisitorException { final ClearGraph op = new ClearGraph(); if (node.isSilent()) op.setSilent(true); final ASTGraphRefAll graphRef = node.jjtGetChild(ASTGraphRefAll.class); if (graphRef.jjtGetNumChildren() > 0) { final TermNode targetGraph = (TermNode) graphRef .jjtGetChild(0).jjtAccept(this, data); if(targetGraph instanceof ConstantNode) { op.setTargetGraph((ConstantNode) targetGraph); } else { op.setTargetSolutionSet(targetGraph.getValueExpression() .getName()); } } else { if (graphRef.isDefault()) { op.setScope(Scope.DEFAULT_CONTEXTS); } else if (graphRef.isNamed()) { op.setScope(Scope.NAMED_CONTEXTS); } if(graphRef.isAllGraphs()) { op.setAllGraphs(true); } if(graphRef.isAllSolutions()) { op.setAllSolutionSets(true); } } return op; } /** * Note: DROP and CLEAR have the identical semantics for bigdata since it * does not support empty graphs. */ @Override public DropGraph visit(final ASTDrop node, final Object data) throws VisitorException { final DropGraph op = new DropGraph(); if(node.isSilent()) op.setSilent(true); final ASTGraphRefAll graphRef = node.jjtGetChild(ASTGraphRefAll.class); if (graphRef.jjtGetNumChildren() > 0) { final TermNode targetGraph = (TermNode) graphRef .jjtGetChild(0).jjtAccept(this, data); if (targetGraph instanceof ConstantNode) { op.setTargetGraph((ConstantNode) targetGraph); } else { op.setTargetSolutionSet(targetGraph.getValueExpression() .getName()); } } else { if (graphRef.isDefault()) { op.setScope(Scope.DEFAULT_CONTEXTS); } else if (graphRef.isNamed()) { op.setScope(Scope.NAMED_CONTEXTS); } if(graphRef.isAllGraphs()) { op.setAllGraphs(true); } if(graphRef.isAllSolutions()) { op.setAllSolutionSets(true); } } return op; } @Override public CreateGraph visit(final ASTCreate node, final Object data) throws VisitorException { final TermNode target = (TermNode) node.jjtGetChild(0).jjtAccept(this, data); final CreateGraph op = new CreateGraph(); if (target instanceof ConstantNode) { op.setTargetGraph((ConstantNode) target); } else { op.setTargetSolutionSet(target.getValueExpression().getName()); final BigdataStatement[] params = doQuadsData(node, data, false/*allowVars*/, true/* allowBlankNodes */); if (params != null && params.length > 0) { op.setParams(params); } } if (node.isSilent()) op.setSilent(true); return op; } @Override public CopyGraph visit(final ASTCopy node, final Object data) throws VisitorException { final CopyGraph op = new CopyGraph(); if (node.isSilent()) op.setSilent(true); final ASTGraphOrDefault sourceNode = (ASTGraphOrDefault) node .jjtGetChild(0); if (sourceNode.jjtGetNumChildren() > 0) { final ConstantNode sourceGraph = (ConstantNode) sourceNode .jjtGetChild(0).jjtAccept(this, data); op.setSourceGraph(sourceGraph); } final ASTGraphOrDefault destinationNode = (ASTGraphOrDefault) node .jjtGetChild(1); if (destinationNode.jjtGetNumChildren() > 0) { final ConstantNode targetGraph = (ConstantNode) destinationNode .jjtGetChild(0).jjtAccept(this, data); op.setTargetGraph(targetGraph); } return op; } @Override public MoveGraph visit(final ASTMove node, final Object data) throws VisitorException { final MoveGraph op = new MoveGraph(); if (node.isSilent()) op.setSilent(true); final ASTGraphOrDefault sourceNode = (ASTGraphOrDefault) node .jjtGetChild(0); if (sourceNode.jjtGetNumChildren() > 0) { final ConstantNode sourceGraph = (ConstantNode) sourceNode .jjtGetChild(0).jjtAccept(this, data); op.setSourceGraph(sourceGraph); } final ASTGraphOrDefault targetNode = (ASTGraphOrDefault) node .jjtGetChild(1); if (targetNode.jjtGetNumChildren() > 0) { final ConstantNode targetGraph = (ConstantNode) targetNode .jjtGetChild(0).jjtAccept(this, data); op.setTargetGraph(targetGraph); } return op; } @Override public AddGraph visit(final ASTAdd node, final Object data) throws VisitorException { final AddGraph op = new AddGraph(); if (node.isSilent()) op.setSilent(true); final ASTGraphOrDefault sourceNode = (ASTGraphOrDefault) node .jjtGetChild(0); if (sourceNode.jjtGetNumChildren() > 0) { final ConstantNode sourceGraph = (ConstantNode) sourceNode .jjtGetChild(0).jjtAccept(this, data); op.setSourceGraph(sourceGraph); } final ASTGraphOrDefault targetNode = (ASTGraphOrDefault) node .jjtGetChild(1); if (targetNode.jjtGetNumChildren() > 0) { final ConstantNode targetGraph = (ConstantNode) targetNode .jjtGetChild(0).jjtAccept(this, data); op.setTargetGraph(targetGraph); } return op; } /* * DELETE/INSERT */ /** * The DELETE/INSERT operation can be used to remove or add triples from/to * the Graph Store based on bindings for a query pattern specified in a * WHERE clause: * * <pre> * ( WITH IRIref )? * ( ( DeleteClause InsertClause? ) | InsertClause ) * ( USING ( NAMED )? IRIref )* * WHERE GroupGraphPattern * </pre> * * The DeleteClause and InsertClause forms can be broken down as follows: * * <pre> * DeleteClause ::= DELETE QuadPattern * InsertClause ::= INSERT QuadPattern * </pre> */ @Override public DeleteInsertGraph visit(final ASTModify node, final Object data) throws VisitorException { final DeleteInsertGraph op = new DeleteInsertGraph(); ConstantNode with = null; { final ASTIRI withNode = node.getWithClause(); if (withNode != null) { // @see https://jira.blazegraph.com/browse/BLZG-1176 // moved to com.bigdata.rdf.sail.sparql.ASTDeferredIVResolution.fillInIV(AST2BOpContext, BOp) // if (!context.tripleStore.isQuads()) { // throw new QuadsOperationInTriplesModeException( // "Using named graph referenced through WITH clause " + // "is not supported in triples mode."); // } with = (ConstantNode) withNode.jjtAccept(this, data); /* * Set the default context for the WHERE clause, DELETE clause, * and/or INSERT clauser. */ graphPattern.setContextVar(with); graphPattern.setStatementPatternScope(Scope.NAMED_CONTEXTS); } } { final ASTGraphPatternGroup whereClause = node.getWhereClause(); if (whereClause != null) { @SuppressWarnings("unchecked") final GraphPatternGroup<IGroupMemberNode> ret = (GraphPatternGroup<IGroupMemberNode>) whereClause .jjtAccept(this, null/* data */); op.setWhereClause(ret); } } { final ASTDeleteClause deleteNode = node.getDeleteClause(); if (deleteNode != null) { if (deleteNode.getName() == null) { // This is a QuadPattern (versus ground data) final QuadData quadData = (QuadData) deleteNode .jjtAccept(this, data); op.setDeleteClause(new QuadsDataOrNamedSolutionSet(quadData)); } else { final QuadsDataOrNamedSolutionSet deleteClause = new QuadsDataOrNamedSolutionSet( deleteNode.getName()); handleSelect(deleteNode.getSelect(), deleteClause); op.setDeleteClause(deleteClause); } } } { final ASTInsertClause insertNode = node.getInsertClause(); if (insertNode != null) { if (insertNode.getName() == null) { // This is a QuadPattern (versus ground data) final QuadData quadData = (QuadData) insertNode .jjtAccept(this, data); op.setInsertClause(new QuadsDataOrNamedSolutionSet(quadData)); } else { final QuadsDataOrNamedSolutionSet insertClause = new QuadsDataOrNamedSolutionSet( insertNode.getName()); handleSelect(insertNode.getSelect(), insertClause); op.setInsertClause(insertClause); } } } return op; } @Override public QuadData visit(final ASTDeleteClause node, final Object data) throws VisitorException { return doQuadsPatternClause(node, data, false/* allowBlankNodes */); } @Override public QuadData visit(final ASTInsertClause node, final Object data) throws VisitorException { return doQuadsPatternClause(node, data, true/* allowBlankNodes */); } private BigdataStatement[] doUnparsedQuadsDataBlock(final ASTUpdate node, final Object data, final boolean allowVars, final boolean allowBlankNodes) throws VisitorException { final ASTUnparsedQuadDataBlock dataBlock = node.jjtGetChild(ASTUnparsedQuadDataBlock.class); final SPARQLStarUpdateDataBlockParser parser = new SPARQLStarUpdateDataBlockParser(context.valueFactory); final Collection<Statement> stmts = new LinkedList<Statement>(); final StatementCollector sc = new StatementCollector(stmts); parser.setRDFHandler(sc); parser.getParserConfig().addNonFatalError(BasicParserSettings.VERIFY_DATATYPE_VALUES); parser.getParserConfig().addNonFatalError(BasicParserSettings.FAIL_ON_UNKNOWN_DATATYPES); try { // TODO process update context somehow? dataset, base URI, etc. parser.parse(new StringReader(dataBlock.getDataBlock()), ""); } catch (RDFParseException e) { throw new VisitorException(e); } catch (RDFHandlerException e) { throw new VisitorException(e); } catch (IOException e) { throw new VisitorException(e); } /** * Reject real quads in triple mode */ // @see https://jira.blazegraph.com/browse/BLZG-1176 // moved to com.bigdata.rdf.sail.sparql.ASTDeferredIVResolution.fillInIV(AST2BOpContext, BOp) // if (!context.tripleStore.isQuads()) { // for (Statement stmt : stmts) { // if (stmt!=null && stmt.getContext()!=null) { // throw new QuadsOperationInTriplesModeException( // "Quads in SPARQL update data block are not supported " + // "in triples mode."); // } // } // } if (!allowBlankNodes) { for (Statement stmt : stmts) { /** * Blank nodes are not allowed in DELETE DATA. * * See http://trac.bigdata.com/ticket/1076#comment:5 */ if (isInvalidBlankNode(stmt.getSubject()) || stmt.getPredicate() instanceof BNode || isInvalidBlankNode(stmt.getObject()) || (stmt.getContext() != null && stmt.getContext() instanceof BNode)) { throw new VisitorException( "Blank nodes are not permitted in DELETE DATA"); } } } final BigdataStatement[] a = stmts.toArray( new BigdataStatement[stmts.size()]); return a; } private boolean isInvalidBlankNode(Value v) { if (v instanceof BigdataBNode && ((BigdataBNode)v).isStatementIdentifier()) { return false; } else { return v instanceof BNode; } } /** * Collect 'QuadData' for an INSERT DATA or DELETE DATA operation. This form * does not allow variables in the quads data. * <p> * Note: The QuadData is basically modeled by the bigdata AST as recursive * {@link StatementPatternNode} containers. This visits the parser AST nodes * and then flattens them into an {@link ISPO}[]. The {@link ISPO}s are * {@link BigdataStatement}s at this point, but they can be converted to * {@link SPO}s when the INSERT DATA or DELETE DATA operation runs. * * @return A flat {@link ISPO}[] modeling the quad data. * * @throws VisitorException * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/573"> * NullPointerException when attempting to INSERT DATA containing a * blank node </a> */ private BigdataStatement[] doQuadsData(final ASTUpdate node, final Object data, final boolean allowVars, final boolean allowBlankNodes) throws VisitorException { final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(); // inherit scope & context (from optional WITH clause) graphPattern.setStatementPatternScope(parentGP.getStatementPatternScope()); graphPattern.setContextVar(parentGP.getContext()); /* * Visit [QuadsData]. * * TODO What is this doing? It is making some decision based on just the * first child... */ if (node.jjtGetNumChildren() > 0) { // first child. final Node child0 = node.jjtGetChild(0); final Object algebraExpr = child0.jjtAccept(this, data); if (algebraExpr instanceof ConstantNode) { // named graph identifier final ConstantNode context = (ConstantNode) algebraExpr; graphPattern.setContextVar(context); graphPattern.setStatementPatternScope(Scope.NAMED_CONTEXTS); } // remaining children (i=1...). for (int i = 1; i < node.jjtGetNumChildren(); i++) { node.jjtGetChild(i).jjtAccept(this, data); } } final QuadData quadData = graphPattern.buildGroup(new QuadData()); graphPattern = parentGP; /* * Flatten the QuadData into a simple ISPO[]. */ final List<ISPO> stmts = new LinkedList<ISPO>(); final Iterator<StatementPatternNode> itr = BOpUtility.visitAll( quadData, StatementPatternNode.class); while (itr.hasNext()) { final StatementPatternNode sp = itr.next(); if (!allowVars) { // Variables not permitted in INSERT DATA or DELETE DATA. assertNotVariable(sp.s()); assertNotVariable(sp.p()); assertNotVariable(sp.o()); assertNotVariable(sp.c()); } if (!allowBlankNodes) { // Blank nodes are not permitted in DELETE DATA. // Note: predicate can never be a blank node (always URI) assertNotAnonymousVariable(sp.s()); assertNotAnonymousVariable(sp.o()); } final BigdataResource s = (BigdataResource) toValue(sp.s()); final BigdataValue o = toValue(sp.o()); final BigdataStatement spo = context.valueFactory.createStatement(// (BigdataResource) s, // (BigdataURI) sp.p().getValue(), // (BigdataValue) o,// (BigdataResource) (sp.c() != null ? sp.c().getValue(): null),// StatementEnum.Explicit// ); // final ISPO spo = new SPO(// // sp.s().getValue().getIV(), // // sp.p().getValue().getIV(), // // sp.o().getValue().getIV(),// // (sp.c() != null ? sp.c().getValue().getIV() : null),// // StatementEnum.Explicit// // ); stmts.add(spo); } final BigdataStatement[] a = stmts.toArray(new BigdataStatement[stmts .size()]); return a; } /** * Method used to correctly reject variables in an INSERT DATA or * DELETE DATA operation. * * @param t * A Subject, Predicate, or Object term. */ private void assertNotVariable(final TermNode t) throws VisitorException { if (t == null) return; if (!t.isVariable()) return; final VarNode v = (VarNode) t; if (v.isAnonymous()) { // Blank node (versus a variable) return; } throw new VisitorException( "Variable not permitted in this context: " + t); } /** * Method used to correctly reject blank nodes in a DELETE DATA clause. * * @param t * A Subject or Object term. */ private void assertNotAnonymousVariable(final TermNode t) throws VisitorException { if (t.isVariable()) { final VarNode v = (VarNode) t; if (v.isAnonymous()) throw new VisitorException( "BlankNode not permitted in this context: " + t); } else { final IV iv = t.getValueExpression().get(); if (iv.isBNode()) throw new VisitorException( "BlankNode not permitted in this context: " + t); } } /** * Convert the {@link TermNode} to a {@link BigdataValue}. IFF the * {@link TermNode} is an anonymous variable, then it is converted into a * blank node whose ID is the name of that anonymous variable. This is used * to turn anonymous variables back into blank nodes for INSERT DATA (DELETE * DATA does not allow blank nodes). * * @param t * The {@link TermNode}. * * @return The {@link BigdataValue}. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/573"> * NullPointerException when attempting to INSERT DATA containing a * blank node </a> */ private BigdataValue toValue(final TermNode t) { if (t.isVariable() && ((VarNode) t).isAnonymous()) { final BigdataBNode s = context.valueFactory.createBNode(t .getValueExpression().getName()); return s; } return t.getValue(); } /** * Collect quads patterns for a DELETE/INSERT operation. This form allows * variables in the quads patterns. * * @param data * * @return The {@link QuadData} (aka template). The {@link QuadData} as * returned by this method is not flattened, but the context has * been applied within any GRAPH section. * * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/571"> * DELETE/INSERT WHERE handling of blank nodes </a> */ private QuadData doQuadsPatternClause(final Node node, final Object data, final boolean allowBlankNodes) throws VisitorException { // Collect construct triples final GroupGraphPattern parentGP = graphPattern; graphPattern = new GroupGraphPattern(); // inherit scope & context graphPattern.setStatementPatternScope(parentGP.getStatementPatternScope()); graphPattern.setContextVar(parentGP.getContext()); for (int i = 0; i < node.jjtGetNumChildren(); i++) { node.jjtGetChild(i).jjtAccept(this, data); } final QuadData quadData = graphPattern.buildGroup(new QuadData()); if (!allowBlankNodes) { /* * Blank nodes are not allowed in the DELETE clause template. */ final Iterator<StatementPatternNode> itr = BOpUtility.visitAll( quadData, StatementPatternNode.class); while (itr.hasNext()) { final StatementPatternNode sp = itr.next(); assertNotAnonymousVariable(sp.s()); assertNotAnonymousVariable(sp.o()); } } graphPattern = parentGP; return quadData; } /** * Solution set names get modeled as variables which report * <code>true</code> for {@link VarNode#isSolutionSet()}. */ @Override final public VarNode visit(final ASTSolutionsRef node, final Object data) throws VisitorException { final String name = node.getName(); final VarNode c = new VarNode(name); c.setSolutionSet(true); return c; } @Override public DropEntailments visit(ASTDropEntailments node, Object data) throws VisitorException { return new DropEntailments(); } @Override public CreateEntailments visit(ASTCreateEntailments node, Object data) throws VisitorException { return new CreateEntailments(); } @Override public EnableEntailments visit(ASTEnableEntailments node, Object data) throws VisitorException { return new EnableEntailments(); } @Override public DisableEntailments visit(ASTDisableEntailments node, Object data) throws VisitorException { return new DisableEntailments(); } }