This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 1e075c9  JENA-2294: Handle SILENT for datasets with limited named 
graph support
     new b873622  Merge pull request #1214 from afs/jena2294-dsg-one
1e075c9 is described below

commit 1e075c9c52b74b0479a474813bed5b274c9ea2e8
Author: Andy Seaborne <[email protected]>
AuthorDate: Tue Mar 1 21:18:47 2022 +0000

    JENA-2294: Handle SILENT for datasets with limited named graph support
---
 .../jena/sparql/modify/UpdateEngineWorker.java     | 460 +++++++++++----------
 .../jena/sparql/modify/request/UpdateBinaryOp.java |  15 +-
 .../jena/sparql/modify/request/UpdateLoad.java     |  69 ++--
 .../sparql/modify/request/UpdateWriterVisitor.java |   4 +-
 .../sparql/modify/AbstractTestUpdateGraphMgt.java  |   6 -
 .../org/apache/jena/sparql/modify/TS_Update.java   |   4 +-
 ...tMem.java => TestUpdateGraphMgtGeneralMem.java} |   2 +-
 ...phMgtMem.java => TestUpdateGraphMgtTxnMem.java} |   4 +-
 .../jena/sparql/modify/TestUpdateSilent.java       | 148 +++++++
 jena-arq/testing/Update/empty.nt                   |   0
 10 files changed, 450 insertions(+), 262 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateEngineWorker.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateEngineWorker.java
index ea80cf0..9c4380b 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateEngineWorker.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateEngineWorker.java
@@ -18,100 +18,111 @@
 
 package org.apache.jena.sparql.modify;
 
-import static org.apache.jena.sparql.modify.TemplateLib.remapDefaultGraph ;
-import static org.apache.jena.sparql.modify.TemplateLib.template ;
-
-import java.util.ArrayList ;
-import java.util.Collection ;
-import java.util.Iterator ;
-import java.util.List ;
-
-import org.apache.jena.atlas.data.BagFactory ;
-import org.apache.jena.atlas.data.DataBag ;
-import org.apache.jena.atlas.data.ThresholdPolicy ;
-import org.apache.jena.atlas.data.ThresholdPolicyFactory ;
-import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.lib.Pair ;
+import static org.apache.jena.sparql.modify.TemplateLib.remapDefaultGraph;
+import static org.apache.jena.sparql.modify.TemplateLib.template;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jena.atlas.data.BagFactory;
+import org.apache.jena.atlas.data.DataBag;
+import org.apache.jena.atlas.data.ThresholdPolicy;
+import org.apache.jena.atlas.data.ThresholdPolicyFactory;
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.Pair;
 import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.graph.Graph ;
-import org.apache.jena.graph.GraphUtil ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.query.Query ;
-import org.apache.jena.query.QueryExecutionFactory ;
-import org.apache.jena.riot.Lang;
-import org.apache.jena.riot.RDFDataMgr;
-import org.apache.jena.riot.RDFLanguages;
-import org.apache.jena.riot.RDFParser;
-import org.apache.jena.sparql.ARQInternalErrorException ;
-import org.apache.jena.sparql.core.* ;
-import org.apache.jena.sparql.engine.Plan ;
-import org.apache.jena.sparql.engine.binding.Binding ;
-import org.apache.jena.sparql.engine.binding.BindingRoot ;
-import org.apache.jena.sparql.graph.GraphFactory ;
-import org.apache.jena.sparql.graph.GraphOps ;
-import org.apache.jena.sparql.modify.request.* ;
-import org.apache.jena.sparql.syntax.Element ;
-import org.apache.jena.sparql.syntax.ElementGroup ;
-import org.apache.jena.sparql.syntax.ElementNamedGraph ;
-import org.apache.jena.sparql.syntax.ElementTriplesBlock ;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphUtil;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.riot.*;
+import org.apache.jena.sparql.ARQInternalErrorException;
+import org.apache.jena.sparql.core.*;
+import org.apache.jena.sparql.engine.Plan;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingRoot;
+import org.apache.jena.sparql.graph.GraphFactory;
+import org.apache.jena.sparql.graph.GraphOps;
+import org.apache.jena.sparql.modify.request.*;
+import org.apache.jena.sparql.syntax.Element;
+import org.apache.jena.sparql.syntax.ElementGroup;
+import org.apache.jena.sparql.syntax.ElementNamedGraph;
+import org.apache.jena.sparql.syntax.ElementTriplesBlock;
 import org.apache.jena.sparql.system.SerializationFactoryFinder;
-import org.apache.jena.sparql.util.Context ;
-import org.apache.jena.update.UpdateException ;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.update.UpdateException;
 
-/** Implementation of general purpose update request execution */ 
+/** Implementation of general purpose update request execution */
 public class UpdateEngineWorker implements UpdateVisitor
 {
-    protected final DatasetGraph datasetGraph ;
-    protected final boolean alwaysSilent = true ;
-    protected final Binding inputBinding;       // Used for UpdateModify only
-    protected final Context context ;
+    protected final DatasetGraph datasetGraph;
+    protected final boolean autoSilent = true;  // DROP and CREATE
+    protected final Binding inputBinding;       // Used for UpdateModify only: 
substitution is better.
+    protected final Context context;
 
     public UpdateEngineWorker(DatasetGraph datasetGraph, Binding inputBinding, 
Context context) {
-        this.datasetGraph = datasetGraph ;
-        this.inputBinding = inputBinding ;
-        this.context = context ;
+        this.datasetGraph = datasetGraph;
+        this.inputBinding = inputBinding;
+        this.context = context;
     }
 
     @Override
     public void visit(UpdateDrop update)
-    { execDropClear(update, false) ; }
+    { execDropClear(update, false); }
 
     @Override
     public void visit(UpdateClear update)
-    { execDropClear(update, true) ; }
+    { execDropClear(update, true); }
 
     protected void execDropClear(UpdateDropClear update, boolean isClear) {
         if ( update.isAll() ) {
-            execDropClear(update, null, true); // Always clear.
+            // ALL
+            execDropClear(update, null, true);  // DROP is CLEAR on DEFAULT.
             execDropClearAllNamed(update, isClear);
         } else if ( update.isAllNamed() )
+            // NAMED
             execDropClearAllNamed(update, isClear);
         else if ( update.isDefault() )
-            execDropClear(update, null, true);
+            // DEFAULT
+            execDropClear(update, null, true);  // DROP is CLEAR on DEFAULT.
         else if ( update.isOneGraph() )
+            // GRAPH iri
             execDropClear(update, update.getGraph(), isClear);
         else
+            // Error: should not happen.
             throw new ARQInternalErrorException("Target is undefined: " + 
update.getTarget());
     }
 
     protected void execDropClear(UpdateDropClear update, Node g, boolean 
isClear) {
-        if ( !alwaysSilent ) {
-            if ( g != null && !datasetGraph.containsGraph(g) && 
!update.isSilent() )
-                error("No such graph: " + g);
-        }
-
-        if ( isClear ) {
-            if ( g == null || datasetGraph.containsGraph(g) )
-                graph(datasetGraph, g).clear();
-        } else
-            datasetGraph.removeGraph(g);
+        // DROP always works.
+        // """
+        //   After successful completion of this operation, the specified 
graphs are no
+        //   longer available for further graph update operations.
+        // """
+        boolean auto = autoSilent && !isClear;
+        executeOperation( auto || update.isSilent(), () -> {
+            if ( g != null && !datasetGraph.containsGraph(g) )
+                    throw errorEx("No such graph: " + g);
+            if ( isClear ) {
+                if ( g == null || datasetGraph.containsGraph(g) )
+                    graphOrThrow(datasetGraph, g).clear();
+            } else {
+                try {
+                    datasetGraph.removeGraph(g);
+                } catch (UnsupportedOperationException ex) {
+                    throw new UpdateException("DROP of named graph not 
supported");
+                }
+            }
+        });
     }
 
     protected void execDropClearAllNamed(UpdateDropClear update, boolean 
isClear) {
         // Avoid ConcurrentModificationException
         List<Node> list = Iter.toList(datasetGraph.listGraphNodes());
-
         for ( Node gn : list )
             execDropClear(update, gn, isClear);
     }
@@ -122,155 +133,162 @@ public class UpdateEngineWorker implements UpdateVisitor
         if ( g == null )
             return;
         if ( datasetGraph.containsGraph(g) ) {
-            if ( !alwaysSilent && !update.isSilent() )
-                error("Graph store already contains graph : " + g);
+            if ( !autoSilent && !update.isSilent() )
+                throw errorEx("Graph store already contains graph : " + g);
             return;
         }
-        // In-memory specific
-        datasetGraph.addGraph(g, GraphFactory.createDefaultGraph());
+        // To be general, add an empty graph.
+        // Most datasets implementations have "auto-create" so CREATE is a 
no-op.
+        // But dataset of separate graphs needs this (check!) to trigger the 
graph.
+        // This is "copy-in" of zero triples.
+        executeOperation(update.isSilent(), () ->
+                { try { datasetGraph.addGraph(g, 
GraphFactory.createDefaultGraph()); }
+                  catch(UnsupportedOperationException ex) {
+                      throw new UpdateException("CREATE of named graph not 
supported");
+                }
+        });
     }
 
     @Override
     public void visit(UpdateLoad update) {
-        // LOAD SILENT? iri ( INTO GraphRef )? 
+        // LOAD SILENT? iri ( INTO GraphRef )?
         String source = update.getSource();
         Node dest = update.getDest();
-        Graph graph = graph(datasetGraph, dest);
-        // We must load buffered if silent so that the dataset graph sees
-        // all or no triples/quads when there is a parse error
-        // (no nested transaction abort). 
-        boolean loadBuffered = update.getSilent() || ! 
datasetGraph.supportsTransactionAbort() ;
-        try {
-            if ( dest == null ) {
-                // LOAD SILENT? iri
-                // Quads accepted (extension).
+        executeOperation(update.isSilent(), ()->{
+            Graph graph = graphOrThrow(datasetGraph, dest);
+            // We must load buffered if silent so that the dataset graph sees
+            // all or no triples/quads when there is a parse error
+            // (no nested transaction abort).
+            try {
+                boolean loadBuffered = update.isSilent() || ! 
datasetGraph.supportsTransactionAbort();
+                if ( dest == null ) {
+                    // LOAD SILENT? iri
+                    // Quads accepted (extension).
+                    if ( loadBuffered ) {
+                        DatasetGraph dsg2 = DatasetGraphFactory.create();
+                        RDFDataMgr.read(dsg2, source);
+                        dsg2.find().forEachRemaining(datasetGraph::add);
+                    } else {
+                        RDFDataMgr.read(datasetGraph, source);
+                    }
+                    return;
+                }
+                // LOAD SILENT? iri INTO GraphRef
+                // Load triples. To give a decent error message and also not 
have the usual
+                // parser behaviour of just selecting default graph triples 
when the
+                // destination is a graph, we need to do the same steps as 
RDFParser.parseURI,
+                // with different checking.
+                TypedInputStream input = RDFDataMgr.open(source);
+                String contentType = input.getContentType();
+                Lang lang = RDFDataMgr.determineLang(source, contentType, 
Lang.TTL);
+                if ( lang == null )
+                    throw new UpdateException("Failed to determine the syntax 
for '"+source+"'");
+                if ( ! RDFLanguages.isTriples(lang) )
+                    throw new UpdateException("Attempt to load quads into a 
graph");
+                RDFParser parser = RDFParser
+                        .source(input.getInputStream())
+                        .forceLang(lang)
+                        .build();
                 if ( loadBuffered ) {
-                    DatasetGraph dsg2 = DatasetGraphFactory.create();
-                    RDFDataMgr.read(dsg2, source);
-                    dsg2.find().forEachRemaining(datasetGraph::add);
+                    Graph g = GraphFactory.createGraphMem();
+                    parser.parse(g);
+                    GraphUtil.addInto(graph, g);
                 } else {
-                    RDFDataMgr.read(datasetGraph, source);
+                    parser.parse(graph);
+                }
+            } catch (RiotException ex) {
+                if ( !update.isSilent() ) {
+                    throw new UpdateException("Failed to LOAD '" + source + "' 
:: " + ex.getMessage(), ex);
                 }
-                return ;
-            }
-            // LOAD SILENT? iri INTO GraphRef
-            // Load triples. To give a decent error message and also not have 
the usual
-            // parser behaviour of just selecting default graph triples when 
the
-            // destination is a graph, we need to do the same steps as 
RDFParser.parseURI,
-            // with different checking.
-            TypedInputStream input = RDFDataMgr.open(source);
-            String contentType = input.getContentType();
-            Lang lang = RDFDataMgr.determineLang(source, contentType, 
Lang.TTL); 
-            if ( lang == null )
-                throw new UpdateException("Failed to determine the syntax for 
'"+source+"'");
-            if ( ! RDFLanguages.isTriples(lang) )
-                throw new UpdateException("Attempt to load quads into a 
graph");
-            RDFParser parser = RDFParser
-                .source(input.getInputStream())
-                .forceLang(lang)
-                .build();
-            if ( loadBuffered ) {
-                Graph g = GraphFactory.createGraphMem();
-                parser.parse(g);
-                GraphUtil.addInto(graph, g);
-            } else {
-                parser.parse(graph);
-            }
-        } catch (RuntimeException ex) {
-            if ( !update.getSilent() ) {
-                if ( ex instanceof UpdateException )
-                    throw ex;
-                throw new UpdateException("Failed to LOAD '" + source + "' :: 
" + ex.getMessage(), ex);
             }
-        }
+        });
     }
 
     @Override
     public void visit(UpdateAdd update) {
-        // ADD SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
-        if ( !validBinaryGraphOp(update) )
-            return;
-        if ( update.getSrc().equals(update.getDest()) )
-            return;
-        // Different source and destination.
-        gsAddTriples(datasetGraph, update.getSrc(), update.getDest());
+        executeOperation(update.isSilent(), ()->{
+            // ADD SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
+            validateBinaryGraphOp(update);
+            if ( update.getSrc().equals(update.getDest()) )
+                return;
+            // Different source and destination.
+            gsAddTriples(datasetGraph, update.getSrc(), update.getDest());
+        });
     }
 
     @Override
     public void visit(UpdateCopy update) {
-        // COPY SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
-        if ( !validBinaryGraphOp(update) )
-            return;
-        if ( update.getSrc().equals(update.getDest()) )
-            return;
-        gsCopy(datasetGraph, update.getSrc(), update.getDest(), 
update.getSilent());
+        executeOperation(update.isSilent(), ()->{
+            // COPY SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
+            validateBinaryGraphOp(update);
+            if ( update.getSrc().equals(update.getDest()) )
+                // Same source and destination.
+                return;
+            // Different source and destination.
+            gsCopy(datasetGraph, update.getSrc(), update.getDest());
+        });
     }
 
     @Override
     public void visit(UpdateMove update) {
-        // MOVE SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
-        if ( !validBinaryGraphOp(update) )
-            return;
-        if ( update.getSrc().equals(update.getDest()) )
-            return;
-        // MOVE (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
-        // Difefrent source and destination.
-        gsCopy(datasetGraph, update.getSrc(), update.getDest(), 
update.getSilent());
-        gsDrop(datasetGraph, update.getSrc(), true);
-    }
-
-    private boolean validBinaryGraphOp(UpdateBinaryOp update) {
+        executeOperation(update.isSilent(), ()->{
+            // MOVE SILENT? (DEFAULT or GRAPH) TO (DEFAULT or GRAPH)
+            validateBinaryGraphOp(update);
+            if ( update.getSrc().equals(update.getDest()) )
+                // Same source and destination.
+                return;
+            // Different source and destination.
+            gsCopy(datasetGraph, update.getSrc(), update.getDest());
+            gsDrop(datasetGraph, update.getSrc());
+        });
+    }
+
+    /** Test whether the operation of G1 to G2 is valid */
+    private void validateBinaryGraphOp(UpdateBinaryOp update) {
         if ( update.getSrc().isDefault() )
-            return true;
-
+            return;
         if ( update.getSrc().isOneNamedGraph() ) {
             Node gn = update.getSrc().getGraph();
-            if ( !datasetGraph.containsGraph(gn) ) {
-                if ( !update.getSilent() )
-                    error("No such graph: " + gn);
-                return false;
-            }
-            return true;
+            if ( !datasetGraph.containsGraph(gn) )
+                throw errorEx("No such graph: " + gn);
         }
-        error("Invalid source target for oepration; " + update.getSrc());
-        return false;
+        throw errorEx("Invalid source target for operation; " + 
update.getSrc());
     }
 
     // ----
     // Core operations
     /** Copy from src to dst : copy overwrites (= deletes) the old contents */
-    protected static void gsCopy(DatasetGraph dsg, Target src, Target dest, 
boolean isSilent)
-    {
-        if ( dest.equals(src) ) 
-            return ;
-        gsClear(dsg, dest, true) ;
-        gsAddTriples(dsg, src, dest) ;
+    protected static void gsCopy(DatasetGraph dsg, Target src, Target dest) {
+        if ( dest.equals(src) )
+            return;
+        gsClear(dsg, dest);
+        gsAddTriples(dsg, src, dest);
     }
 
     /** Add triples from src to dest */
     protected static void gsAddTriples(DatasetGraph dsg, Target src, Target 
dest) {
-        Graph gSrc = graph(dsg, src);
-        Graph gDest = graph(dsg, dest);
+        Graph gSrc = graphOrThrow(dsg, src);
+        Graph gDest = graphOrThrow(dsg, dest);
         GraphOps.addAll(gDest, gSrc.find());
     }
 
     /** Clear target */
-    protected static void gsClear(DatasetGraph dsg, Target target, boolean 
isSilent) {
+    protected static void gsClear(DatasetGraph dsg, Target target) {
         // No create - we tested earlier.
-        Graph g = graph(dsg, target);
+        Graph g = graphOrThrow(dsg, target);
         g.clear();
     }
 
     /** Remove the target graph */
-    protected static void gsDrop(DatasetGraph dsg, Target target, boolean 
isSilent) {
+    protected static void gsDrop(DatasetGraph dsg, Target target) {
         if ( target.isDefault() )
             dsg.getDefaultGraph().clear();
         else
             dsg.removeGraph(target.getGraph());
     }
-    
+
     // ----
-    
+
     @Override
     public void visit(UpdateDataInsert update) {
         for ( Quad quad : update.getQuads() )
@@ -285,16 +303,16 @@ public class UpdateEngineWorker implements UpdateVisitor
 
     @Override
     public void visit(UpdateDeleteWhere update) {
-        List<Quad> quads = update.getQuads() ;
+        List<Quad> quads = update.getQuads();
         // Removed from SPARQL : Convert bNodes to named variables first.
-        //quads = convertBNodesToVariables(quads) ;
-        
+        //quads = convertBNodesToVariables(quads);
+
         // Convert quads to a pattern.
-        Element el = elementFromQuads(quads) ;
-        
+        Element el = elementFromQuads(quads);
+
         // Decided to serialize the bindings, but could also have decided to
         // serialize the quads after applying the template instead.
-        
+
         ThresholdPolicy<Binding> policy = 
ThresholdPolicyFactory.policyFromContext(datasetGraph.getContext());
         DataBag<Binding> db = BagFactory.newDefaultBag(policy, 
SerializationFactoryFinder.bindingSerializationFactory());
         try {
@@ -310,7 +328,7 @@ public class UpdateEngineWorker implements UpdateVisitor
             db.close();
         }
     }
-    
+
     @Override
     public void visit(UpdateModify update) {
         Node withGraph = update.getWithIRI();
@@ -332,22 +350,22 @@ public class UpdateEngineWorker implements UpdateVisitor
             //   but the rewrite with ElementNamedGraph is closer to SPARQL.
             // Better, treat as
             // WHERE { GRAPH <with> { ... } }
-            // This is the SPARQL wording (which is a bit loose).  
-            elt = new ElementNamedGraph(withGraph, elt) ;
+            // This is the SPARQL wording (which is a bit loose).
+            elt = new ElementNamedGraph(withGraph, elt);
         }
 
         // WITH :
         // The quads from deletion/insertion are altered when streamed
-        // into the templates later on. 
-        
+        // into the templates later on.
+
         // -------------------
-        
+
         if ( dsg == null )
-            dsg = datasetGraph ;
-        
-        Query query = elementToQuery(elt) ;
+            dsg = datasetGraph;
+
+        Query query = elementToQuery(elt);
         ThresholdPolicy<Binding> policy = 
ThresholdPolicyFactory.policyFromContext(datasetGraph.getContext());
-        DataBag<Binding> db = BagFactory.newDefaultBag(policy, 
SerializationFactoryFinder.bindingSerializationFactory()) ;
+        DataBag<Binding> db = BagFactory.newDefaultBag(policy, 
SerializationFactoryFinder.bindingSerializationFactory());
         try {
             Iterator<Binding> bindings = evalBindings(query, dsg, 
inputBinding, context);
 
@@ -382,7 +400,7 @@ public class UpdateEngineWorker implements UpdateVisitor
     }
 
     private Graph graphOrDummy(DatasetGraph dsg, Node gn) {
-        Graph g = graph(datasetGraph, gn);
+        Graph g = graphOrThrow(datasetGraph, gn);
         if ( g == null )
             g = GraphFactory.createGraphMem();
         return g;
@@ -412,7 +430,7 @@ public class UpdateEngineWorker implements UpdateVisitor
     }
 
     // JENA-1059 : optimization : process templates for ground triples and do 
these once.
-    // execDelete ; execInsert
+    // execDelete; execInsert
     // Quads involving only IRIs and literals do not change from binding to
     // binding so any inserts, rather than repeatedly if they are going to be
     // done at all. Note bNodes (if legal at this point) change from template
@@ -420,20 +438,20 @@ public class UpdateEngineWorker implements UpdateVisitor
     /**
      * Split quads into ground terms (no variables) and templated quads.
      * @param quads
-     * @return Pair of (ground quads, templated quads) 
+     * @return Pair of (ground quads, templated quads)
      */
     private static Pair<List<Quad>, List<Quad>> split(Collection<Quad> quads) {
         // Guess size.
-        //    Pre-size in case large (i.e. 10K+). 
-        List<Quad> constQuads = new ArrayList<>(quads.size()) ;
+        //    Pre-size in case large (i.e. 10K+).
+        List<Quad> constQuads = new ArrayList<>(quads.size());
         //    ... in which case we assume the templated triples are small / 
non-existent.
-        List<Quad> templateQuads = new ArrayList<>() ;
+        List<Quad> templateQuads = new ArrayList<>();
         quads.forEach((q)-> {
             if ( constQuad(q))
-                constQuads.add(q) ;
+                constQuads.add(q);
             else
-                templateQuads.add(q) ;
-        }) ;
+                templateQuads.add(q);
+        });
         return Pair.create(constQuads, templateQuads);
     }
 
@@ -441,41 +459,41 @@ public class UpdateEngineWorker implements UpdateVisitor
         return constTerm(quad.getGraph())     && constTerm(quad.getSubject()) 
&&
                constTerm(quad.getPredicate()) && constTerm(quad.getObject());
     }
-    
+
     private static boolean constTerm(Node n) {
-        return n.isURI() || n.isLiteral() ;
+        return n.isURI() || n.isLiteral();
     }
 
     protected static void execDelete(DatasetGraph dsg, List<Quad> quads, Node 
dftGraph, Iterator<Binding> bindings) {
-        Pair<List<Quad>, List<Quad>> p = split(quads) ;
-        execDelete(dsg, p.getLeft(), p.getRight(), dftGraph, bindings) ;
+        Pair<List<Quad>, List<Quad>> p = split(quads);
+        execDelete(dsg, p.getLeft(), p.getRight(), dftGraph, bindings);
     }
-    
+
     protected static void execDelete(DatasetGraph dsg, List<Quad> onceQuads, 
List<Quad> templateQuads, Node dftGraph, Iterator<Binding> bindings) {
         if ( onceQuads != null && bindings.hasNext() ) {
-            onceQuads = remapDefaultGraph(onceQuads, dftGraph) ;
-            onceQuads.forEach(q->deleteFromDatasetGraph(dsg, q)) ;
+            onceQuads = remapDefaultGraph(onceQuads, dftGraph);
+            onceQuads.forEach(q->deleteFromDatasetGraph(dsg, q));
         }
-        Iterator<Quad> it = template(templateQuads, dftGraph, bindings) ;
+        Iterator<Quad> it = template(templateQuads, dftGraph, bindings);
         if ( it == null )
-            return ;
-        it.forEachRemaining(q->deleteFromDatasetGraph(dsg, q)) ;
+            return;
+        it.forEachRemaining(q->deleteFromDatasetGraph(dsg, q));
     }
 
     protected static void execInsert(DatasetGraph dsg, List<Quad> quads, Node 
dftGraph, Iterator<Binding> bindings) {
-        Pair<List<Quad>, List<Quad>> p = split(quads) ;
-        execInsert(dsg, p.getLeft(), p.getRight(), dftGraph, bindings) ;
+        Pair<List<Quad>, List<Quad>> p = split(quads);
+        execInsert(dsg, p.getLeft(), p.getRight(), dftGraph, bindings);
     }
-    
+
     protected static void execInsert(DatasetGraph dsg, List<Quad> onceQuads, 
List<Quad> templateQuads, Node dftGraph, Iterator<Binding> bindings) {
         if ( onceQuads != null && bindings.hasNext() ) {
-            onceQuads = remapDefaultGraph(onceQuads, dftGraph) ;
-            onceQuads.forEach((q)->addToDatasetGraph(dsg, q)) ;
+            onceQuads = remapDefaultGraph(onceQuads, dftGraph);
+            onceQuads.forEach((q)->addToDatasetGraph(dsg, q));
         }
-        Iterator<Quad> it = template(templateQuads, dftGraph, bindings) ;
+        Iterator<Quad> it = template(templateQuads, dftGraph, bindings);
         if ( it == null )
-            return ;
-        it.forEachRemaining((q)->addToDatasetGraph(dsg, q)) ;
+            return;
+        it.forEachRemaining((q)->addToDatasetGraph(dsg, q));
     }
 
     // Catch all individual adds of quads
@@ -484,7 +502,7 @@ public class UpdateEngineWorker implements UpdateVisitor
         if ( quad.isLegalAsData() )
             datasetGraph.add(quad);
         // Else drop.
-        // Log.warn(UpdateEngineWorker.class, "Bad quad as data: "+quad) ;
+        // Log.warn(UpdateEngineWorker.class, "Bad quad as data: "+quad);
     }
 
     // Catch all individual deletes of quads
@@ -525,23 +543,43 @@ public class UpdateEngineWorker implements UpdateVisitor
         return toReturn;
     }
 
-    protected static Graph graph(DatasetGraph datasetGraph, Node gn) {
-        if ( gn == null || gn == Quad.defaultGraphNodeGenerated )
+    /**
+     * Execute.
+     * <br/>
+     * Return true if successful.
+     * <br/>
+     *
+     * Otherwise if not silent: throw UpdateException, if silent, return false
+     */
+    private boolean executeOperation(boolean isSilent, Runnable action) {
+        try {
+            action.run();
+            return true;
+        } catch (UpdateException ex) {
+            if ( isSilent )
+                return false;
+            throw ex;
+        }
+    }
+
+    protected static Graph graphOrThrow(DatasetGraph datasetGraph, Node gn) {
+        if ( gn == null || Quad.isDefaultGraph(gn) )
             return datasetGraph.getDefaultGraph();
-        else
-            return datasetGraph.getGraph(gn);
+        Graph g = datasetGraph.getGraph(gn);
+        if ( g == null )
+            throw errorEx("No such graph in this dataset: "+gn);
+        return g;
     }
 
-    protected static Graph graph(DatasetGraph datasetGraph, Target target) {
+    protected static Graph graphOrThrow(DatasetGraph datasetGraph, Target 
target) {
         if ( target.isDefault() )
             return datasetGraph.getDefaultGraph();
         if ( target.isOneNamedGraph() )
-            return graph(datasetGraph, target.getGraph());
-        error("Target does not name one graph: " + target);
-        return null;
+            return graphOrThrow(datasetGraph, target.getGraph());
+        throw errorEx("Target does not name one graph: " + target);
     }
 
-    protected static void error(String msg) {
-        throw new UpdateException(msg);
+    protected static UpdateException errorEx(String msg) {
+        return new UpdateException(msg);
     }
 }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateBinaryOp.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateBinaryOp.java
index 73b5446..91b4644 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateBinaryOp.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateBinaryOp.java
@@ -32,7 +32,7 @@ public abstract class UpdateBinaryOp extends Update
     {
         checkTarget(src) ;
         checkTarget(dest) ;
-        this.src = src ; 
+        this.src = src ;
         this.dest = dest ;
         this.silent = silent ;
     }
@@ -40,15 +40,20 @@ public abstract class UpdateBinaryOp extends Update
     private static void checkTarget(Target target)
     {
         if ( ! target.isDefault() && ! target.isOneNamedGraph() )
-            throw new ARQException("Illegal target: must identify a single 
graph: "+target) ; 
+            throw new ARQException("Illegal target: must identify a single 
graph: "+target) ;
     }
 
     public Target getSrc()      { return src ; }
 
     public Target getDest()     { return dest ; }
-    
-    public boolean getSilent()  { return silent ; }
-    
+
+    /**
+     * @deprecated use {@link #isSilent}
+     */
+    @Deprecated
+    public boolean getSilent()  { return isSilent() ; }
+    public boolean isSilent()   { return silent ; }
+
     @Override
     final
     public boolean equalTo(Update obj, NodeIsomorphismMap isoMap) {
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateLoad.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateLoad.java
index efb605b..27e2665 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateLoad.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateLoad.java
@@ -20,61 +20,62 @@ package org.apache.jena.sparql.modify.request;
 
 import java.util.Objects;
 
-import org.apache.jena.graph.Node ;
-import org.apache.jena.graph.NodeFactory ;
-import org.apache.jena.sparql.util.Iso ;
-import org.apache.jena.sparql.util.NodeIsomorphismMap ;
-import org.apache.jena.update.Update ;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.util.Iso;
+import org.apache.jena.sparql.util.NodeIsomorphismMap;
+import org.apache.jena.update.Update;
 
 public class UpdateLoad extends Update
 {
-    private final String source ;
-    private final Node dest ;
-    private boolean silent ;
-    
+    private final String source;
+    private final Node dest;
+    private boolean silent;
 
-    public UpdateLoad(String source, String dest)
-    {
-        this(source, NodeFactory.createURI(dest), false) ;
+
+    public UpdateLoad(String source, String dest) {
+        this(source, NodeFactory.createURI(dest), false);
     }
-    
-    public UpdateLoad(String source, String dest, boolean silent)
-    {
-        this(source, NodeFactory.createURI(dest), silent) ;
+
+    public UpdateLoad(String source, String dest, boolean silent) {
+        this(source, NodeFactory.createURI(dest), silent);
     }
 
-    public UpdateLoad(String source, Node dest)
-    {
-        this(source, dest, false) ;
+    public UpdateLoad(String source, Node dest) {
+        this(source, dest, false);
     }
 
-    public UpdateLoad(String source, Node dest, boolean silent)
-    {
-        this.source = source ;
-        this.dest = dest ;
-        this.silent = silent ;
+    public UpdateLoad(String source, Node dest, boolean silent) {
+        this.source = source;
+        this.dest = dest;
+        this.silent = silent;
     }
 
-    public String  getSource()      { return source ; }
-    public Node    getDest()        { return dest ; }
-    public boolean getSilent()      { return silent ; }
+    public String  getSource()      { return source; }
+    public Node    getDest()        { return dest; }
+    /**
+     * @deprecated use {@link #isSilent}
+     */
+    @Deprecated
+    public boolean getSilent()      { return isSilent(); }
+    public boolean isSilent()       { return silent; }
 
     @Override
     public void visit(UpdateVisitor visitor)
-    { visitor.visit(this) ; }
+    { visitor.visit(this); }
 
     @Override
     public boolean equalTo(Update obj, NodeIsomorphismMap isoMap) {
         if (this == obj)
-            return true ;
+            return true;
         if (obj == null)
-            return false ;
+            return false;
         if (getClass() != obj.getClass())
-            return false ;
-        UpdateLoad other = (UpdateLoad)obj ;
-        return 
+            return false;
+        UpdateLoad other = (UpdateLoad)obj;
+        return
             silent == other.silent &&
                Objects.equals(source, other.source) &&
-            Iso.nodeIso(dest, other.dest, isoMap) ;
+            Iso.nodeIso(dest, other.dest, isoMap);
     }
 }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
index 7a941a1..daa7618 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/modify/request/UpdateWriterVisitor.java
@@ -95,7 +95,7 @@ public class UpdateWriterVisitor implements UpdateVisitor {
         out.ensureStartOfLine();
         out.print("LOAD");
         out.print(" ");
-        if ( update.getSilent() )
+        if ( update.isSilent() )
             out.print("SILENT ");
 
         outputStringAsURI(update.getSource());
@@ -126,7 +126,7 @@ public class UpdateWriterVisitor implements UpdateVisitor {
 
     protected void printUpdate2(UpdateBinaryOp update, String name) {
         out.print(name);
-        if ( update.getSilent() )
+        if ( update.isSilent() )
             out.print(" SILENT");
         out.print(" ");
         printTargetUpdate2(update.getSrc());
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/AbstractTestUpdateGraphMgt.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/AbstractTestUpdateGraphMgt.java
index cb4e25d..c2c50f7 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/AbstractTestUpdateGraphMgt.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/AbstractTestUpdateGraphMgt.java
@@ -43,10 +43,6 @@ public abstract class AbstractTestUpdateGraphMgt extends 
AbstractTestUpdateBase
         Update u = new UpdateCreate(graphIRI);
 
         UpdateAction.execute(u, gStore);
-        // Only true if a graph caching layer exists.
-        // JENA-1068 removed that layer
-        // (which wasn't safe anyway - it only "existed" in the memory cache)
-// assertTrue(gStore.containsGraph(graphIRI)) ;
         assertTrue(graphEmpty(gStore.getGraph(graphIRI)));
 
         // With "auto SILENT" then these aren't errors.
@@ -83,8 +79,6 @@ public abstract class AbstractTestUpdateGraphMgt extends 
AbstractTestUpdateBase
 
         u = new UpdateCreate(graphIRI, true);
         UpdateAction.execute(u, gStore);
-        // JENA-1068
-// assertTrue(gStore.containsGraph(graphIRI)) ;
         assertTrue(graphEmpty(gStore.getGraph(graphIRI)));
 
         u = new UpdateDrop(graphIRI);
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TS_Update.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TS_Update.java
index 2d0f7a9..8f4f330 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TS_Update.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TS_Update.java
@@ -25,8 +25,10 @@ import org.junit.runners.Suite.SuiteClasses ;
 @RunWith(Suite.class)
 @SuiteClasses( {
     TestUpdateGraphMem.class
-    , TestUpdateGraphMgtMem.class
+    , TestUpdateGraphMgtTxnMem.class
+    , TestUpdateGraphMgtGeneralMem.class
     , TestUpdateOperations.class
+    , TestUpdateSilent.class
     , TestUpdateCompare.class
     , TestUpdateWriter.class
     , TestUpdateBuild.class
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtGeneralMem.java
similarity index 93%
copy from 
jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
copy to 
jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtGeneralMem.java
index 1a6bb48..e688a27 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtGeneralMem.java
@@ -21,7 +21,7 @@ package org.apache.jena.sparql.modify ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.DatasetGraphFactory ;
 
-public class TestUpdateGraphMgtMem extends AbstractTestUpdateGraphMgt {
+public class TestUpdateGraphMgtGeneralMem extends AbstractTestUpdateGraphMgt {
     @Override
     protected DatasetGraph getEmptyDatasetGraph() {
         return DatasetGraphFactory.create() ;
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtTxnMem.java
similarity index 89%
rename from 
jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
rename to 
jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtTxnMem.java
index 1a6bb48..b27dde3 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtMem.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateGraphMgtTxnMem.java
@@ -21,9 +21,9 @@ package org.apache.jena.sparql.modify ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.DatasetGraphFactory ;
 
-public class TestUpdateGraphMgtMem extends AbstractTestUpdateGraphMgt {
+public class TestUpdateGraphMgtTxnMem extends AbstractTestUpdateGraphMgt {
     @Override
     protected DatasetGraph getEmptyDatasetGraph() {
-        return DatasetGraphFactory.create() ;
+        return DatasetGraphFactory.createTxnMem() ;
     }
 }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateSilent.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateSilent.java
new file mode 100644
index 0000000..c3915c6
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/modify/TestUpdateSilent.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.modify;
+
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphOne;
+import org.apache.jena.sparql.exec.UpdateExec;
+import org.apache.jena.sparql.graph.GraphFactory;
+import org.apache.jena.update.UpdateException;
+import org.junit.Test;
+
+/**
+ * Tests of SILENT. The tests are written to work aginst {@link 
DatasetGraphOne}
+ * which has the characteristic that it does not support named graphs.
+ * <p>
+ * Normally, datasets behave as if all named graphs exists for 
adding/copying/moving
+ * into graphs without needing create.
+ * <p>
+ * JENA-2293
+ */
+public class TestUpdateSilent {
+
+    private void test(String updateRequest) {
+        // A characteristic of DatasetGraphOne is that it has no
+        // named graphs and they can't be added.
+        DatasetGraph dataset = 
DatasetGraphOne.create(GraphFactory.createGraphMem());
+        UpdateExec.dataset(dataset).update(updateRequest).build().execute();
+    }
+
+    @Test(expected = UpdateException.class)
+    public void LOAD_toNonExistingGraph() {
+        test("LOAD <file:testing/Update/empty.nt> INTO GRAPH 
<http://example/no-such-graph>");
+    }
+
+    @Test
+    public void LOAD_SILENT_toNonExistingGraph() {
+        test("LOAD SILENT <file:testing/Update/empty.nt> INTO GRAPH 
<http://example/no-such-graph>");
+    }
+
+
+    @Test(expected = UpdateException.class)
+    public void CLEAR_nonExistingGraph() {
+        test("CLEAR GRAPH <http://example/no-such-graph>");
+    }
+
+    @Test
+    public void CLEAR_SILENT_nonExistingGraph() {
+        test("CLEAR SILENT GRAPH <http://example/no-such-graph>");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void CREATE_nonExistingGraph() {
+        //The target is DatasetGraphOne which does not provide named graphs.
+        test("CREATE GRAPH <file:testing/Update/empty.nt>");
+    }
+
+    @Test
+    public void CREATE_SILENT_nonExistingGraph() {
+        test("CREATE SILENT GRAPH <file:testing/Update/empty.nt>");
+    }
+
+    @Test
+    public void DROP_ofNonExistingGraph() {
+        // DROP non-existent is not an error.
+        test("DROP GRAPH <http://example/no-such-graph>");
+    }
+
+    @Test
+    public void DROP_SILENT_ofNonExistingGraph() {
+        test("DROP SILENT GRAPH <http://example/no-such-graph>");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void COPY_toNonExistingGraph() {
+        test("COPY DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test
+    public void COPY_SILENT_toNonExistingGraph() {
+        test("COPY SILENT DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void COPY_fromNonExistingGraph() {
+        test("COPY <http://example/no-such-graph> TO DEFAULT");
+    }
+
+    @Test
+    public void COPY_SILENT_fromNonExistingGraph() {
+        test("COPY SILENT <http://example/no-such-graph> TO DEFAULT");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void MOVE_toNonExistingGraph_isError() {
+        test("MOVE DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test
+    public void MOVE_SILENT_toNonExistingGraph() {
+        test("MOVE SILENT DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void MOVE_fromNonExistingGraph() {
+        test("MOVE <http://example/no-such-graph> TO DEFAULT");
+    }
+
+    @Test
+    public void MOVE_SILENT_fromNonExistingGraph() {
+        test("MOVE SILENT <http://example/no-such-graph> TO DEFAULT");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void ADD_toNonExistingGraph() {
+        test("ADD DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test
+    public void ADD_SILENT_toNonExistingGraph() {
+        test("ADD SILENT DEFAULT TO <http://example/no-such-graph>");
+    }
+
+    @Test(expected = UpdateException.class)
+    public void ADD_fromNonExistingGraph() {
+        test("ADD <http://example/no-such-graph> TO DEFAULT");
+    }
+
+    @Test
+    public void ADD_SILENT_fromNonExistingGraph() {
+        test("ADD SILENT <http://example/no-such-graph> TO DEFAULT");
+    }
+}
diff --git a/jena-arq/testing/Update/empty.nt b/jena-arq/testing/Update/empty.nt
new file mode 100644
index 0000000..e69de29

Reply via email to