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
commit c83e440e2277d674319a2be4d9dcb559f4b4a0a2 Author: Andy Seaborne <[email protected]> AuthorDate: Sun Feb 15 16:04:09 2026 +0000 GH-3751: OpExecutorTDB: Test input on entry. Wrap in QueryIterFailed handling --- .../sparql/engine/main/StageGeneratorGeneric.java | 48 ++-- .../apache/jena/tdb1/solver/OpExecutorTDB1.java | 163 ++++++-------- .../jena/tdb1/solver/StageGeneratorDirectTDB.java | 39 ++-- .../apache/jena/tdb1/store/Test_SPARQL_TDB1.java | 21 +- .../apache/jena/tdb2/solver/OpExecutorTDB2.java | 243 +++++++++------------ .../jena/tdb2/solver/StageGeneratorDirectTDB.java | 2 +- .../apache/jena/tdb2/store/Test_SPARQL_TDB.java | 117 +++++----- 7 files changed, 292 insertions(+), 341 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/StageGeneratorGeneric.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/StageGeneratorGeneric.java index 2ddb212fb4..14092374c6 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/StageGeneratorGeneric.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/StageGeneratorGeneric.java @@ -63,38 +63,30 @@ public class StageGeneratorGeneric implements StageGenerator { protected QueryIterator execute(BasicPattern pattern, ReorderTransformation reorder, QueryIterator input, ExecutionContext execCxt) { Explain.explain(pattern, execCxt.getContext()) ; + try { + if ( ! input.hasNext() ) + return input ; - if ( reorder != null && pattern.size() >= 2 ) { - // If pattern size is 0 or 1, nothing to do. - BasicPattern bgp2 = pattern ; + if ( reorder != null && pattern.size() >= 2 ) { + // If pattern size is 0 or 1, nothing to do. + BasicPattern bgp2 = pattern ; - // Try to ground the pattern - if ( ! input.isJoinIdentity() ) { - QueryIterPeek peek = QueryIterPeek.create(input, execCxt) ; - // And now use this one - input = peek ; - Binding b ; - // Eager access may fail e.g. due to timeout. - try { - b = peek.peek() ; - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); + // Try to ground the pattern + if ( ! input.isJoinIdentity() ) { + QueryIterPeek peek = QueryIterPeek.create(input, execCxt) ; + // And now use this one + input = peek ; + Binding b = peek.peek() ; + bgp2 = Substitute.substitute(pattern, b) ; } - bgp2 = Substitute.substitute(pattern, b) ; + ReorderProc reorderProc = reorder.reorderIndexes(bgp2) ; + pattern = reorderProc.reorder(pattern) ; } - ReorderProc reorderProc = reorder.reorderIndexes(bgp2) ; - pattern = reorderProc.reorder(pattern) ; - } else { - // Eager access may fail e.g. due to timeout. - try { - if ( ! input.hasNext() ) - return input ; - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); - } - } - Explain.explain("Reorder/generic", pattern, execCxt.getContext()) ; - return PatternMatchData.execute(execCxt.getActiveGraph(), pattern, input, null, execCxt) ; + Explain.explain("Reorder/generic", pattern, execCxt.getContext()) ; + return PatternMatchData.execute(execCxt.getActiveGraph(), pattern, input, null, execCxt) ; + } catch (Exception e) { + return new QueryIterFailed(input, execCxt, e); + } } } diff --git a/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/OpExecutorTDB1.java b/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/OpExecutorTDB1.java index 9b41596b53..a7eff6f9fc 100644 --- a/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/OpExecutorTDB1.java +++ b/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/OpExecutorTDB1.java @@ -27,7 +27,6 @@ import org.apache.jena.atlas.lib.tuple.Tuple; import org.apache.jena.atlas.logging.Log; import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; -import org.apache.jena.sparql.ARQInternalErrorException; import org.apache.jena.sparql.algebra.Op; import org.apache.jena.sparql.algebra.op.*; import org.apache.jena.sparql.algebra.optimize.TransformFilterPlacement; @@ -42,6 +41,7 @@ import org.apache.jena.sparql.engine.iterator.QueryIterPeek; import org.apache.jena.sparql.engine.main.OpExecutor; import org.apache.jena.sparql.engine.main.OpExecutorFactory; import org.apache.jena.sparql.engine.main.QC; +import org.apache.jena.sparql.engine.main.StageGenerator; import org.apache.jena.sparql.engine.main.iterator.QueryIterGraph; import org.apache.jena.sparql.engine.optimizer.reorder.ReorderProc; import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation; @@ -53,28 +53,28 @@ import org.apache.jena.tdb1.store.NodeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** TDB executor for algebra expressions. It is the standard ARQ executor - * except for basic graph patterns and filtered basic graph patterns (currently). +/** + * TDB1 executor for algebra expressions. It is the standard ARQ executor + * except for basic graph patterns and filtered basic graph patterns (currently). * - * See also: StageGeneratorDirectTDB, a non-reordering + * See also: {@link StageGeneratorDirectTDB}, a non-reordering {@link StageGenerator} */ public class OpExecutorTDB1 extends OpExecutor { private static final Logger log = LoggerFactory.getLogger(OpExecutorTDB1.class); - public final static OpExecutorFactory OpExecFactoryTDB = new OpExecutorFactory() - { + public final static OpExecutorFactory OpExecFactoryTDB = new OpExecutorFactory() { @Override - public OpExecutor create(ExecutionContext execCxt) - { return new OpExecutorTDB1(execCxt); } + public OpExecutor create(ExecutionContext execCxt) { + return new OpExecutorTDB1(execCxt); + } }; private final boolean isForTDB; // A new compile object is created for each op compilation. // So the execCxt is changing as we go through the query-compile-execute process - public OpExecutorTDB1(ExecutionContext execCxt) - { + public OpExecutorTDB1(ExecutionContext execCxt) { super(execCxt); // NB. The dataset may be a TDB one, or a general one. // Any merged union graph magic (for a TDB dataset was handled @@ -92,8 +92,7 @@ public class OpExecutorTDB1 extends OpExecutor } @Override - protected QueryIterator execute(OpFilter opFilter, QueryIterator input) - { + protected QueryIterator execute(OpFilter opFilter, QueryIterator input) { if ( ! isForTDB ) return super.execute(opFilter, input); @@ -101,8 +100,7 @@ public class OpExecutorTDB1 extends OpExecutor // Where does ARQ catch this? // (filter (bgp ...)) - if ( OpBGP.isBGP(opFilter.getSubOp()) ) - { + if ( OpBGP.isBGP(opFilter.getSubOp()) ) { // Still may be a TDB graph in a non-TDB dataset (e.g. a named model) GraphTDB graph = (GraphTDB)execCxt.getActiveGraph(); OpBGP opBGP = (OpBGP)opFilter.getSubOp(); @@ -110,24 +108,20 @@ public class OpExecutorTDB1 extends OpExecutor } // (filter (quadpattern ...)) - if ( opFilter.getSubOp() instanceof OpQuadPattern quadPattern ) - { + if ( opFilter.getSubOp() instanceof OpQuadPattern quadPattern ) { DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); - return optimizeExecuteQuads(ds, input, - quadPattern.getGraphNode(), quadPattern.getBasicPattern(), - opFilter.getExprs(), execCxt); + return optimizeExecuteQuads(ds, input, quadPattern.getGraphNode(), quadPattern.getBasicPattern(), opFilter.getExprs(), execCxt); } // (filter (anything else)) return super.execute(opFilter, input); - } + } // ---- Triple patterns @Override - protected QueryIterator execute(OpBGP opBGP, QueryIterator input) - { - if ( ! isForTDB ) + protected QueryIterator execute(OpBGP opBGP, QueryIterator input) { + if ( !isForTDB ) return super.execute(opBGP, input); GraphTDB graph = (GraphTDB)execCxt.getActiveGraph(); @@ -136,15 +130,9 @@ public class OpExecutorTDB1 extends OpExecutor } @Override - protected QueryIterator execute(OpQuadPattern quadPattern, QueryIterator input) - { - if ( ! isForTDB ) + protected QueryIterator execute(OpQuadPattern quadPattern, QueryIterator input) { + if ( !isForTDB ) return super.execute(quadPattern, input); - - // DatasetGraph dg = execCxt.getDataset(); - // if ( ! ( dg instanceof DatasetGraphTDB ) ) - // throw new InternalErrorException("Not a TDB backed dataset in quad pattern execution"); - DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); BasicPattern bgp = quadPattern.getBasicPattern(); Node gn = quadPattern.getGraphNode(); @@ -152,16 +140,15 @@ public class OpExecutorTDB1 extends OpExecutor } @Override - protected QueryIterator execute(OpGraph opGraph, QueryIterator input) - { - // Path evaluation or dataset sets which do not go straight to the DatasetGraphTDB + protected QueryIterator execute(OpGraph opGraph, QueryIterator input) { + // Path evaluation or dataset sets which do not go straight to the + // DatasetGraphTDB return new QueryIterGraph(input, opGraph, execCxt); } /** Execute a BGP (and filters) on a TDB graph, which may be in default storage or it may be a named graph */ private static QueryIterator executeBGP(GraphTDB graph, OpBGP opBGP, QueryIterator input, ExprList exprs, - ExecutionContext execCxt) - { + ExecutionContext execCxt) { DatasetGraphTDB dsgtdb = graph.getDatasetGraphTDB(); // Is it the real default graph (normal route or explicitly named)? if ( ! isDefaultGraphStorage(graph.getGraphName())) @@ -177,10 +164,11 @@ public class OpExecutorTDB1 extends OpExecutor /** Execute, with optimization, a basic graph pattern on the default graph storage */ private static QueryIterator optimizeExecuteTriples(DatasetGraphTDB dsgtdb, QueryIterator input, BasicPattern pattern, ExprList exprs, - ExecutionContext execCxt) - { - if ( ! input.hasNext() ) - return input; + ExecutionContext execCxt) { + try { + if ( ! input.hasNext() ) + return input; + } catch (Exception ex) { return new QueryIterFailed(input, execCxt, ex); } // -- Input // Must pass this iterator into the next stage. @@ -188,8 +176,7 @@ public class OpExecutorTDB1 extends OpExecutor { // Must be 2 or triples to reorder. ReorderTransformation transform = dsgtdb.getReorderTransform(); - if ( transform != null ) - { + if ( transform != null ) { QueryIterPeek peek = QueryIterPeek.create(input, execCxt); input = peek; // Must pass on try { @@ -211,24 +198,23 @@ public class OpExecutorTDB1 extends OpExecutor } /** Execute, with optimization, a quad pattern */ - private static QueryIterator optimizeExecuteQuads(DatasetGraphTDB dsgtdb, - QueryIterator input, - Node gn, BasicPattern bgp, - ExprList exprs, ExecutionContext execCxt) - { - // ---- Graph names with special meaning. + private static QueryIterator optimizeExecuteQuads(DatasetGraphTDB dsgtdb, QueryIterator input, + Node gn, BasicPattern bgp, ExprList exprs, ExecutionContext execCxt) { + try { + if ( ! input.hasNext() ) + return input; + } catch (Exception ex) { return new QueryIterFailed(input, execCxt, ex); } + // ---- Graph names with special meaning. gn = decideGraphNode(gn, execCxt); if ( gn == null ) return optimizeExecuteTriples(dsgtdb, input, bgp, exprs, execCxt); // ---- Execute quads+filters - if ( bgp.size() >= 2 ) - { + if ( bgp.size() >= 2 ) { ReorderTransformation transform = dsgtdb.getReorderTransform(); - if ( transform != null ) - { + if ( transform != null ) { QueryIterPeek peek = QueryIterPeek.create(input, execCxt); input = peek; // Original input now invalid. try { @@ -237,13 +223,6 @@ public class OpExecutorTDB1 extends OpExecutor return new QueryIterFailed(input, execCxt, e); } } - } else { - try { - if ( ! input.hasNext() ) - return input; - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); - } } if ( exprs == null ) { @@ -257,9 +236,12 @@ public class OpExecutorTDB1 extends OpExecutor Op op = TransformFilterPlacement.transform(exprs, gn, bgp); return plainExecute(op, input, execCxt); } - /** Execute without modification of the op - does <b>not</b> apply special graph name translations */ - private static QueryIterator plainExecute(Op op, QueryIterator input, ExecutionContext execCxt) - { + + /** + * Execute without modification of the op - does <b>not</b> apply special graph + * name translations + */ + private static QueryIterator plainExecute(Op op, QueryIterator input, ExecutionContext execCxt) { // -- Execute // Switch to a non-reordering executor // The Op may be a sequence due to TransformFilterPlacement @@ -275,23 +257,19 @@ public class OpExecutorTDB1 extends OpExecutor return QC.execute(op, input, ec2); } - private static BasicPattern reorder(BasicPattern pattern, QueryIterPeek peek, ReorderTransformation transform) - { - if ( transform != null ) - { + private static BasicPattern reorder(BasicPattern pattern, QueryIterPeek peek, ReorderTransformation transform) { + // Caller tested that peek.hasNext is true. + if ( transform != null ) { // This works by getting one result from the peek iterator, // and creating the more gounded BGP. The tranform is used to // determine the best order and the transformation is returned. This // transform is applied to the unsubstituted pattern (which will be // substituted as part of evaluation. - if ( ! peek.hasNext() ) - throw new ARQInternalErrorException("Peek iterator is already empty"); - BasicPattern pattern2 = Substitute.substitute(pattern, peek.peek() ); // Calculate the reordering based on the substituted pattern. ReorderProc proc = transform.reorderIndexes(pattern2); - // Then reorder original patten + // Then reorder original pattern pattern = proc.reorder(pattern); } return pattern; @@ -301,17 +279,15 @@ public class OpExecutorTDB1 extends OpExecutor * Returns null for default graph in storage (use the triple table). * Returns Node.ANY for the union graph */ - public static Node decideGraphNode(Node gn, ExecutionContext execCxt) - { - // ---- Graph names with special meaning. + public static Node decideGraphNode(Node gn, ExecutionContext execCxt) { + // ---- Graph names with special meaning. // Graph names with special meaning: // Quad.defaultGraphIRI -- the IRI used in GRAPH <> to mean the default graph. // Quad.defaultGraphNodeGenerated -- the internal marker node used for the quad form of queries. // Quad.unionGraph -- the IRI used in GRAPH <> to mean the union of named graphs - if ( isDefaultGraphStorage(gn) ) - { + if ( isDefaultGraphStorage(gn) ) { // Storage concrete, default graph. // Either outside GRAPH (no implicit union) // or using the "name" of the default graph @@ -327,8 +303,7 @@ public class OpExecutorTDB1 extends OpExecutor } // Is this a query against the real default graph in the storage (in a 3-tuple table). - private static boolean isDefaultGraphStorage(Node gn) - { + private static boolean isDefaultGraphStorage(Node gn) { if ( gn == null ) return true; @@ -342,8 +317,7 @@ public class OpExecutorTDB1 extends OpExecutor } @Override - protected QueryIterator execute(OpDatasetNames dsNames, QueryIterator input) - { + protected QueryIterator execute(OpDatasetNames dsNames, QueryIterator input) { DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); Node gn = dsNames.getGraphNode(); @@ -356,8 +330,7 @@ public class OpExecutorTDB1 extends OpExecutor // ---- OpExecute factories and plain executor. private static OpExecutorFactory plainFactory = new OpExecutorPlainFactoryTDB(); - private static class OpExecutorPlainFactoryTDB implements OpExecutorFactory - { + private static class OpExecutorPlainFactoryTDB implements OpExecutorFactory { @Override public OpExecutor create(ExecutionContext execCxt) { @@ -366,27 +339,24 @@ public class OpExecutorTDB1 extends OpExecutor } /** An op executor that simply executes a BGP or QuadPattern without any reordering */ - private static class OpExecutorPlainTDB extends OpExecutor - { + private static class OpExecutorPlainTDB extends OpExecutor { Predicate<Tuple<NodeId>> filter = null; - public OpExecutorPlainTDB(ExecutionContext execCxt) - { + public OpExecutorPlainTDB(ExecutionContext execCxt) { super(execCxt); filter = QC2.getFilter(execCxt.getContext()); } @Override - public QueryIterator execute(OpBGP opBGP, QueryIterator input) - { + public QueryIterator execute(OpBGP opBGP, QueryIterator input) { Graph g = execCxt.getActiveGraph(); - if ( g instanceof GraphTDB gtdb ) - { + if ( g instanceof GraphTDB gtdb ) { BasicPattern bgp = opBGP.getPattern(); Explain.explain("Execute", bgp, execCxt.getContext()); // Triple-backed (but may be named as explicit default graph). - //return SolverLib.execute((GraphTDB)g, bgp, input, filter, execCxt); + // return SolverLib.execute((GraphTDB)g, bgp, input, filter, + // execCxt); Node gn = decideGraphNode(gtdb.getGraphName(), execCxt); return PatternMatchTDB1.execute(gtdb.getDatasetGraphTDB(), gn, bgp, input, filter, execCxt); } @@ -395,26 +365,23 @@ public class OpExecutorTDB1 extends OpExecutor } @Override - public QueryIterator execute(OpQuadPattern opQuadPattern, QueryIterator input) - { + public QueryIterator execute(OpQuadPattern opQuadPattern, QueryIterator input) { Node gn = opQuadPattern.getGraphNode(); gn = decideGraphNode(gn, execCxt); - if ( execCxt.getDataset() instanceof DatasetGraphTDB dsgtdb ) - { + if ( execCxt.getDataset() instanceof DatasetGraphTDB dsgtdb ) { Explain.explain("Execute", opQuadPattern.getPattern(), execCxt.getContext()); BasicPattern bgp = opQuadPattern.getBasicPattern(); return PatternMatchTDB1.execute(dsgtdb, gn, bgp, input, filter, execCxt); } // Maybe a TDB named graph inside a non-TDB dataset. Graph g = execCxt.getActiveGraph(); - if ( g instanceof GraphTDB gtdb ) - { - // Triples graph from TDB (which is the default graph of the dataset), + if ( g instanceof GraphTDB gtdb ) { + // Triples graph from TDB, which is the default graph of the dataset, // used a named graph in a composite dataset. BasicPattern bgp = opQuadPattern.getBasicPattern(); Explain.explain("Execute", bgp, execCxt.getContext()); - // Don't pass in G -- gn may be different. + // Don't pass in g -- gn may be different. return PatternMatchTDB1.execute(gtdb.getDatasetGraphTDB(), gn, bgp, input, filter, execCxt); } Log.warn(this, "Non-DatasetGraphTDB passed to OpExecutorPlainTDB"); diff --git a/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/StageGeneratorDirectTDB.java b/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/StageGeneratorDirectTDB.java index 10f9cc13f2..96b45966ea 100644 --- a/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/StageGeneratorDirectTDB.java +++ b/jena-tdb1/src/main/java/org/apache/jena/tdb1/solver/StageGeneratorDirectTDB.java @@ -23,38 +23,35 @@ package org.apache.jena.tdb1.solver; import java.util.function.Predicate; -import org.apache.jena.atlas.lib.tuple.Tuple ; -import org.apache.jena.graph.Graph ; -import org.apache.jena.sparql.core.BasicPattern ; -import org.apache.jena.sparql.engine.ExecutionContext ; -import org.apache.jena.sparql.engine.QueryIterator ; -import org.apache.jena.sparql.engine.main.StageGenerator ; +import org.apache.jena.atlas.lib.tuple.Tuple; +import org.apache.jena.graph.Graph; +import org.apache.jena.sparql.core.BasicPattern; +import org.apache.jena.sparql.engine.ExecutionContext; +import org.apache.jena.sparql.engine.QueryIterator; +import org.apache.jena.sparql.engine.main.StageGenerator; import org.apache.jena.tdb1.store.GraphTDB; import org.apache.jena.tdb1.store.NodeId; -/** Execute TDB requests directly -- no reordering - * Using OpExecutor is preferred. +/** + * Execute TDB requests directly -- no reordering. Using OpExecutor is preferred. */ -public class StageGeneratorDirectTDB implements StageGenerator -{ +public class StageGeneratorDirectTDB implements StageGenerator { // Using OpExecutor is preferred. - StageGenerator above = null ; + StageGenerator above = null; - public StageGeneratorDirectTDB(StageGenerator original) - { - above = original ; + public StageGeneratorDirectTDB(StageGenerator original) { + above = original; } @Override - public QueryIterator execute(BasicPattern pattern, QueryIterator input, ExecutionContext execCxt) - { + public QueryIterator execute(BasicPattern pattern, QueryIterator input, ExecutionContext execCxt) { // --- In case this isn't for TDB - Graph g = execCxt.getActiveGraph() ; + Graph g = execCxt.getActiveGraph(); - if ( ! ( g instanceof GraphTDB graph) ) + if ( !(g instanceof GraphTDB graph) ) // Not us - bounce up the StageGenerator chain - return above.execute(pattern, input, execCxt) ; - Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()) ; - return PatternMatchTDB1.execute(graph, pattern, input, filter, execCxt) ; + return above.execute(pattern, input, execCxt); + Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); + return PatternMatchTDB1.execute(graph, pattern, input, filter, execCxt); } } diff --git a/jena-tdb1/src/test/java/org/apache/jena/tdb1/store/Test_SPARQL_TDB1.java b/jena-tdb1/src/test/java/org/apache/jena/tdb1/store/Test_SPARQL_TDB1.java index 2a5fffcc38..4539b39c6f 100644 --- a/jena-tdb1/src/test/java/org/apache/jena/tdb1/store/Test_SPARQL_TDB1.java +++ b/jena-tdb1/src/test/java/org/apache/jena/tdb1/store/Test_SPARQL_TDB1.java @@ -23,6 +23,8 @@ package org.apache.jena.tdb1.store; import static org.junit.Assert.assertEquals; +import org.junit.Test ; + import org.apache.jena.atlas.lib.StrUtils ; import org.apache.jena.graph.Graph ; import org.apache.jena.graph.NodeFactory ; @@ -30,6 +32,10 @@ import org.apache.jena.graph.Triple ; import org.apache.jena.query.* ; import org.apache.jena.rdf.model.Model ; import org.apache.jena.rdf.model.ModelFactory ; +import org.apache.jena.sparql.core.DatasetGraph; +import org.apache.jena.sparql.exec.QueryExec; +import org.apache.jena.sparql.exec.RowSet; +import org.apache.jena.sparql.exec.RowSetOps; import org.apache.jena.sparql.exec.UpdateExec; import org.apache.jena.sparql.sse.SSE ; import org.apache.jena.system.Txn; @@ -39,7 +45,6 @@ import org.apache.jena.tdb1.base.file.Location; import org.apache.jena.update.UpdateAction; import org.apache.jena.update.UpdateFactory; import org.apache.jena.update.UpdateRequest; -import org.junit.Test ; /** * Test SPARQL @@ -152,6 +157,20 @@ public class Test_SPARQL_TDB1 assertEquals(false, b); } + @Test + public void sparql7() { + // https://github.com/apache/jena/issues/3751 + String qs = """ + PREFIX : <http://example/> + SELECT * { ?x :property+ ?y . ?z :q1 123 . ?z :q2 456 . } + """; + DatasetGraph dsg = TDB1Factory.createDatasetGraph(); + dsg.executeRead(()->{ + RowSet rs = QueryExec.dataset(dsg).query(qs).select(); + RowSetOps.consume(rs); + }); + } + // Test transactions effective. @Test diff --git a/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/OpExecutorTDB2.java b/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/OpExecutorTDB2.java index 2273e07bae..23c649d348 100644 --- a/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/OpExecutorTDB2.java +++ b/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/OpExecutorTDB2.java @@ -27,7 +27,6 @@ import org.apache.jena.atlas.lib.tuple.Tuple; import org.apache.jena.atlas.logging.Log; import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; -import org.apache.jena.sparql.ARQInternalErrorException; import org.apache.jena.sparql.algebra.Op; import org.apache.jena.sparql.algebra.op.*; import org.apache.jena.sparql.algebra.optimize.TransformFilterPlacement; @@ -42,6 +41,7 @@ import org.apache.jena.sparql.engine.iterator.QueryIterPeek; import org.apache.jena.sparql.engine.main.OpExecutor; import org.apache.jena.sparql.engine.main.OpExecutorFactory; import org.apache.jena.sparql.engine.main.QC; +import org.apache.jena.sparql.engine.main.StageGenerator; import org.apache.jena.sparql.engine.main.iterator.QueryIterGraph; import org.apache.jena.sparql.engine.optimizer.reorder.ReorderProc; import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation; @@ -53,17 +53,17 @@ import org.apache.jena.tdb2.store.NodeId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** TDB executor for algebra expressions. It is the standard ARQ executor - * except for basic graph patterns and filtered basic graph patterns (currently). +/** + * TDB2 executor for algebra expressions. It is the standard ARQ executor + * except for basic graph patterns and filtered basic graph patterns (currently). * - * See also: StageGeneratorDirectTDB, a non-reordering + * See also: {@link StageGeneratorDirectTDB}, a non-reordering {@link StageGenerator} */ public class OpExecutorTDB2 extends OpExecutor { private static final Logger log = LoggerFactory.getLogger(OpExecutorTDB2.class); - public final static OpExecutorFactory OpExecFactoryTDB = new OpExecutorFactory() - { + public final static OpExecutorFactory OpExecFactoryTDB = new OpExecutorFactory() { @Override public OpExecutor create(ExecutionContext execCxt) { return new OpExecutorTDB2(execCxt); } @@ -73,8 +73,7 @@ public class OpExecutorTDB2 extends OpExecutor // A new compile object is created for each op compilation. // So the execCxt is changing as we go through the query-compile-execute process - public OpExecutorTDB2(ExecutionContext execCxt) - { + public OpExecutorTDB2(ExecutionContext execCxt) { super(execCxt); // NB. The dataset may be a TDB one, or a general one. // Any merged union graph magic (for a TDB dataset was handled @@ -96,29 +95,22 @@ public class OpExecutorTDB2 extends OpExecutor // Need to work with SolverLib which wraps the NodeId bindgins with a converter. @Override - protected QueryIterator execute(OpDistinct opDistinct, QueryIterator input) - { + protected QueryIterator execute(OpDistinct opDistinct, QueryIterator input) { return super.execute(opDistinct, input); } @Override - protected QueryIterator execute(OpReduced opReduced, QueryIterator input) - { + protected QueryIterator execute(OpReduced opReduced, QueryIterator input) { return super.execute(opReduced, input); } @Override - protected QueryIterator execute(OpFilter opFilter, QueryIterator input) - { + protected QueryIterator execute(OpFilter opFilter, QueryIterator input) { if ( ! isForTDB ) return super.execute(opFilter, input); - // If the filter does not apply to the input?? - // Where does ARQ catch this? - // (filter (bgp ...)) - if ( OpBGP.isBGP(opFilter.getSubOp()) ) - { + if ( OpBGP.isBGP(opFilter.getSubOp()) ) { // Still may be a TDB graph in a non-TDB dataset (e.g. a named model) GraphTDB graph = (GraphTDB)execCxt.getActiveGraph(); OpBGP opBGP = (OpBGP)opFilter.getSubOp(); @@ -126,8 +118,7 @@ public class OpExecutorTDB2 extends OpExecutor } // (filter (quadpattern ...)) - if ( opFilter.getSubOp() instanceof OpQuadPattern quadPattern) - { + if ( opFilter.getSubOp() instanceof OpQuadPattern quadPattern) { DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); return optimizeExecuteQuads(ds, input, quadPattern.getGraphNode(), quadPattern.getBasicPattern(), @@ -136,31 +127,23 @@ public class OpExecutorTDB2 extends OpExecutor // (filter (anything else)) return super.execute(opFilter, input); - } + } // ---- Triple patterns @Override - protected QueryIterator execute(OpBGP opBGP, QueryIterator input) - { + protected QueryIterator execute(OpBGP opBGP, QueryIterator input) { if ( ! isForTDB ) return super.execute(opBGP, input); GraphTDB graph = (GraphTDB)execCxt.getActiveGraph(); return executeBGP(graph, opBGP, input, null, execCxt); - } @Override - protected QueryIterator execute(OpQuadPattern quadPattern, QueryIterator input) - { + protected QueryIterator execute(OpQuadPattern quadPattern, QueryIterator input) { if ( ! isForTDB ) return super.execute(quadPattern, input); - - // DatasetGraph dg = execCxt.getDataset(); - // if ( ! ( dg instanceof DatasetGraphTDB ) ) - // throw new InternalErrorException("Not a TDB backed dataset in quad pattern execution"); - DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); BasicPattern bgp = quadPattern.getBasicPattern(); Node gn = quadPattern.getGraphNode(); @@ -168,20 +151,17 @@ public class OpExecutorTDB2 extends OpExecutor } @Override - protected QueryIterator execute(OpGraph opGraph, QueryIterator input) - { + protected QueryIterator execute(OpGraph opGraph, QueryIterator input) { // Path evaluation or dataset sets which do not go straight to the DatasetGraphTDB return new QueryIterGraph(input, opGraph, execCxt); } /** Execute a BGP (and filters) on a TDB graph, which may be in default storage or it may be a named graph */ - private static QueryIterator executeBGP(GraphTDB graph, OpBGP opBGP, QueryIterator input, ExprList exprs, - ExecutionContext execCxt) - { + private static QueryIterator executeBGP(GraphTDB graph, OpBGP opBGP, QueryIterator input, + ExprList exprs, ExecutionContext execCxt) { DatasetGraphTDB dsgtdb = graph.getDSG(); // Is it the real default graph (normal route or explicitly named)? - if ( ! isDefaultGraphStorage(graph.getGraphName())) - { + if ( !isDefaultGraphStorage(graph.getGraphName()) ) { // Not default storage - it's a named graph in storage. return optimizeExecuteQuads(dsgtdb, input, graph.getGraphName(), opBGP.getPattern(), exprs, execCxt); } @@ -192,41 +172,36 @@ public class OpExecutorTDB2 extends OpExecutor /** Execute, with optimization, a basic graph pattern on the default graph storage */ private static QueryIterator optimizeExecuteTriples(DatasetGraphTDB dsgtdb, QueryIterator input, - BasicPattern pattern, ExprList exprs, - ExecutionContext execCxt) + BasicPattern pattern, ExprList exprs, ExecutionContext execCxt) { - // -- Input - // Must pass this iterator into the next stage. - if ( pattern.size() >= 2 ) { - // Must be 2 or triples to reorder. - ReorderTransformation transform = dsgtdb.getReorderTransform(); - if ( transform != null ) { - QueryIterPeek peek = QueryIterPeek.create(input, execCxt); - input = peek; // Must pass on - try { + try { + if ( ! input.hasNext() ) + return input; + + // -- Input + // Must pass this iterator into the next stage. + if ( pattern.size() >= 2 ) { + // Must be 2 or triples to reorder. + ReorderTransformation transform = dsgtdb.getReorderTransform(); + if ( transform != null ) { + QueryIterPeek peek = QueryIterPeek.create(input, execCxt); + input = peek; // Must pass on pattern = reorder(pattern, peek, transform); - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); } } - } else { - try { - if ( !input.hasNext() ) - return input; - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); + + if ( exprs == null ) { + Explain.explain("Execute", pattern, execCxt.getContext()); + Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); + return PatternMatchTDB2.execute(dsgtdb, Quad.defaultGraphNodeGenerated, pattern, input, filter, execCxt); } - } + // -- Filter placement - if ( exprs == null ) { - Explain.explain("Execute", pattern, execCxt.getContext()); - Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); - return PatternMatchTDB2.execute(dsgtdb, Quad.defaultGraphNodeGenerated, pattern, input, filter, execCxt); + Op op = TransformFilterPlacement.transform(exprs, pattern); + return plainExecute(op, input, execCxt); + } catch (Exception e) { + return new QueryIterFailed(input, execCxt, e); } - // -- Filter placement - - Op op = TransformFilterPlacement.transform(exprs, pattern); - return plainExecute(op, input, execCxt); } /** Execute, with optimization, a quad pattern */ @@ -236,48 +211,44 @@ public class OpExecutorTDB2 extends OpExecutor ExprList exprs, ExecutionContext execCxt) { // ---- Graph names with special meaning. - gn = decideGraphNode(gn, execCxt); if ( gn == null ) return optimizeExecuteTriples(dsgtdb, input, bgp, exprs, execCxt); + try { + if ( ! input.hasNext() ) + return input; - // ---- Execute quads+filters - if ( bgp.size() >= 2 ) { - ReorderTransformation transform = dsgtdb.getReorderTransform(); + // ---- Execute quads+filters + if ( bgp.size() >= 2 ) { + ReorderTransformation transform = dsgtdb.getReorderTransform(); - if ( transform != null ) { - QueryIterPeek peek = QueryIterPeek.create(input, execCxt); - input = peek; // Original input now invalid. - try { + if ( transform != null ) { + QueryIterPeek peek = QueryIterPeek.create(input, execCxt); + input = peek; // Original input now invalid. bgp = reorder(bgp, peek, transform); - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); } } - } else { - try { - if ( !input.hasNext() ) - return input; - } catch (Exception e) { - return new QueryIterFailed(input, execCxt, e); + + if ( exprs == null ) { + // Triple-backed (but may be named as explicit default graph). + Explain.explain("Execute", bgp, execCxt.getContext()); + Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); + return PatternMatchTDB2.execute(dsgtdb, gn, bgp, input, filter, execCxt); } - } - if ( exprs == null ) { - // Triple-backed (but may be named as explicit default graph). - Explain.explain("Execute", bgp, execCxt.getContext()); - Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); - return PatternMatchTDB2.execute(dsgtdb, gn, bgp, input, filter, execCxt); + // -- Filter placement + Op op = TransformFilterPlacement.transform(exprs, gn, bgp); + return plainExecute(op, input, execCxt); + } catch (Exception e) { + return new QueryIterFailed(input, execCxt, e); } - - // -- Filter placement - Op op = TransformFilterPlacement.transform(exprs, gn, bgp); - return plainExecute(op, input, execCxt); } - /** Execute without modification of the op - does <b>not</b> apply special graph name translations */ - private static QueryIterator plainExecute(Op op, QueryIterator input, ExecutionContext execCxt) - { + /** + * Execute without modification of the op - does <b>not</b> apply special graph + * name translations + */ + private static QueryIterator plainExecute(Op op, QueryIterator input, ExecutionContext execCxt) { // -- Execute // Switch to a non-reordering executor // The Op may be a sequence due to TransformFilterPlacement @@ -293,43 +264,37 @@ public class OpExecutorTDB2 extends OpExecutor return QC.execute(op, input, ec2); } - private static BasicPattern reorder(BasicPattern pattern, QueryIterPeek peek, ReorderTransformation transform) - { - if ( transform != null ) - { + private static BasicPattern reorder(BasicPattern pattern, QueryIterPeek peek, ReorderTransformation transform) { + // Caller tested that peek.hasNext is true. + if ( transform != null ) { // This works by getting one result from the peek iterator, - // and creating the more gounded BGP. The tranform is used to + // and creating the more grounded BGP. The transform is used to // determine the best order and the transformation is returned. This // transform is applied to the unsubstituted pattern (which will be // substituted as part of evaluation. - if ( ! peek.hasNext() ) - throw new ARQInternalErrorException("Peek iterator is already empty"); - - BasicPattern pattern2 = Substitute.substitute(pattern, peek.peek() ); + BasicPattern pattern2 = Substitute.substitute(pattern, peek.peek()); // Calculate the reordering based on the substituted pattern. ReorderProc proc = transform.reorderIndexes(pattern2); - // Then reorder original patten + // Then reorder original pattern pattern = proc.reorder(pattern); } return pattern; } - /** Handle special graph node names. - * Returns null for default graph in storage (use the triple table). - * Returns Node.ANY for the union graph + /** + * Handle special graph node names. Returns null for default graph in storage + * (use the triple table). Returns Node.ANY for the union graph */ - public static Node decideGraphNode(Node gn, ExecutionContext execCxt) - { - // ---- Graph names with special meaning. + public static Node decideGraphNode(Node gn, ExecutionContext execCxt) { + // ---- Graph names with special meaning. // Graph names with special meaning: - // Quad.defaultGraphIRI -- the IRI used in GRAPH <> to mean the default graph. - // Quad.defaultGraphNodeGenerated -- the internal marker node used for the quad form of queries. - // Quad.unionGraph -- the IRI used in GRAPH <> to mean the union of named graphs + // * Quad.defaultGraphIRI -- the IRI used in GRAPH <> to mean the default graph. + // * Quad.defaultGraphNodeGenerated -- the internal marker node used for the quad form of queries. + // * Quad.unionGraph -- the IRI used in GRAPH <> to mean the union of named graphs - if ( isDefaultGraphStorage(gn) ) - { + if ( isDefaultGraphStorage(gn) ) { // Storage concrete, default graph. // Either outside GRAPH (no implicit union) // or using the "name" of the default graph @@ -347,8 +312,7 @@ public class OpExecutorTDB2 extends OpExecutor } // Is this a query against the real default graph in the storage (in a 3-tuple table). - private static boolean isDefaultGraphStorage(Node gn) - { + private static boolean isDefaultGraphStorage(Node gn) { if ( gn == null ) return true; @@ -362,8 +326,7 @@ public class OpExecutorTDB2 extends OpExecutor } @Override - protected QueryIterator execute(OpDatasetNames dsNames, QueryIterator input) - { + protected QueryIterator execute(OpDatasetNames dsNames, QueryIterator input) { DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset(); Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()); Node gn = dsNames.getGraphNode(); @@ -376,64 +339,58 @@ public class OpExecutorTDB2 extends OpExecutor // ---- OpExecute factories and plain executor. private static OpExecutorFactory plainFactory = new OpExecutorPlainFactoryTDB(); - private static class OpExecutorPlainFactoryTDB implements OpExecutorFactory - { + private static class OpExecutorPlainFactoryTDB implements OpExecutorFactory { @Override - public OpExecutor create(ExecutionContext execCxt) - { + public OpExecutor create(ExecutionContext execCxt) { return new OpExecutorPlainTDB(execCxt); } } - /** An op executor that simply executes a BGP or QuadPattern without any reordering */ - private static class OpExecutorPlainTDB extends OpExecutor - { + /** + * An op executor that simply executes a BGP or QuadPattern without any + * reordering + */ + private static class OpExecutorPlainTDB extends OpExecutor { Predicate<Tuple<NodeId>> filter = null; - public OpExecutorPlainTDB(ExecutionContext execCxt) - { + public OpExecutorPlainTDB(ExecutionContext execCxt) { super(execCxt); filter = QC2.getFilter(execCxt.getContext()); } @Override - public QueryIterator execute(OpBGP opBGP, QueryIterator input) - { + public QueryIterator execute(OpBGP opBGP, QueryIterator input) { Graph g = execCxt.getActiveGraph(); - if ( g instanceof GraphTDB gtdb ) - { + if ( g instanceof GraphTDB gtdb ) { BasicPattern bgp = opBGP.getPattern(); Explain.explain("Execute", bgp, execCxt.getContext()); // Triple-backed (but may be named as explicit default graph). Node gn = decideGraphNode(gtdb.getGraphName(), execCxt); return PatternMatchTDB2.execute(gtdb.getDSG(), gn, bgp, input, filter, execCxt); } - Log.warn(this, "Non-GraphTDB passed to OpExecutorPlainTDB: "+g.getClass().getSimpleName()); + Log.warn(this, "Non-GraphTDB passed to OpExecutorPlainTDB: " + g.getClass().getSimpleName()); return super.execute(opBGP, input); } @Override - public QueryIterator execute(OpQuadPattern opQuadPattern, QueryIterator input) - { + public QueryIterator execute(OpQuadPattern opQuadPattern, QueryIterator input) { Node gn = opQuadPattern.getGraphNode(); gn = decideGraphNode(gn, execCxt); - if ( execCxt.getDataset() instanceof DatasetGraphTDB dsgtdb ) - { + if ( execCxt.getDataset() instanceof DatasetGraphTDB dsgtdb ) { Explain.explain("Execute", opQuadPattern.getPattern(), execCxt.getContext()); BasicPattern bgp = opQuadPattern.getBasicPattern(); return PatternMatchTDB2.execute(dsgtdb, gn, bgp, input, filter, execCxt); } // Maybe a TDB named graph inside a non-TDB dataset. Graph g = execCxt.getActiveGraph(); - if ( g instanceof GraphTDB gtdb ) - { - // Triples graph from TDB (which is the default graph of the dataset), + if ( g instanceof GraphTDB gtdb ) { + // Triples graph from TDB, which is the default graph of the dataset, // used a named graph in a composite dataset. BasicPattern bgp = opQuadPattern.getBasicPattern(); Explain.explain("Execute", bgp, execCxt.getContext()); - // Don't pass in G -- gn may be different. + // Don't pass in g -- gn may be different. return PatternMatchTDB2.execute(gtdb.getDSG(), gn, bgp, input, filter, execCxt); } Log.warn(this, "Non-DatasetGraphTDB passed to OpExecutorPlainTDB"); diff --git a/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java b/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java index 2e5605da03..709148ff35 100644 --- a/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java +++ b/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java @@ -34,7 +34,7 @@ import org.apache.jena.tdb2.store.GraphViewSwitchable; import org.apache.jena.tdb2.store.NodeId; /** - * Execute TDB requests directly -- no reordering Using OpExecutor is preferred. + * Execute TDB requests directly -- no reordering. Using OpExecutor is preferred. */ public class StageGeneratorDirectTDB implements StageGenerator { // Using OpExecutor is preferred. diff --git a/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/Test_SPARQL_TDB.java b/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/Test_SPARQL_TDB.java index 2374295b22..fd9fc01f3d 100644 --- a/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/Test_SPARQL_TDB.java +++ b/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/Test_SPARQL_TDB.java @@ -32,8 +32,13 @@ import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.Triple; import org.apache.jena.query.*; import org.apache.jena.rdf.model.Model; +import org.apache.jena.sparql.core.DatasetGraph; +import org.apache.jena.sparql.exec.QueryExec; +import org.apache.jena.sparql.exec.RowSet; +import org.apache.jena.sparql.exec.RowSetOps; import org.apache.jena.sparql.sse.SSE; import org.apache.jena.system.Txn; +import org.apache.jena.tdb2.DatabaseMgr; import org.apache.jena.tdb2.TDB2; import org.apache.jena.tdb2.TDB2Factory; import org.apache.jena.update.*; @@ -41,8 +46,7 @@ import org.apache.jena.update.*; /** * Test SPARQL */ -public class Test_SPARQL_TDB -{ +public class Test_SPARQL_TDB { private static Dataset create() { return TDB2Factory.createDataset(); } @@ -55,12 +59,13 @@ public class Test_SPARQL_TDB private static Triple triple = SSE.parseTriple("(<x> <y> 123)"); // Standalone graph. - @Test public void sparql1() - { - // Test OpExecutor.execute(OpBGP) for a named graph used as a standalone model + @Test + public void sparql1() { + // Test OpExecutor.execute(OpBGP) for a named graph used as a standalone + // model Dataset ds = create(); add(ds, graphName, triple); - Txn.executeRead(ds, ()->{ + Txn.executeRead(ds, () -> { Model m = ds.getNamedModel(graphName); String qs = "SELECT * { ?s ?p ?o . }"; Query query = QueryFactory.create(qs); @@ -71,17 +76,18 @@ public class Test_SPARQL_TDB } // Standalone graph. - @Test public void sparql2() - { - // Test OpExecutor.execute(OpFilter)for a named graph used as a standalone model + @Test + public void sparql2() { + // Test OpExecutor.execute(OpFilter)for a named graph used as a standalone + // model Dataset ds = create(); add(ds, graphName, triple); - Txn.executeRead(ds, ()->{ + Txn.executeRead(ds, () -> { Model m = ds.getNamedModel(graphName); String qs = "SELECT * { ?s ?p ?o . FILTER ( ?o < 456 ) }"; Query query = QueryFactory.create(qs); - try(QueryExecution qexec = QueryExecutionFactory.create(query, m)) { + try (QueryExecution qexec = QueryExecutionFactory.create(query, m)) { ResultSet rs = qexec.execSelect(); ResultSetFormatter.consume(rs); } @@ -89,11 +95,11 @@ public class Test_SPARQL_TDB } // Requires OpDatasetNames - @Test public void sparql3() - { + @Test + public void sparql3() { Dataset dataset = create(); // No triple added - Txn.executeRead(dataset, ()->{ + Txn.executeRead(dataset, () -> { Query query = QueryFactory.create("SELECT ?g { GRAPH ?g {} }"); QueryExecution qExec = QueryExecutionFactory.create(query, dataset); ResultSet rs = qExec.execSelect(); @@ -102,11 +108,11 @@ public class Test_SPARQL_TDB }); } - @Test public void sparql4() - { + @Test + public void sparql4() { Dataset dataset = create(); add(dataset, graphName, triple); - Txn.executeRead(dataset, ()->{ + Txn.executeRead(dataset, () -> { Query query = QueryFactory.create("SELECT ?g { GRAPH ?g {} }"); QueryExecution qExec = QueryExecutionFactory.create(query, dataset); ResultSet rs = qExec.execSelect(); @@ -115,30 +121,44 @@ public class Test_SPARQL_TDB }); } - @Test public void sparql5() - { + @Test + public void sparql5() { Dataset dataset = create(); add(dataset, graphName, triple); - Txn.executeRead(dataset, ()->{ - Query query = QueryFactory.create("ASK { GRAPH <"+graphName+"> {} }"); + Txn.executeRead(dataset, () -> { + Query query = QueryFactory.create("ASK { GRAPH <" + graphName + "> {} }"); boolean b = QueryExecutionFactory.create(query, dataset).execAsk(); assertEquals(true, b); }); } - @Test public void sparql6() - { + @Test + public void sparql6() { Dataset dataset = create(); add(dataset, graphName, triple); - Txn.executeRead(dataset, ()->{ + Txn.executeRead(dataset, () -> { Query query = QueryFactory.create("ASK { GRAPH <http://example/x> {} }"); boolean b = QueryExecutionFactory.create(query, dataset).execAsk(); assertEquals(false, b); }); } + @Test + public void sparql7() { + // https://github.com/apache/jena/issues/3751 + String qs = """ + PREFIX : <http://example/> + SELECT * { ?x :property+ ?y . ?z :q1 123 . ?z :q2 456 . } + """; + DatasetGraph dsg = DatabaseMgr.createDatasetGraph(); + dsg.executeRead(()->{ + RowSet rs = QueryExec.dataset(dsg).query(qs).select(); + RowSetOps.consume(rs); + }); + } + private static void add(Dataset dataset, String graphName, Triple triple) { - Txn.executeWrite(dataset, ()->{ + Txn.executeWrite(dataset, () -> { Graph g2 = dataset.asDatasetGraph().getGraph(NodeFactory.createURI(graphName)); g2.add(triple); }); @@ -146,10 +166,10 @@ public class Test_SPARQL_TDB // Test transactions effective. - @Test public void sparql_txn_1() - { + @Test + public void sparql_txn_1() { Dataset dataset = create(); - Txn.executeWrite(dataset, ()->{ + Txn.executeWrite(dataset, () -> { update(dataset, "INSERT DATA { <x:s> <x:p> <x:o> }"); }); // Explicit trasnaction steps. @@ -159,56 +179,55 @@ public class Test_SPARQL_TDB assertEquals(1, n); n = count(dataset, "SELECT * { <x:s> <x:p> <x:o>}"); assertEquals(1, n); - } finally { dataset.end(); } + } finally { + dataset.end(); + } } - @Test public void sparql_txn_2() - { + @Test + public void sparql_txn_2() { Dataset dataset1 = create(Location.mem("foo")); Dataset dataset2 = create(Location.mem("foo")); - Txn.executeWrite(dataset1, ()->{ + Txn.executeWrite(dataset1, () -> { update(dataset1, "INSERT DATA { <x:s> <x:p> <x:o> }"); }); - Txn.executeRead(dataset1, ()->{ + Txn.executeRead(dataset1, () -> { assertEquals(1, count(dataset1)); }); // Same location. - Txn.executeRead(dataset2, ()->{ + Txn.executeRead(dataset2, () -> { assertEquals(1, count(dataset2)); }); } - @Test public void sparql_update_unionGraph() - { + @Test + public void sparql_update_unionGraph() { Dataset ds = TDB2Factory.createDataset(); // Update concrete default graph - Txn.executeWrite(ds, ()->{ + Txn.executeWrite(ds, () -> { ds.asDatasetGraph().add(SSE.parseQuad("(<g> <s> <p> 123)")); }); ds.getContext().setTrue(TDB2.symUnionDefaultGraph); - Txn.executeWrite(ds, ()->{ + Txn.executeWrite(ds, () -> { // Update by looking in union graph - String us = StrUtils.strjoinNL( - "INSERT { GRAPH <http://example/g2> { ?s ?p 'NEW' } }", - "WHERE { ", - "?s ?p 123", - " }" ); + String us = StrUtils.strjoinNL("INSERT { GRAPH <http://example/g2> { ?s ?p 'NEW' } }", "WHERE { ", "?s ?p 123", " }"); UpdateRequest req = UpdateFactory.create(us); UpdateAction.execute(req, ds); }); - Txn.executeRead(ds, ()->{ + Txn.executeRead(ds, () -> { Model m = ds.getNamedModel("http://example/g2"); - assertEquals(1, m.size(), ()->"Did not find 1 statement in named graph"); + assertEquals(1, m.size(), () -> "Did not find 1 statement in named graph"); }); } - private int count(Dataset dataset) - { return count(dataset, "SELECT * { ?s ?p ?o }"); } + private int count(Dataset dataset) { + return count(dataset, "SELECT * { ?s ?p ?o }"); + } private int count(Dataset dataset, String queryString) @@ -218,8 +237,8 @@ public class Test_SPARQL_TDB ResultSet rs = qExec.execSelect(); return ResultSetFormatter.consume(rs); } - private void update(Dataset dataset, String string) - { + + private void update(Dataset dataset, String string) { UpdateRequest req = UpdateFactory.create(string); UpdateExecution proc = UpdateExecutionFactory.create(req, dataset); proc.execute();
