afs commented on code in PR #2822:
URL: https://github.com/apache/jena/pull/2822#discussion_r1832829076


##########
jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionCancel.java:
##########
@@ -229,6 +247,111 @@ public void test_cancel_json() {
         cancellationTest("JSON {\":a\": \"b\"} WHERE {}", 
exec->exec.execJson().get(0));
     }
 
+    /**
+     * Registers the function <urn:cancelSignal> which returns its value if 
present.
+     * A RuntimeException is raised if there is no cancel signal in the 
execution context.
+     *
+     * Note: Prior to jena-5.3.0 the cancel signal was only set when 
configuring a timeout.
+     * However, implementations may also want to react to manual abort via 
{@link QueryExec#abort()}.
+     */
+    public static FunctionRegistry 
registerCancelSignalFunction(FunctionRegistry fnReg) {
+        fnReg.put("urn:cancelSignal", iri -> new FunctionBase0() {
+            @Override
+            protected NodeValue exec(List<NodeValue> args, FunctionEnv env) {
+                ExecutionContext execCxt = (ExecutionContext)env;
+                AtomicBoolean cancelSignal = execCxt.getCancelSignal();
+                if (cancelSignal == null) {
+                    throw new RuntimeException("No cancel signal in execution 
context.");
+                }
+                return NodeValue.makeBoolean(cancelSignal.get());
+            }
+
+            @Override
+            public NodeValue exec() {
+                throw new IllegalStateException("Should never be called");
+            }
+        });
+
+        return fnReg;
+    }
+
+    /** Set cancel signal function via {@link QueryExecBuilder#set(Symbol, 
Object). */

Review Comment:
   ```suggestion
       /** Set cancel signal function via {@link QueryExecBuilder#set(Symbol, 
Object)}. */
   ```



##########
jena-arq/src/main/java/org/apache/jena/http/sys/ExecUpdateHTTPBuilder.java:
##########
@@ -275,25 +276,25 @@ public Y context(Context context) {
         if ( context == null )
             return thisBuilder();
         ensureContext();
-        this.context.setAll(context);
+        this.contextAcc.context(context);
         return thisBuilder();
     }
 
     public Y set(Symbol symbol, Object value) {
         ensureContext();
-        this.context.set(symbol, value);
+        this.contextAcc.set(symbol, value);
         return thisBuilder();
     }
 
     public Y set(Symbol symbol, boolean value) {
         ensureContext();
-        this.context.set(symbol, value);
+        this.contextAcc.set(symbol, value);
         return thisBuilder();
     }
 
     private void ensureContext() {
-        if ( context == null )
-            context = Context.create();
+//        if ( context == null )
+//            context = Context.create();

Review Comment:
   Remove. 
   
   Changing to  `ContextAccumulator` is an improvement.



##########
jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateEngineWorker.java:
##########
@@ -537,7 +537,7 @@ protected static Iterator<Binding> evalBindings(Query 
query, DatasetGraph dsg, B
         }
 
         // Not QueryExecDataset.dataset(...) because of initialBinding.
-        QueryExecDatasetBuilder builder = 
QueryExecDatasetBuilder.create().dataset(dsg).query(query);
+        QueryExecDatasetBuilder builder = 
QueryExecDatasetBuilder.create().dataset(dsg).query(query).context(context);

Review Comment:
   See comment on the main PR. This is potentially something that impacts users 
and we need to consider migration.



##########
jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionCancel.java:
##########
@@ -229,6 +247,111 @@ public void test_cancel_json() {
         cancellationTest("JSON {\":a\": \"b\"} WHERE {}", 
exec->exec.execJson().get(0));
     }
 
+    /**
+     * Registers the function <urn:cancelSignal> which returns its value if 
present.
+     * A RuntimeException is raised if there is no cancel signal in the 
execution context.
+     *
+     * Note: Prior to jena-5.3.0 the cancel signal was only set when 
configuring a timeout.
+     * However, implementations may also want to react to manual abort via 
{@link QueryExec#abort()}.
+     */
+    public static FunctionRegistry 
registerCancelSignalFunction(FunctionRegistry fnReg) {
+        fnReg.put("urn:cancelSignal", iri -> new FunctionBase0() {
+            @Override
+            protected NodeValue exec(List<NodeValue> args, FunctionEnv env) {
+                ExecutionContext execCxt = (ExecutionContext)env;
+                AtomicBoolean cancelSignal = execCxt.getCancelSignal();
+                if (cancelSignal == null) {
+                    throw new RuntimeException("No cancel signal in execution 
context.");
+                }
+                return NodeValue.makeBoolean(cancelSignal.get());
+            }
+
+            @Override
+            public NodeValue exec() {
+                throw new IllegalStateException("Should never be called");
+            }
+        });
+
+        return fnReg;
+    }
+
+    /** Set cancel signal function via {@link QueryExecBuilder#set(Symbol, 
Object). */
+    @Test
+    public void test_cancel_signal_query_1() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }")
+                .set(ARQConstants.registryFunctions, fnReg)
+                .build()) {
+            Assert.assertEquals(1, 
ResultSetFormatter.consume(ResultSet.adapt(qe.select())));
+        }
+    }
+
+    /** Set cancel signal function via {@link 
QueryExecBuilder#context(Context). */

Review Comment:
   Fix for Javadoc warnings:
   
   ```suggestion
       /** Set cancel signal function via {@link 
QueryExecBuilder#context(Context)}. */
   ```
   
   also import `QueryExecBuilder`.



##########
jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java:
##########
@@ -457,6 +457,23 @@ private void startQueryIteratorActual() {
 
         execInit();
 
+        // JENA-2821 - Unconditionally provide a cancel signal because manual 
abort via QueryExec.abort()
+        //             may be triggered any time, even if no timeouts were 
configured.
+        //             Prior to this issue, the cancel signal was only 
provided when timeouts were configured.
+
+        // The following note is older:
+        //   JENA-2141 - the timeout can go off while building the query 
iterator structure.
+        //   In this case, use a signal passed through the context.
+        //   We don't know if getPlan().iterator() does a lot of work or not
+        //   (ideally it shouldn't start executing the query but in some 
sub-systems
+        //   it might be necessary)
+        //
+        //   This applies to the time to first result because to get the first 
result, the
+        //   queryIterator must have been built. So it does not apply for the 
second
+        //   stage of N,-1 or N,M.
+        context.set(ARQConstants.symCancelQuery, cancelSignal);
+
+

Review Comment:
   Remove one empty line.



##########
jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionCancel.java:
##########
@@ -229,6 +247,111 @@ public void test_cancel_json() {
         cancellationTest("JSON {\":a\": \"b\"} WHERE {}", 
exec->exec.execJson().get(0));
     }
 
+    /**
+     * Registers the function <urn:cancelSignal> which returns its value if 
present.
+     * A RuntimeException is raised if there is no cancel signal in the 
execution context.
+     *
+     * Note: Prior to jena-5.3.0 the cancel signal was only set when 
configuring a timeout.
+     * However, implementations may also want to react to manual abort via 
{@link QueryExec#abort()}.
+     */
+    public static FunctionRegistry 
registerCancelSignalFunction(FunctionRegistry fnReg) {
+        fnReg.put("urn:cancelSignal", iri -> new FunctionBase0() {
+            @Override
+            protected NodeValue exec(List<NodeValue> args, FunctionEnv env) {
+                ExecutionContext execCxt = (ExecutionContext)env;
+                AtomicBoolean cancelSignal = execCxt.getCancelSignal();
+                if (cancelSignal == null) {
+                    throw new RuntimeException("No cancel signal in execution 
context.");
+                }
+                return NodeValue.makeBoolean(cancelSignal.get());
+            }
+
+            @Override
+            public NodeValue exec() {
+                throw new IllegalStateException("Should never be called");
+            }
+        });
+
+        return fnReg;
+    }
+
+    /** Set cancel signal function via {@link QueryExecBuilder#set(Symbol, 
Object). */
+    @Test
+    public void test_cancel_signal_query_1() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }")
+                .set(ARQConstants.registryFunctions, fnReg)
+                .build()) {
+            Assert.assertEquals(1, 
ResultSetFormatter.consume(ResultSet.adapt(qe.select())));
+        }
+    }
+
+    /** Set cancel signal function via {@link 
QueryExecBuilder#context(Context). */
+    @Test
+    public void test_cancel_signal_query_2() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        Context cxt = ARQ.getContext().copy();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        FunctionRegistry.set(cxt, fnReg);
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }").context(cxt).build()) {
+            Assert.assertEquals(1, 
ResultSetFormatter.consume(ResultSet.adapt(qe.select())));
+        }
+    }
+
+    /** Set cancel signal function via {@link QueryExec#getContext()}. */
+    @Test
+    public void test_cancel_signal_query_3() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }").build()) {
+            FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+            FunctionRegistry.set(qe.getContext(), fnReg);
+            ResultSetFormatter.consume(ResultSet.adapt(qe.select()));
+        }
+    }
+
+    /** Set cancel signal function via {@link UpdateExecBuilder#set(Symbol, 
Object)}. */
+    @Test
+    public void test_cancel_signal_update_1() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        UpdateExec ue = UpdateExec.dataset(dsg)
+                .update("INSERT { <s> <p> ?o } WHERE { 
BIND(<urn:cancelSignal>() AS ?o) }")
+                .set(ARQConstants.registryFunctions, fnReg)
+                .build();
+        ue.execute();
+        Assert.assertEquals(1, Iter.count(dsg.find()));
+    }
+
+    /** Set cancel signal function via {@link 
UpdateExecBuilder#context(Context). */

Review Comment:
   ```suggestion
       /** Set cancel signal function via {@link 
UpdateExecBuilder#context(Context)}. */
   ```



##########
jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java:
##########
@@ -457,6 +457,23 @@ private void startQueryIteratorActual() {
 
         execInit();
 
+        // JENA-2821 - Unconditionally provide a cancel signal because manual 
abort via QueryExec.abort()
+        //             may be triggered any time, even if no timeouts were 
configured.
+        //             Prior to this issue, the cancel signal was only 
provided when timeouts were configured.
+
+        // The following note is older:
+        //   JENA-2141 - the timeout can go off while building the query 
iterator structure.
+        //   In this case, use a signal passed through the context.
+        //   We don't know if getPlan().iterator() does a lot of work or not
+        //   (ideally it shouldn't start executing the query but in some 
sub-systems
+        //   it might be necessary)
+        //
+        //   This applies to the time to first result because to get the first 
result, the
+        //   queryIterator must have been built. So it does not apply for the 
second
+        //   stage of N,-1 or N,M.

Review Comment:
   Remove.



##########
jena-arq/src/main/java/org/apache/jena/sparql/exec/http/UpdateExecHTTP.java:
##########
@@ -79,10 +79,10 @@ public static UpdateExecHTTPBuilder service(String 
endpointURL) {
         this.sendMode = sendMode;
     }
 
-//    @Override
-//    public Context getContext() {
-//        return null;
-//    }
+    @Override
+    public Context getContext() {
+        return context;
+    }
 //

Review Comment:
   Since this class is changing, please remove commented out 
`getDatasetGraph()`.



##########
jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionCancel.java:
##########
@@ -229,6 +247,111 @@ public void test_cancel_json() {
         cancellationTest("JSON {\":a\": \"b\"} WHERE {}", 
exec->exec.execJson().get(0));
     }
 
+    /**
+     * Registers the function <urn:cancelSignal> which returns its value if 
present.
+     * A RuntimeException is raised if there is no cancel signal in the 
execution context.
+     *
+     * Note: Prior to jena-5.3.0 the cancel signal was only set when 
configuring a timeout.
+     * However, implementations may also want to react to manual abort via 
{@link QueryExec#abort()}.

Review Comment:
   I'm not a fan of "historical" documentation in javadoc. That just sticks 
around for a very long time. The metric I like is "will it be useful in 5 years 
time?". I don't think that passes that test.
   
   I suggest either removing this part or putting in a non-javadoc comment.



##########
jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionCancel.java:
##########
@@ -229,6 +247,111 @@ public void test_cancel_json() {
         cancellationTest("JSON {\":a\": \"b\"} WHERE {}", 
exec->exec.execJson().get(0));
     }
 
+    /**
+     * Registers the function <urn:cancelSignal> which returns its value if 
present.
+     * A RuntimeException is raised if there is no cancel signal in the 
execution context.
+     *
+     * Note: Prior to jena-5.3.0 the cancel signal was only set when 
configuring a timeout.
+     * However, implementations may also want to react to manual abort via 
{@link QueryExec#abort()}.
+     */
+    public static FunctionRegistry 
registerCancelSignalFunction(FunctionRegistry fnReg) {
+        fnReg.put("urn:cancelSignal", iri -> new FunctionBase0() {
+            @Override
+            protected NodeValue exec(List<NodeValue> args, FunctionEnv env) {
+                ExecutionContext execCxt = (ExecutionContext)env;
+                AtomicBoolean cancelSignal = execCxt.getCancelSignal();
+                if (cancelSignal == null) {
+                    throw new RuntimeException("No cancel signal in execution 
context.");
+                }
+                return NodeValue.makeBoolean(cancelSignal.get());
+            }
+
+            @Override
+            public NodeValue exec() {
+                throw new IllegalStateException("Should never be called");
+            }
+        });
+
+        return fnReg;
+    }
+
+    /** Set cancel signal function via {@link QueryExecBuilder#set(Symbol, 
Object). */
+    @Test
+    public void test_cancel_signal_query_1() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }")
+                .set(ARQConstants.registryFunctions, fnReg)
+                .build()) {
+            Assert.assertEquals(1, 
ResultSetFormatter.consume(ResultSet.adapt(qe.select())));
+        }
+    }
+
+    /** Set cancel signal function via {@link 
QueryExecBuilder#context(Context). */
+    @Test
+    public void test_cancel_signal_query_2() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        Context cxt = ARQ.getContext().copy();
+        FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+        FunctionRegistry.set(cxt, fnReg);
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }").context(cxt).build()) {
+            Assert.assertEquals(1, 
ResultSetFormatter.consume(ResultSet.adapt(qe.select())));
+        }
+    }
+
+    /** Set cancel signal function via {@link QueryExec#getContext()}. */
+    @Test
+    public void test_cancel_signal_query_3() {
+        DatasetGraph dsg = DatasetGraphFactory.create();
+        try (QueryExec qe = QueryExec.dataset(dsg).query("SELECT 
(<urn:cancelSignal>() AS ?foobar) { }").build()) {
+            FunctionRegistry fnReg = registerCancelSignalFunction(new 
FunctionRegistry());
+            FunctionRegistry.set(qe.getContext(), fnReg);
+            ResultSetFormatter.consume(ResultSet.adapt(qe.select()));
+        }
+    }
+
+    /** Set cancel signal function via {@link UpdateExecBuilder#set(Symbol, 
Object)}. */
+    @Test
+    public void test_cancel_signal_update_1() {

Review Comment:
   I think these should go in new `TestUpdateExecutionCancel`.



##########
jena-arq/src/main/java/org/apache/jena/http/sys/ExecUpdateHTTPBuilder.java:
##########
@@ -275,25 +276,25 @@ public Y context(Context context) {
         if ( context == null )
             return thisBuilder();
         ensureContext();
-        this.context.setAll(context);
+        this.contextAcc.context(context);
         return thisBuilder();
     }
 
     public Y set(Symbol symbol, Object value) {
         ensureContext();
-        this.context.set(symbol, value);
+        this.contextAcc.set(symbol, value);
         return thisBuilder();
     }
 
     public Y set(Symbol symbol, boolean value) {
         ensureContext();
-        this.context.set(symbol, value);
+        this.contextAcc.set(symbol, value);
         return thisBuilder();
     }
 
     private void ensureContext() {

Review Comment:
   Yes, good idea to remove it. "Later" rarely happens.



##########
jena-arq/src/main/java/org/apache/jena/sparql/exec/UpdateExecDatasetBuilder.java:
##########
@@ -43,7 +45,11 @@ public class UpdateExecDatasetBuilder implements 
UpdateExecBuilder {
 
     private DatasetGraph dataset            = null;
     private Query        query              = null;
-    private Context      context            = null;
+    // private Context      context            = null;
+    private ContextAccumulator contextAcc =
+            ContextAccumulator.newBuilder(()->ARQ.getContext(), 
()->Context.fromDataset(dataset));
+
+

Review Comment:
   Remove one empty line.



##########
jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java:
##########
@@ -457,6 +457,23 @@ private void startQueryIteratorActual() {
 
         execInit();
 
+        // JENA-2821 - Unconditionally provide a cancel signal because manual 
abort via QueryExec.abort()
+        //             may be triggered any time, even if no timeouts were 
configured.
+        //             Prior to this issue, the cancel signal was only 
provided when timeouts were configured.
+
+        // The following note is older:
+        //   JENA-2141 - the timeout can go off while building the query 
iterator structure.
+        //   In this case, use a signal passed through the context.
+        //   We don't know if getPlan().iterator() does a lot of work or not
+        //   (ideally it shouldn't start executing the query but in some 
sub-systems
+        //   it might be necessary)
+        //
+        //   This applies to the time to first result because to get the first 
result, the
+        //   queryIterator must have been built. So it does not apply for the 
second
+        //   stage of N,-1 or N,M.
+        context.set(ARQConstants.symCancelQuery, cancelSignal);
+
+

Review Comment:
   This is, of course, the one line fix to the original issue!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to