You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2025/05/08 21:24:18 UTC

(jena) branch main updated (7a38f75fd3 -> 561d15ddf5)

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

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


    from 7a38f75fd3 Bump @babel/core from 7.26.10 to 7.27.1 in /jena-fuseki2/jena-fuseki-ui
     new 00b7f7bf78 GH-3172: Fix test suite for mixed JUnit4/JUnit5 usage
     new 561d15ddf5 GH-3171: Improve determination of project variables

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/main/java/org/apache/jena/query/ARQ.java   |   4 +-
 .../src/main/java/org/apache/jena/query/Query.java | 178 +++---
 .../apache/jena/riot/lang/CollectorStreamBase.java |   3 +-
 .../jena/riot/lang/CollectorStreamQuads.java       |   3 +-
 .../jena/riot/lang/CollectorStreamTriples.java     |   4 +-
 .../org/apache/jena/sparql/algebra/OpAsQuery.java  | 707 ++++++++++-----------
 .../org/apache/jena/sparql/core/QueryCompare.java  |   4 +-
 .../apache/jena/sparql/engine/QueryEngineBase.java |   2 +-
 .../apache/jena/sparql/exec/QueryExecDataset.java  |  20 +-
 .../jena/sparql/exec/QueryExecDatasetBuilder.java  |   2 +-
 .../org/apache/jena/sparql/exec/http/Service.java  |   2 +-
 .../org/apache/jena/sparql/lang/SPARQLParser.java  |   1 -
 .../apache/jena/sparql/lang/SPARQLParserBase.java  |  11 +-
 .../jena/sparql/modify/UpdateEngineWorker.java     |   2 +-
 .../org/apache/jena/sparql/syntax/PatternVars.java |   1 -
 .../syntax/syntaxtransform/QueryTransformOps.java  |   6 +-
 .../java/org/apache/jena/arq/ARQTestSuite.java     |  36 +-
 .../test/java/org/apache/jena/sparql/TC_ARQ.java   |  20 +-
 .../apache/jena/sparql/algebra/TestOpAsQuery.java  |   2 +-
 .../org/apache/jena/sparql/syntax/TS_Syntax.java   |   1 +
 .../apache/jena/sparql/syntax/TestQueryParser.java |   2 +-
 .../jena/sparql/syntax/TestQueryProjectVars.java   |  58 ++
 .../examples/riot/ExRIOT5_StreamRDFCollect.java    |   1 -
 .../arq/querybuilder/AbstractQueryBuilder.java     |   4 +-
 24 files changed, 584 insertions(+), 490 deletions(-)
 create mode 100644 jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryProjectVars.java


(jena) 01/02: GH-3172: Fix test suite for mixed JUnit4/JUnit5 usage

Posted by an...@apache.org.
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 00b7f7bf783e588423b30eff3fd75c5d35066e4f
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Tue May 6 18:24:03 2025 +0100

    GH-3172: Fix test suite for mixed JUnit4/JUnit5 usage
---
 .../java/org/apache/jena/arq/ARQTestSuite.java     | 36 +++++++++++++++++-----
 .../test/java/org/apache/jena/sparql/TC_ARQ.java   | 20 ++++++------
 2 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java b/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
index bf59b20ea0..d1d023f2d9 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
@@ -18,10 +18,30 @@
 
 package org.apache.jena.arq;
 
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
+
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.AfterAll;
+
+
+// JUnit 4
+//import org.junit.AfterClass;
+//import org.junit.BeforeClass;
+//import org.junit.runner.RunWith;
+//import org.junit.runners.Suite;
+//@RunWith(Suite.class)
+//@Suite.SuiteClasses( {
+
+//JUnit 5
+//import org.junit.platform.suite.api.SelectClasses;
+//import org.junit.platform.suite.api.Suite;
+//import org.junit.jupiter.api.BeforeAll;
+//import org.junit.jupiter.api.AfterAll;
+//
+//@Suite
+//@SelectClasses({
 
 import org.apache.jena.atlas.TC_Atlas_ARQ;
 import org.apache.jena.atlas.legacy.BaseTest2;
@@ -44,8 +64,8 @@ import org.apache.jena.util.TS_UtilsARQ;
  * All the ARQ tests
  */
 
-@RunWith(Suite.class)
-@Suite.SuiteClasses( {
+@Suite
+@SelectClasses({
     TC_Atlas_ARQ.class,
 
     TS_UtilsARQ.class,
@@ -94,14 +114,14 @@ public class ARQTestSuite {
 
     public static final String log4jPropertiesResourceName = "log4j2.properties";
 
-    @BeforeClass
+    @BeforeAll
     public static void beforeClass() {
         NodeValue.VerboseWarnings = false;
         E_Function.WarnOnUnknownFunction = false;
         BaseTest2.setTestLogging();
     }
 
-    @AfterClass
+    @AfterAll
     public static void afterClass() {
         BaseTest2.unsetTestLogging();
     }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/TC_ARQ.java b/jena-arq/src/test/java/org/apache/jena/sparql/TC_ARQ.java
index 6ef2916344..0ef65932c8 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/TC_ARQ.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/TC_ARQ.java
@@ -18,6 +18,11 @@
 
 package org.apache.jena.sparql;
 
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
 import org.apache.jena.query.TS_Query;
 import org.apache.jena.rdf12.TS_RDF12;
 import org.apache.jena.sparql.algebra.TS_Algebra ;
@@ -52,15 +57,12 @@ import org.apache.jena.sparql.syntax.TS_Syntax ;
 import org.apache.jena.sparql.transaction.TS_Transaction ;
 import org.apache.jena.sparql.util.TS_DyadicDatasetGraphs;
 import org.apache.jena.sparql.util.TS_Util ;
-import org.apache.jena.sparql.util.iso.TS_Iso ;
 import org.apache.jena.sparql.util.compose.TS_DatasetCollectors;
-import org.junit.AfterClass ;
-import org.junit.BeforeClass ;
-import org.junit.runner.RunWith ;
-import org.junit.runners.Suite ;
+import org.apache.jena.sparql.util.iso.TS_Iso ;
+
+@Suite
+@SelectClasses({
 
-@RunWith(Suite.class)
-@Suite.SuiteClasses( {
     TS_SSE.class
     , TS_LangSPARQL.class
 
@@ -110,7 +112,7 @@ public class TC_ARQ
     private static boolean bVerboseWarnings;
     private static boolean bWarnOnUnknownFunction;
 
-    @BeforeClass
+    @BeforeAll
     public static void beforeClass() {
         bVerboseWarnings = NodeValue.VerboseWarnings;
         bWarnOnUnknownFunction = E_Function.WarnOnUnknownFunction;
@@ -118,7 +120,7 @@ public class TC_ARQ
         E_Function.WarnOnUnknownFunction = false;
     }
 
-    @AfterClass
+    @AfterAll
     public static void afterClass() {
         NodeValue.VerboseWarnings = bVerboseWarnings;
         E_Function.WarnOnUnknownFunction = bWarnOnUnknownFunction;


(jena) 02/02: GH-3171: Improve determination of project variables

Posted by an...@apache.org.
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 561d15ddf52e384fa5fccf06e888ce637c24899e
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Tue May 6 20:44:29 2025 +0100

    GH-3171: Improve determination of project variables
---
 .../src/main/java/org/apache/jena/query/ARQ.java   |   4 +-
 .../src/main/java/org/apache/jena/query/Query.java | 178 +++---
 .../apache/jena/riot/lang/CollectorStreamBase.java |   3 +-
 .../jena/riot/lang/CollectorStreamQuads.java       |   3 +-
 .../jena/riot/lang/CollectorStreamTriples.java     |   4 +-
 .../org/apache/jena/sparql/algebra/OpAsQuery.java  | 707 ++++++++++-----------
 .../org/apache/jena/sparql/core/QueryCompare.java  |   4 +-
 .../apache/jena/sparql/engine/QueryEngineBase.java |   2 +-
 .../apache/jena/sparql/exec/QueryExecDataset.java  |  20 +-
 .../jena/sparql/exec/QueryExecDatasetBuilder.java  |   2 +-
 .../org/apache/jena/sparql/exec/http/Service.java  |   2 +-
 .../org/apache/jena/sparql/lang/SPARQLParser.java  |   1 -
 .../apache/jena/sparql/lang/SPARQLParserBase.java  |  11 +-
 .../jena/sparql/modify/UpdateEngineWorker.java     |   2 +-
 .../org/apache/jena/sparql/syntax/PatternVars.java |   1 -
 .../syntax/syntaxtransform/QueryTransformOps.java  |   6 +-
 .../apache/jena/sparql/algebra/TestOpAsQuery.java  |   2 +-
 .../org/apache/jena/sparql/syntax/TS_Syntax.java   |   1 +
 .../apache/jena/sparql/syntax/TestQueryParser.java |   2 +-
 .../jena/sparql/syntax/TestQueryProjectVars.java   |  58 ++
 .../examples/riot/ExRIOT5_StreamRDFCollect.java    |   1 -
 .../arq/querybuilder/AbstractQueryBuilder.java     |   4 +-
 22 files changed, 545 insertions(+), 473 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
index 2448cc3c73..7fa7ea804f 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/ARQ.java
@@ -234,9 +234,9 @@ public class ARQ
     public static final Symbol useSAX = SystemARQ.allocSymbol("useSAX");
 
     /**
-     * Indicate whether duplicate select and groupby variables are allowed.
-     * If false, duplicates are silently suppressed; it's not an error.
+     * @deprecated Ignored
      */
+    @Deprecated(forRemoval = true)
     public static final boolean allowDuplicateSelectColumns = false;
 
     /**
diff --git a/jena-arq/src/main/java/org/apache/jena/query/Query.java b/jena-arq/src/main/java/org/apache/jena/query/Query.java
index 905862c681..659f7a00ba 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/Query.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/Query.java
@@ -350,14 +350,14 @@ public class Query extends Prologue implements Cloneable, Printable
     /** Return a list of the variables requested (SELECT) */
     public List<String> getResultVars() {
         // Ensure "SELECT *" processed
-        setResultVars();
+        ensureResultVars();
         return Var.varNames(projectVars.getVars());
     }
 
     /** Return a list of the variables requested (SELECT) */
     public List<Var> getProjectVars() {
         // Ensure "SELECT *" processed
-        setResultVars();
+        ensureResultVars();
         return projectVars.getVars();
     }
 
@@ -368,12 +368,12 @@ public class Query extends Prologue implements Cloneable, Printable
     /** Add a collection of projection variables to a SELECT query */
     public void addProjectVars(Collection<? > vars) {
         for ( Object obj : vars ) {
-            if ( obj instanceof String ) {
-                this.addResultVar((String)obj);
+            if ( obj instanceof String s ) {
+                addResultVar(s);
                 continue;
             }
-            if ( obj instanceof Var ) {
-                this.addResultVar((Var)obj);
+            if ( obj instanceof Var v ) {
+                addResultVar(v);
                 continue;
             }
             throw new QueryException("Not a variable or variable name: " + obj);
@@ -381,6 +381,82 @@ public class Query extends Prologue implements Cloneable, Printable
         resultVarsSet = true;
     }
 
+    /**
+     * Are the projectVars set?
+     * This can happen incrementally (the syntax has explicitly named variables e.g. {@code SELECT ?x`})
+     * or by analysis {@code SELECT *}.
+     */
+    private boolean resultVarsSet = false;
+
+    /**
+     * Set the results variables if necessary, when the query has "*" ({@code SELECT *}
+     * or {@code DESCRIBE *}) and for a construct query. This operation is idempotent and can
+     * be called to ensure the results variables have been set.
+     * @deprecated Use {@link #ensureResultVars()}
+     */
+    @Deprecated(forRemoval = true)
+    public void setResultVars() {
+        ensureResultVars();
+    }
+
+    public void ensureResultVars() {
+        if ( resultVarsSet )
+            return;
+        _resetResultVars();
+    }
+
+    /**
+     * If modifying a query, it may be necessary to reset the calculated result
+     * variables of the query for {@code SELECT *} and {@code DESCRIBE *} and
+     * {@code CONSTRUCT}.
+     */
+    public void resetResultVars() {
+        _resetResultVars();
+    }
+
+    private void _resetResultVars() {
+        if ( isQueryResultStar() )
+            projectVars.clear();
+
+        if ( getQueryPattern() == null ) {
+            if ( !this.isDescribeType() )
+                Log.warn(this, "No query pattern in non-DESCRIBE query");
+            resultVarsSet = true;
+            return;
+        }
+
+        _findAndAddNamedVars();
+        resultVarsSet = true;
+    }
+
+    private void _findAndAddNamedVars() {
+        Iterator<Var> varIter = null;
+        if ( isQueryResultStar() ) {
+            if ( hasGroupBy() ) {
+                varIter = groupVars.getVars().iterator();
+            } else {
+                // Binding variables -- in patterns, not in filters and not in EXISTS
+                LinkedHashSet<Var> queryVars = new LinkedHashSet<>();
+                PatternVars.vars(queryVars, this.getQueryPattern());
+                if ( this.hasValues() )
+                    queryVars.addAll(getValuesVariables());
+                varIter = queryVars.iterator();
+            }
+
+        } else {
+            varIter = projectVars.getVars().iterator();
+        }
+
+        // All query variables, including ones from bNodes in the query.
+
+        for ( ; varIter.hasNext() ; ) {
+            Var var = varIter.next();
+            if ( var.isNamedVar() && ! projectVars.contains(var) ) {
+                addResultVar(var);
+            }
+        }
+    }
+
     /** Add a projection variable to a SELECT query */
     public void addResultVar(String varName) {
         varName = Var.canonical(varName);
@@ -436,19 +512,18 @@ public class Query extends Prologue implements Cloneable, Printable
                 // SELECT (?a+?b AS ?x) ?x
                 throw new QueryBuildException("Duplicate variable (had an expression) in result projection '" + v + "'");
             // SELECT ?x ?x
-            if ( !ARQ.allowDuplicateSelectColumns )
-                return;
-            // else drop through and have two variables of the same name.
+            return;
         }
         varExprList.add(v);
     }
 
-    private static void _addVarExpr(VarExprList varExprList, Var v, Expr expr) {
+    private void _addVarExpr(VarExprList varExprList, Var v, Expr expr) {
         if ( varExprList.contains(v) )
             // SELECT ?x (?a+?b AS ?x)
             // SELECT (2*?a AS ?x) (?a+?b AS ?x)
             throw new QueryBuildException("Duplicate variable in result projection '" + v + "'");
         varExprList.add(v, expr);
+        resultVarsSet = true;
     }
 
     protected VarExprList groupVars = new VarExprList();
@@ -467,8 +542,7 @@ public class Query extends Prologue implements Cloneable, Printable
         addGroupBy(Var.alloc(varName));
     }
 
-    public void addGroupBy(Node v)
-    {
+    public void addGroupBy(Node v) {
         _addVar(groupVars, Var.alloc(v));
     }
 
@@ -483,7 +557,6 @@ public class Query extends Prologue implements Cloneable, Printable
             addGroupBy(expr.asVar());
             return;
         }
-
         groupVars.add(v, expr);
     }
 
@@ -615,85 +688,10 @@ public class Query extends Prologue implements Cloneable, Printable
         throw new QueryException("Result node not recognized: " + node);
     }
 
-    /** Get the result list (things wanted - not the results themselves)
-     *  of a DESCRIBE query. */
-    public List<Node> getResultURIs() { return resultNodes; }
-
-    private boolean resultVarsSet = false;
-    /**
-     * Set the results variables if necessary, when the query has "*" ({@code SELECT *}
-     * or {@code DESCRIBE *}) and for a construct query. This operation is idempotent and can
-     * be called to ensure the results variables have been set.
-     */
-    public void setResultVars() {
-        if ( resultVarsSet )
-            return;
-        synchronized (this) {
-            if ( resultVarsSet )
-                return;
-            // Synchronized in case this query is used in a multithreaded
-            // situation calling setResultVars(). JENA-1861.
-            resetResultVars();
-            resultVarsSet = true;
-        }
-    }
-
     /**
-     * If modifying a query, it may be necessary to reset the calculate of the result
-     * variables of the query for {@code SELECT *} and {@code DESCRIBE *} and {@code CONSTRUCT}.
+     * Get the result list URI of a DESCRIBE query.
      */
-    public void resetResultVars() {
-        if ( isQueryResultStar() )
-            projectVars.clear();
-
-        if ( getQueryPattern() == null ) {
-            if ( !this.isDescribeType() )
-                Log.warn(this, "setResultVars(): no query pattern");
-            return;
-        }
-
-        if ( isSelectType() ) {
-            if ( isQueryResultStar() )
-                findAndAddNamedVars();
-            return;
-        }
-
-        if ( isConstructType() ) {
-            // All named variables are in-scope
-            findAndAddNamedVars();
-            return;
-        }
-
-        if ( isDescribeType() ) {
-            if ( isQueryResultStar() )
-                findAndAddNamedVars();
-            return;
-        }
-//        if ( isAskType() ) {
-//        }
-    }
-
-    private void findAndAddNamedVars() {
-        Iterator<Var> varIter = null;
-        if ( hasGroupBy() )
-            varIter = groupVars.getVars().iterator();
-        else {
-            // Binding variables -- in patterns, not in filters and not in EXISTS
-            LinkedHashSet<Var> queryVars = new LinkedHashSet<>();
-            PatternVars.vars(queryVars, this.getQueryPattern());
-            if ( this.hasValues() )
-                queryVars.addAll(getValuesVariables());
-            varIter = queryVars.iterator();
-        }
-
-        // All query variables, including ones from bNodes in the query.
-
-        for ( ; varIter.hasNext() ; ) {
-            Var var = varIter.next();
-            if ( var.isNamedVar() )
-                addResultVar(var);
-        }
-    }
+    public List<Node> getResultURIs() { return resultNodes; }
 
     public void visit(QueryVisitor visitor) {
         visitor.startVisit(this);
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamBase.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamBase.java
index 52feeb5c17..03b618c318 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamBase.java
@@ -38,8 +38,9 @@ import org.apache.jena.sparql.core.Quad ;
  * of creating many threads is significant.
  *
  * @param <T> Type of the value stored in the collection
+ * @deprecated Use {@link CollectorStreamRDF}
  */
-@Deprecated(forRemoval = true)
+@Deprecated  //(forRemoval = true) -- this confuses the compiler in the maven build.
 public abstract class CollectorStreamBase<T> implements StreamRDF {
 	private final PrefixMap prefixes = PrefixMapFactory.create();
 	private String baseIri;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamQuads.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamQuads.java
index 6369a203c6..d6068739f3 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamQuads.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamQuads.java
@@ -27,9 +27,10 @@ import org.apache.jena.sparql.core.Quad ;
 /**
  * Collector stream for quads.
  *
- * @see CollectorStreamBase
+ * @deprecated Use {@link CollectorStreamRDF}
  */
 @Deprecated(forRemoval = true)
+@SuppressWarnings("all")
 public class CollectorStreamQuads extends CollectorStreamBase<Quad>	implements StreamRDF {
 	private List<Quad> quads = new ArrayList<>();
 
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamTriples.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamTriples.java
index 6ff00d4d9a..6d8bd46ccd 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamTriples.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/CollectorStreamTriples.java
@@ -27,9 +27,11 @@ import org.apache.jena.riot.system.StreamRDF;
 /**
  * Collector stream for triples.
  *
- * @see CollectorStreamBase
+ * @deprecated Use {@link CollectorStreamRDF}
  */
 @Deprecated(forRemoval = true)
+@SuppressWarnings("all")
+
 public class CollectorStreamTriples extends CollectorStreamBase<Triple> implements StreamRDF {
 	private List<Triple> triples = new ArrayList<>();
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
index eb26879e22..301d9c0118 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/OpAsQuery.java
@@ -16,38 +16,38 @@
  * limitations under the License.
  */
 
-package org.apache.jena.sparql.algebra ;
+package org.apache.jena.sparql.algebra;
 
-import java.util.* ;
-import java.util.function.BiConsumer ;
+import java.util.*;
+import java.util.function.BiConsumer;
 import java.util.function.Function;
 
-import org.apache.jena.atlas.lib.NotImplemented ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.graph.Triple ;
-import org.apache.jena.query.Query ;
-import org.apache.jena.query.QueryFactory ;
-import org.apache.jena.query.SortCondition ;
-import org.apache.jena.query.Syntax ;
-import org.apache.jena.sparql.ARQInternalErrorException ;
-import org.apache.jena.sparql.ARQNotImplemented ;
-import org.apache.jena.sparql.algebra.op.* ;
-import org.apache.jena.sparql.core.BasicPattern ;
-import org.apache.jena.sparql.core.Quad ;
-import org.apache.jena.sparql.core.Var ;
-import org.apache.jena.sparql.core.VarExprList ;
-import org.apache.jena.sparql.engine.QueryIterator ;
+import org.apache.jena.atlas.lib.NotImplemented;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryFactory;
+import org.apache.jena.query.SortCondition;
+import org.apache.jena.query.Syntax;
+import org.apache.jena.sparql.ARQInternalErrorException;
+import org.apache.jena.sparql.ARQNotImplemented;
+import org.apache.jena.sparql.algebra.op.*;
+import org.apache.jena.sparql.core.BasicPattern;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.core.VarExprList;
+import org.apache.jena.sparql.engine.QueryIterator;
 import org.apache.jena.sparql.engine.Rename;
-import org.apache.jena.sparql.expr.* ;
-import org.apache.jena.sparql.expr.aggregate.Aggregator ;
-import org.apache.jena.sparql.pfunction.PropFuncArg ;
-import org.apache.jena.sparql.syntax.* ;
+import org.apache.jena.sparql.expr.*;
+import org.apache.jena.sparql.expr.aggregate.Aggregator;
+import org.apache.jena.sparql.pfunction.PropFuncArg;
+import org.apache.jena.sparql.syntax.*;
 import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransform;
 import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransformCleanGroupsOfOne;
 import org.apache.jena.sparql.syntax.syntaxtransform.ElementTransformer;
 import org.apache.jena.sparql.syntax.syntaxtransform.ExprTransformApplyElementTransform;
-import org.apache.jena.sparql.util.graph.GraphList ;
-import org.apache.jena.vocabulary.RDF ;
+import org.apache.jena.sparql.util.graph.GraphList;
+import org.apache.jena.vocabulary.RDF;
 
 /**
  * Convert an Op expression in SPARQL syntax, that is, the reverse of algebra
@@ -63,7 +63,7 @@ import org.apache.jena.vocabulary.RDF ;
  * <p>
  * Some attempt is made to handle algebra expressions with operators from the optimizer.
  * <p>
- * It is possible to build algrebra expressions directly for which there is no SPARQL query
+ * It is possible to build algebra expressions directly for which there is no SPARQL query
  * that generates that algebra.  This code may produce an equivalent query but that is
  * not guaranteed.
  */
@@ -71,7 +71,7 @@ public class OpAsQuery {
     // Some things that can be done further:
 
     // TODO Optimization formats like OpTopN (which is an optimizer additional algebra operator).
-    // TODO More group flattening. This is better presentation, not chanage in algebra.
+    // TODO More group flattening. This is better presentation, not change in algebra.
     //    OPTIONAL (LeftJoin) unbundles the LHS to avoid { { P OPTIONAL{} } OPTIONAL{} }
     //    {   { ... } BIND } -- the inner { } is not necessary
     //       This is actually a general situation.
@@ -79,8 +79,10 @@ public class OpAsQuery {
     // e.g. BIND, VALUES.
 
     public static Query asQuery(Op op) {
-        Converter converter = new Converter(op) ;
-        return converter.convert() ;
+        Converter converter = new Converter(op);
+        Query query2 = converter.convert();
+        query2.ensureResultVars();
+        return query2;
     }
 
     public static Element asElement(Op op) {
@@ -106,174 +108,173 @@ public class OpAsQuery {
         // The stack of processing in a query is:
         // slice-distinct/reduce-project-order-filter[having]-extend*[AS and aggregate naming]-group-pattern
 
-        OpSlice        opSlice    = null ;
-        OpDistinct     opDistinct = null ;
-        OpReduced      opReduced  = null ;
-        OpProject      opProject  = null ;
-        OpOrder        opOrder    = null ;
-        OpFilter       opHaving   = null ;
-        List<OpExtend> opExtends  = new ArrayList<>() ;
-        OpGroup        opGroup    = null ;
+        OpSlice        opSlice    = null;
+        OpDistinct     opDistinct = null;
+        OpReduced      opReduced  = null;
+        OpProject      opProject  = null;
+        OpOrder        opOrder    = null;
+        OpFilter       opHaving   = null;
+        List<OpExtend> opExtends  = new ArrayList<>();
+        OpGroup        opGroup    = null;
         // End of the modifiers.
         // The pattern of the group or query itself if not grouped.
-        Op             pattern    = null ;
+        Op             pattern    = null;
 
         private QueryLevelDetails() {}
 
         // Debugging help.
         void info() {
             if ( opSlice != null )
-                System.out.printf("slice: (%d, %d)\n", opSlice.getStart(), opSlice.getLength()) ;
+                System.out.printf("slice: (%d, %d)\n", opSlice.getStart(), opSlice.getLength());
             if ( opDistinct != null )
-                System.out.printf("distinct\n") ;
+                System.out.printf("distinct\n");
             if ( opReduced != null )
-                System.out.printf("reduced\n") ;
+                System.out.printf("reduced\n");
             if ( opProject != null )
-                System.out.printf("project: %s\n", opProject.getVars()) ;
+                System.out.printf("project: %s\n", opProject.getVars());
             if ( opOrder != null )
-                System.out.printf("order: %s\n", opOrder.getConditions()) ;
+                System.out.printf("order: %s\n", opOrder.getConditions());
             if ( opHaving != null )
-                System.out.printf("having: %s\n", opHaving.getExprs()) ;
+                System.out.printf("having: %s\n", opHaving.getExprs());
             if ( opExtends != null && !opExtends.isEmpty() ) {
-                List<VarExprList> z = opExtends.stream().map(x -> x.getVarExprList()).toList() ;
-                System.out.printf("assigns: %s\n", z) ;
+                List<VarExprList> z = opExtends.stream().map(x -> x.getVarExprList()).toList();
+                System.out.printf("assigns: %s\n", z);
             }
             if ( opGroup != null ) {
-                List<ExprAggregator> aggregators = opGroup.getAggregators() ;
-                List<Var> aggVars = aggregators.stream().map(x -> x.getAggVar().asVar()).toList() ;
-                System.out.printf("group: %s |-| %s\n", opGroup.getGroupVars(), opGroup.getAggregators()) ;
-                System.out.printf("group agg vars: %s\n", aggVars) ;
+                List<ExprAggregator> aggregators = opGroup.getAggregators();
+                List<Var> aggVars = aggregators.stream().map(x -> x.getAggVar().asVar()).toList();
+                System.out.printf("group: %s |-| %s\n", opGroup.getGroupVars(), opGroup.getAggregators());
+                System.out.printf("group agg vars: %s\n", aggVars);
             }
         }
 
         static QueryLevelDetails analyse(Op operation) {
             // Walk inwards, collecting the query level information.
             // slice-distinct/reduce-project-order-filter[having]-extend*[AS and aggregate naming]-group-pattern
-            QueryLevelDetails details = new QueryLevelDetails() ;
+            QueryLevelDetails details = new QueryLevelDetails();
 
-            Op op = operation ;
+            Op op = operation;
             if ( op instanceof OpSlice ) {
-                details.opSlice = (OpSlice)op ;
-                op = details.opSlice.getSubOp() ;
+                details.opSlice = (OpSlice)op;
+                op = details.opSlice.getSubOp();
             }
             if ( op instanceof OpDistinct ) {
-                details.opDistinct = (OpDistinct)op ;
-                op = details.opDistinct.getSubOp() ;
+                details.opDistinct = (OpDistinct)op;
+                op = details.opDistinct.getSubOp();
             }
             if ( op instanceof OpReduced ) {
-                details.opReduced = (OpReduced)op ;
-                op = details.opReduced.getSubOp() ;
+                details.opReduced = (OpReduced)op;
+                op = details.opReduced.getSubOp();
             }
             if ( op instanceof OpProject ) {
-                details.opProject = (OpProject)op ;
-                op = details.opProject.getSubOp() ;
+                details.opProject = (OpProject)op;
+                op = details.opProject.getSubOp();
 
             }
             if ( op instanceof OpOrder ) {
-                details.opOrder = (OpOrder)op ;
-                op = details.opOrder.getSubOp() ;
+                details.opOrder = (OpOrder)op;
+                op = details.opOrder.getSubOp();
             }
 
             // Lookahead to see if an opGroup can be found.
             // If no group, leave and process as in WHERE clause.
-            details.opGroup = getGroup(op) ;
+            details.opGroup = getGroup(op);
             if ( details.opGroup == null ) {
                 // If project, deal with as AS.
                 // Else as regular WHERE
                 if ( details.opProject != null ) {
-                    op = processExtend(op, details.opExtends) ;
-                    details.pattern = op ;
+                    op = processExtend(op, details.opExtends);
+                    details.pattern = op;
                 } else
-                    details.pattern = op ;
-                return details ;
+                    details.pattern = op;
+                return details;
             }
             // (group) found.
-            details.pattern = details.opGroup.getSubOp() ;
+            details.pattern = details.opGroup.getSubOp();
             if ( op instanceof OpFilter ) {
-                details.opHaving = (OpFilter)op ;
-                op = details.opHaving.getSubOp() ;
+                details.opHaving = (OpFilter)op;
+                op = details.opHaving.getSubOp();
             }
             // Can't tell if it's an "aggregation" except by looking at the
             // assignment variables.
 
             // AS and aggregate renames.
-            op = processExtend(op, details.opExtends) ;
+            op = processExtend(op, details.opExtends);
 
             if ( !(op instanceof OpGroup) ) {
-                System.out.println("Expected (group), got " + op.getName()) ;
+                System.out.println("Expected (group), got " + op.getName());
             }
 
-            return details ;
+            return details;
         }
     }
 
     public static class Converter implements OpVisitor {
-        private Query               query ;
-        private Op                  queryOp ;
-        private Element             element      = null ;
-        private ElementGroup        currentGroup = null ;
-        private Deque<ElementGroup> stack        = new ArrayDeque<>() ;
-        private boolean             hasRun       = false ;
+        private Query               query;
+        private Op                  queryOp;
+        private Element             element      = null;
+        private ElementGroup        currentGroup = null;
+        private Deque<ElementGroup> stack        = new ArrayDeque<>();
+        private boolean             hasRun       = false;
 
         public Converter(Op op) {
-            this.query = null ;
-            this.queryOp = op ;
-            currentGroup = new ElementGroup() ;
+            this.query = null;
+            this.queryOp = op;
+            currentGroup = new ElementGroup();
         }
 
         Query convert() {
             if ( !hasRun )
                 try {
                     // Which may be broken, or null, if something went wrong.
-                    query = convertInner() ;
+                    query = convertInner();
                 }
                 finally {
-                    this.hasRun = true ;
+                    this.hasRun = true;
                 }
-            return query ;
+            return query;
         }
 
         Query convertInner() {
-            this.query = QueryFactory.create() ;
+            this.query = QueryFactory.create();
             // Special case. SELECT * { ... BIND (  AS ...) }
             if ( queryOp instanceof OpExtend ) {
-                List<OpExtend> assignments = new ArrayList<>() ;
-                Op op = processExtend(queryOp, assignments) ;
-                processQueryPattern(op, assignments) ;
-                query.setQueryResultStar(true) ;
-                query.resetResultVars();
-                return query ;
+                List<OpExtend> assignments = new ArrayList<>();
+                Op op = processExtend(queryOp, assignments);
+                processQueryPattern(op, assignments);
+                query.setQueryResultStar(true);
+                return query;
             }
 
             // There is a projection.
-            QueryLevelDetails level = QueryLevelDetails.analyse(queryOp) ;
-            processQueryPattern(level) ;
+            QueryLevelDetails level = QueryLevelDetails.analyse(queryOp);
+            processQueryPattern(level);
 
             // Modifier stack.
             // slice-distinct/reduce-project-order-filter[having]-extend[AS]-extend[agg]-group-pattern
             // Do as executed (the reverse order) because e.g. extends have effects on project.
 
             // Substitution mapping
-            Map<ExprVar, Expr> aggVarExprMap = new HashMap<>() ;
+            Map<ExprVar, Expr> aggVarExprMap = new HashMap<>();
 
             if ( level.opGroup != null ) {
-                query.getGroupBy().addAll(level.opGroup.getGroupVars()) ;
+                query.getGroupBy().addAll(level.opGroup.getGroupVars());
                 level.opGroup.getAggregators().forEach(eAgg -> {
-                    ExprVar v = eAgg.getAggVar() ;
-                    Aggregator agg = eAgg.getAggregator() ;
-                    aggVarExprMap.put(v, eAgg) ;
-                }) ;
-                query.getAggregators().addAll(level.opGroup.getAggregators()) ;
+                    ExprVar v = eAgg.getAggVar();
+                    Aggregator agg = eAgg.getAggregator();
+                    aggVarExprMap.put(v, eAgg);
+                });
+                query.getAggregators().addAll(level.opGroup.getAggregators());
             }
 
             ExprTransform varToExpr = new ExprTransformCopy() {
                 @Override
                 public Expr transform(ExprVar nv) {
                     if ( aggVarExprMap.containsKey(nv) )
-                        return aggVarExprMap.get(nv) ;
-                    return nv ;
+                        return aggVarExprMap.get(nv);
+                    return nv;
                 }
-            } ;
+            };
 
             // The assignments will become part of the project.
             // Using VarExprList to preserve order; https://github.com/apache/jena/issues/1369
@@ -281,34 +282,34 @@ public class OpAsQuery {
             if ( level.opExtends != null ) {
                 processExtends(level.opExtends, (var,expr)->{
                     // Internal rename.
-                    expr = rewrite(expr, varToExpr) ;
-                    assignments.add(var, expr) ;
-                }) ;
+                    expr = rewrite(expr, varToExpr);
+                    assignments.add(var, expr);
+                });
 
             }
 
             if ( level.opHaving != null ) {
                 level.opHaving.getExprs().getList().forEach(expr -> {
-                    expr = rewrite(expr, varToExpr) ;
-                    query.getHavingExprs().add(expr) ;
-                }) ;
+                    expr = rewrite(expr, varToExpr);
+                    query.getHavingExprs().add(expr);
+                });
             }
 
             if ( level.opOrder != null ) {
                 level.opOrder.getConditions().forEach(sc -> {
-                    Expr expr = sc.getExpression() ;
-                    expr = rewrite(expr, varToExpr) ;
+                    Expr expr = sc.getExpression();
+                    expr = rewrite(expr, varToExpr);
                     if ( expr == sc.getExpression() )
-                        query.addOrderBy(sc) ;
+                        query.addOrderBy(sc);
                     else
-                        query.addOrderBy(new SortCondition(expr, sc.getDirection())) ;
-                }) ;
+                        query.addOrderBy(new SortCondition(expr, sc.getDirection()));
+                });
             }
 
             if ( level.opProject == null ) {
-                query.setQueryResultStar(true) ;
+                query.setQueryResultStar(true);
                 // No project, Make BINDs
-                //processQueryPattern(op, assignments) ;
+                //processQueryPattern(op, assignments);
 
             } else {
                 // Where assignments and projections align the assignments will become part of the projection
@@ -365,27 +366,26 @@ public class OpAsQuery {
                     int offset = assignVars.indexOf(v);
                     if (offset > projectOffset) {
                         // Note that 'query.addResultVar' handles the case where e is null
-                        query.addResultVar(v, e) ;
+                        query.addResultVar(v, e);
                     }
                     else {
                         // Either the variable did not map to an expression or
                         // the expression was added as BIND - in any case just project the variable
-                        query.addResultVar(v, null) ;
+                        query.addResultVar(v, null);
                     }
                 }
             }
 
             if ( level.opDistinct != null )
-                query.setDistinct(true) ;
+                query.setDistinct(true);
             if ( level.opReduced != null )
-                query.setReduced(true) ;
+                query.setReduced(true);
 
             if ( level.opSlice != null ) {
-                query.setOffset(level.opSlice.getStart()) ;
-                query.setLimit(level.opSlice.getLength()) ;
+                query.setOffset(level.opSlice.getStart());
+                query.setLimit(level.opSlice.getLength());
             }
-            query.resetResultVars() ;
-            return query ;
+            return query;
         }
 
         /**
@@ -395,28 +395,28 @@ public class OpAsQuery {
          */
         private static Op processExtend(Op op, List<OpExtend> assignments) {
             while ( op instanceof OpExtend ) {
-                OpExtend opExtend = (OpExtend)op ;
+                OpExtend opExtend = (OpExtend)op;
                 // JENA-1843
-                assignments.add(0, opExtend) ;
-                op = opExtend.getSubOp() ;
+                assignments.add(0, opExtend);
+                op = opExtend.getSubOp();
             }
-            return op ;
+            return op;
         }
 
         private static void processExtends(List<OpExtend> ext, BiConsumer<Var, Expr> action) {
             ext.forEach(extend->{
-                extend.getVarExprList().forEachVarExpr(action) ;
+                extend.getVarExprList().forEachVarExpr(action);
             });
         }
 
         private static void processAssigns(List<OpAssign> assigns, BiConsumer<Var, Expr> action) {
             assigns.forEach(assign->{
-                assign.getVarExprList().forEachVarExpr(action) ;
+                assign.getVarExprList().forEachVarExpr(action);
             });
         }
 
         private static Expr rewrite(Expr expr, ExprTransform transform) {
-            return ExprTransformer.transform(transform, expr) ;
+            return ExprTransformer.transform(transform, expr);
         }
 
         /**
@@ -424,12 +424,12 @@ public class OpAsQuery {
          * Cleans up the ElementGroup produced.
          */
         private void processQueryPattern(QueryLevelDetails level) {
-            Op op = level.pattern ;
-            op.visit(this) ;
-            ElementGroup eg = this.currentGroup ;
-            Element e = fixupGroupsOfOne(eg) ;
-            query.setQueryPattern(e) ;
-            query.setQuerySelectType() ;
+            Op op = level.pattern;
+            op.visit(this);
+            ElementGroup eg = this.currentGroup;
+            Element e = fixupGroupsOfOne(eg);
+            query.setQueryPattern(e);
+            query.setQuerySelectType();
         }
 
         // Can't distinguish
@@ -439,12 +439,12 @@ public class OpAsQuery {
         // They have the same algebra.
         // This code chooses to use the second form.
         private void processQueryPattern(Op op, List<OpExtend> assignments) {
-            op.visit(this) ;
-            ElementGroup eg = this.currentGroup ;
-            processExtends(assignments,(v,e)->eg.addElement(new ElementBind(v, e)) ) ;
-            Element e = cleanupGroup(eg) ;
-            query.setQueryPattern(e) ;
-            query.setQuerySelectType() ;
+            op.visit(this);
+            ElementGroup eg = this.currentGroup;
+            processExtends(assignments,(v,e)->eg.addElement(new ElementBind(v, e)) );
+            Element e = cleanupGroup(eg);
+            query.setQueryPattern(e);
+            query.setQuerySelectType();
         }
 
         private Element cleanupGroup(ElementGroup eg) {
@@ -454,71 +454,71 @@ public class OpAsQuery {
         }
 
         private Element fixupGroupsOfOne(ElementGroup eg) {
-            ElementTransform transform = new ElementTransformCleanGroupsOfOne() ;
-            ExprTransform exprTransform = new ExprTransformApplyElementTransform(transform) ;
-            Element el2 = ElementTransformer.transform(eg, transform, exprTransform) ;
+            ElementTransform transform = new ElementTransformCleanGroupsOfOne();
+            ExprTransform exprTransform = new ExprTransformApplyElementTransform(transform);
+            Element el2 = ElementTransformer.transform(eg, transform, exprTransform);
             // Top level is always a group or a subquery.
             if ( ! ( el2 instanceof ElementGroup ) && ! ( el2 instanceof ElementSubQuery ) ) {
-                ElementGroup eg2 = new ElementGroup() ;
+                ElementGroup eg2 = new ElementGroup();
                 eg2.addElement(el2);
-                el2 = eg2 ;
+                el2 = eg2;
             }
-            return el2 ;
+            return el2;
         }
 
         private Element asElement(Op op) {
-            ElementGroup g = asElementGroup(op) ;
+            ElementGroup g = asElementGroup(op);
             if ( g.size() == 1 )
-                return g.get(0) ;
-            return g ;
+                return g.get(0);
+            return g;
         }
 
         private ElementGroup asElementGroup(Op op) {
-            startSubGroup() ;
-            op.visit(this) ;
-            return endSubGroup() ;
+            startSubGroup();
+            op.visit(this);
+            return endSubGroup();
         }
 
         @Override
         public void visit(OpBGP opBGP) {
-            currentGroup().addElement(process(opBGP.getPattern())) ;
+            currentGroup().addElement(process(opBGP.getPattern()));
         }
 
         @Override
         public void visit(OpTriple opTriple) {
-            currentGroup().addElement(process(opTriple.getTriple())) ;
+            currentGroup().addElement(process(opTriple.getTriple()));
         }
 
         @Override
         public void visit(OpQuad opQuad) {
-            throw new ARQNotImplemented("OpQuad") ;
+            throw new ARQNotImplemented("OpQuad");
         }
 
         @Override
         public void visit(OpProcedure opProcedure) {
-            throw new ARQNotImplemented("OpProcedure") ;
+            throw new ARQNotImplemented("OpProcedure");
         }
 
         @Override
         public void visit(OpPropFunc opPropFunc) {
-            Node s = processPropFuncArg(opPropFunc.getSubjectArgs()) ;
-            Node o = processPropFuncArg(opPropFunc.getObjectArgs()) ;
-            Triple t = Triple.create(s, opPropFunc.getProperty(), o) ;
-            currentGroup().addElement(process(t)) ;
+            Node s = processPropFuncArg(opPropFunc.getSubjectArgs());
+            Node o = processPropFuncArg(opPropFunc.getObjectArgs());
+            Triple t = Triple.create(s, opPropFunc.getProperty(), o);
+            currentGroup().addElement(process(t));
         }
 
         private Node processPropFuncArg(PropFuncArg args) {
             if ( args.isNode() )
-                return args.getArg() ;
+                return args.getArg();
 
             // List ...
-            List<Node> list = args.getArgList() ;
+            List<Node> list = args.getArgList();
             if ( list.size() == 0 )
-                return RDF.Nodes.nil ;
-            BasicPattern bgp = new BasicPattern() ;
-            Node head = GraphList.listToTriples(list, bgp) ;
-            currentGroup().addElement(process(bgp)) ;
-            return head ;
+                return RDF.Nodes.nil;
+            BasicPattern bgp = new BasicPattern();
+            Node head = GraphList.listToTriples(list, bgp);
+            currentGroup().addElement(process(bgp));
+            return head;
         }
 
         // There is one special case to consider:
@@ -528,118 +528,118 @@ public class OpAsQuery {
 
         @Override
         public void visit(OpSequence opSequence) {
-            ElementGroup g = currentGroup() ;
-            boolean nestGroup = !g.isEmpty() ;
+            ElementGroup g = currentGroup();
+            boolean nestGroup = !g.isEmpty();
             if ( nestGroup ) {
-                startSubGroup() ;
-                g = currentGroup() ;
+                startSubGroup();
+                g = currentGroup();
             }
 
             for ( Op op : opSequence.getElements() ) {
-                Element e = asElement(op) ;
-                insertIntoGroup(g, e) ;
+                Element e = asElement(op);
+                insertIntoGroup(g, e);
             }
 
             if ( nestGroup )
-                endSubGroup() ;
-            return ;
+                endSubGroup();
+            return;
         }
 
         @Override
         public void visit(OpDisjunction opDisjunction) {
             ElementUnion elUnion = new ElementUnion();
             for ( Op op : opDisjunction.getElements() ) {
-                Element el = asElement(op) ;
+                Element el = asElement(op);
                 elUnion.addElement(el);
             }
-            currentGroup().addElement(elUnion) ;
+            currentGroup().addElement(elUnion);
         }
 
         private Element process(BasicPattern pattern) {
             // The different SPARQL versions (1.0, 1.1) use different internal
             // structures for BGPs.
             if ( query.getSyntax() == Syntax.syntaxSPARQL_10 ) {
-                ElementTriplesBlock e = new ElementTriplesBlock() ;
+                ElementTriplesBlock e = new ElementTriplesBlock();
                 for ( Triple t : pattern )
                     // Leave bNode variables as they are
                     // Query serialization will deal with them.
-                    e.addTriple(t) ;
-                return e ;
+                    e.addTriple(t);
+                return e;
             }
 
             if ( query.getSyntax() == Syntax.syntaxSPARQL_12 || query.getSyntax() == Syntax.syntaxSPARQL_11 || query.getSyntax() == Syntax.syntaxARQ ) {
-                ElementPathBlock e = new ElementPathBlock() ;
+                ElementPathBlock e = new ElementPathBlock();
                 for ( Triple t : pattern )
                     // Leave bNode variables as they are
                     // Query serialization will deal with them.
-                    e.addTriple(t) ;
-                return e ;
+                    e.addTriple(t);
+                return e;
             }
 
-            throw new ARQInternalErrorException("Unrecognized syntax: " + query.getSyntax()) ;
+            throw new ARQInternalErrorException("Unrecognized syntax: " + query.getSyntax());
         }
 
         private ElementTriplesBlock process(Triple triple) {
             // Unsubtle
-            ElementTriplesBlock e = new ElementTriplesBlock() ;
-            e.addTriple(triple) ;
-            return e ;
+            ElementTriplesBlock e = new ElementTriplesBlock();
+            e.addTriple(triple);
+            return e;
         }
 
         @Override
         public void visit(OpQuadPattern quadPattern) {
-            Node graphNode = quadPattern.getGraphNode() ;
+            Node graphNode = quadPattern.getGraphNode();
             if ( graphNode.equals(Quad.defaultGraphNodeGenerated) ) {
-                currentGroup().addElement(process(quadPattern.getBasicPattern())) ;
+                currentGroup().addElement(process(quadPattern.getBasicPattern()));
             } else {
-                startSubGroup() ;
-                Element e = asElement(new OpBGP(quadPattern.getBasicPattern())) ;
-                endSubGroup() ;
+                startSubGroup();
+                Element e = asElement(new OpBGP(quadPattern.getBasicPattern()));
+                endSubGroup();
 
                 // If not element group make it one
                 if ( !(e instanceof ElementGroup) ) {
-                    ElementGroup g = new ElementGroup() ;
-                    g.addElement(e) ;
-                    e = g ;
+                    ElementGroup g = new ElementGroup();
+                    g.addElement(e);
+                    e = g;
                 }
 
-                Element graphElt = new ElementNamedGraph(graphNode, e) ;
-                currentGroup().addElement(graphElt) ;
+                Element graphElt = new ElementNamedGraph(graphNode, e);
+                currentGroup().addElement(graphElt);
             }
         }
 
         @Override
         public void visit(OpQuadBlock quadBlock) {
             // Gather into OpQuadPatterns.
-            throw new NotImplemented("OpQuadBlock") ;
+            throw new NotImplemented("OpQuadBlock");
         }
 
         @Override
         public void visit(OpPath opPath) {
-            ElementPathBlock epb = new ElementPathBlock() ;
-            epb.addTriplePath(opPath.getTriplePath()) ;
-            ElementGroup g = currentGroup() ;
-            g.addElement(epb) ;
+            ElementPathBlock epb = new ElementPathBlock();
+            epb.addTriplePath(opPath.getTriplePath());
+            ElementGroup g = currentGroup();
+            g.addElement(epb);
         }
 
         @Override
         public void visit(OpJoin opJoin) {
-            Element eLeft = asElement(opJoin.getLeft()) ;
-            ElementGroup eRightGroup = asElementGroup(opJoin.getRight()) ;
-            Element eRight = eRightGroup ;
+            Element eLeft = asElement(opJoin.getLeft());
+            ElementGroup eRightGroup = asElementGroup(opJoin.getRight());
+            Element eRight = eRightGroup;
             // Very special case. If the RHS is not something that risks
             // reparsing into a combined element of a group, strip the group-of-one.
             // See also ElementTransformCleanGroupsOfOne
             if ( eRightGroup.size() == 1 ) {
                 // This always was a {} around it but it's unnecessary in a group of one.
                 if ( eRightGroup.get(0) instanceof ElementSubQuery )
-                    eRight = eRightGroup.get(0) ;
+                    eRight = eRightGroup.get(0);
             }
 
-            ElementGroup g = currentGroup() ;
-            insertIntoGroup(g, eLeft) ;
-            insertIntoGroup(g, eRight) ;
-            return ;
+            ElementGroup g = currentGroup();
+            insertIntoGroup(g, eLeft);
+            insertIntoGroup(g, eRight);
+            return;
         }
 
         @Override
@@ -648,72 +648,70 @@ public class OpAsQuery {
         }
 
         private void convertLeftJoin(Op opLeft, Op opRight, ExprList exprs) {
-            Element eLeft = asElement(opLeft) ;
-            ElementGroup eRight = asElementGroup(opRight) ;
+            Element eLeft = asElement(opLeft);
+            ElementGroup eRight = asElementGroup(opRight);
 
             // If the RHS is (filter) we need to protect it from becoming
             // part of the expr for the LeftJoin.
             // OPTIONAL {{ ?s ?p ?o FILTER (?o>34) }} is not the same as
             // OPTIONAL { ?s ?p ?o FILTER (?o>34) }
 
-            boolean mustProtect = eRight.getElements().stream().anyMatch(el -> el instanceof ElementFilter ) ;
+            boolean mustProtect = eRight.getElements().stream().anyMatch(el -> el instanceof ElementFilter );
 
             if ( mustProtect ) {
-                ElementGroup eRight2 = new ElementGroup() ;
+                ElementGroup eRight2 = new ElementGroup();
                 eRight2.addElement(eRight);
-                eRight = eRight2 ;
+                eRight = eRight2;
             }
 
             if ( exprs != null ) {
                 for ( Expr expr : exprs ) {
-                    ElementFilter f = new ElementFilter(expr) ;
-                    eRight.addElement(f) ;
+                    ElementFilter f = new ElementFilter(expr);
+                    eRight.addElement(f);
                 }
             }
-            ElementGroup g = currentGroup() ;
+            ElementGroup g = currentGroup();
             if ( !emptyGroup(eLeft) ) {
                 if ( eLeft instanceof ElementGroup )
-                    g.getElements().addAll(((ElementGroup)eLeft).getElements()) ;
+                    g.getElements().addAll(((ElementGroup)eLeft).getElements());
                 else
-                    g.addElement(eLeft) ;
+                    g.addElement(eLeft);
             }
 
-            ElementOptional opt = new ElementOptional(eRight) ;
-            g.addElement(opt) ;
+            ElementOptional opt = new ElementOptional(eRight);
+            g.addElement(opt);
         }
 
         @Override
         public void visit(OpMinus opMinus) {
-            Element eLeft = asElement(opMinus.getLeft()) ;
-            Element eRight = asElementGroup(opMinus.getRight()) ;
-            ElementMinus elMinus = new ElementMinus(eRight) ;
-            ElementGroup g = currentGroup() ;
+            Element eLeft = asElement(opMinus.getLeft());
+            Element eRight = asElementGroup(opMinus.getRight());
+            ElementMinus elMinus = new ElementMinus(eRight);
+            ElementGroup g = currentGroup();
             if ( !emptyGroup(eLeft) )
-                g.addElement(eLeft) ;
-            g.addElement(elMinus) ;
+                g.addElement(eLeft);
+            g.addElement(elMinus);
         }
 
         @Override
         public void visit(OpUnion opUnion) {
-            Element eLeft = asElementGroup(opUnion.getLeft()) ;
-            Element eRight = asElementGroup(opUnion.getRight()) ;
+            Element eLeft = asElementGroup(opUnion.getLeft());
+            Element eRight = asElementGroup(opUnion.getRight());
             if ( eLeft instanceof ElementUnion ) {
-                ElementUnion elUnion = (ElementUnion)eLeft ;
-                elUnion.addElement(eRight) ;
-                return ;
+                ElementUnion elUnion = (ElementUnion)eLeft;
+                elUnion.addElement(eRight);
+                return;
             }
-            // Multiple unions.
-            // if ( eRight instanceof ElementUnion )
-            // {
-            // ElementUnion elUnion = (ElementUnion)eRight ;
-            // elUnion.getElements().add(0, eLeft) ;
-            // return ;
-            // }
+//            // Multiple unions.
+//            if ( eRight instanceof ElementUnion elUnion) {
+//                elUnion.getElements().add(0, eLeft);
+//                return;
+//            }
 
-            ElementUnion elUnion = new ElementUnion() ;
-            elUnion.addElement(eLeft) ;
-            elUnion.addElement(eRight) ;
-            currentGroup().addElement(elUnion) ;
+            ElementUnion elUnion = new ElementUnion();
+            elUnion.addElement(eLeft);
+            elUnion.addElement(eRight);
+            currentGroup().addElement(elUnion);
         }
 
         @Override
@@ -732,17 +730,17 @@ public class OpAsQuery {
 
         // Op2 where the syntax form is "left WORD { right }"
         private void visitGroupPatternRight(Op2 op2, Function<ElementGroup, Element> functionRightToElement) {
-            Element eLeft = asElement(op2.getLeft()) ;
-            ElementGroup eRight = asElementGroup(op2.getRight()) ;
-            ElementGroup g = currentGroup() ;
+            Element eLeft = asElement(op2.getLeft());
+            ElementGroup eRight = asElementGroup(op2.getRight());
+            ElementGroup g = currentGroup();
             if ( !emptyGroup(eLeft) ) {
                 if ( eLeft instanceof ElementGroup )
-                    g.getElements().addAll(((ElementGroup)eLeft).getElements()) ;
+                    g.getElements().addAll(((ElementGroup)eLeft).getElements());
                 else
-                    g.addElement(eLeft) ;
+                    g.addElement(eLeft);
             }
-            Element elt = functionRightToElement.apply(eRight) ;
-            g.addElement(elt) ;
+            Element elt = functionRightToElement.apply(eRight);
+            g.addElement(elt);
         }
 
         @Override
@@ -756,118 +754,118 @@ public class OpAsQuery {
         public void visit(OpFilter opFilter) {
             // (filter .. (filter ( ... )) (non-canonicalizing OpFilters)
             // Inner gets Grouped unnecessarily.
-            Element e = asElement(opFilter.getSubOp()) ;
+            Element e = asElement(opFilter.getSubOp());
             if ( currentGroup() != e )
-                currentGroup().addElement(e) ;
-            element = currentGroup() ; // Was cleared by asElement.
+                currentGroup().addElement(e);
+            element = currentGroup(); // Was cleared by asElement.
 
-            ExprList exprs = opFilter.getExprs() ;
+            ExprList exprs = opFilter.getExprs();
             for ( Expr expr : exprs ) {
-                ElementFilter f = new ElementFilter(expr) ;
-                currentGroup().addElement(f) ;
+                ElementFilter f = new ElementFilter(expr);
+                currentGroup().addElement(f);
             }
         }
 
         @Override
         public void visit(OpGraph opGraph) {
-            startSubGroup() ;
-            Element e = asElement(opGraph.getSubOp()) ;
-            endSubGroup() ;
+            startSubGroup();
+            Element e = asElement(opGraph.getSubOp());
+            endSubGroup();
 
             // If not element group make it one
             if ( !(e instanceof ElementGroup) ) {
-                ElementGroup g = new ElementGroup() ;
-                g.addElement(e) ;
-                e = g ;
+                ElementGroup g = new ElementGroup();
+                g.addElement(e);
+                e = g;
             }
 
-            Element graphElt = new ElementNamedGraph(opGraph.getNode(), e) ;
-            currentGroup().addElement(graphElt) ;
+            Element graphElt = new ElementNamedGraph(opGraph.getNode(), e);
+            currentGroup().addElement(graphElt);
         }
 
         @Override
         public void visit(OpService opService) {
             // Hmm - if the subnode has been optimized, we may fail.
-            Op op = opService.getSubOp() ;
-            Element x = asElement(opService.getSubOp()) ;
-            Element elt = new ElementService(opService.getService(), x, opService.getSilent()) ;
-            currentGroup().addElement(elt) ;
+            Op op = opService.getSubOp();
+            Element x = asElement(opService.getSubOp());
+            Element elt = new ElementService(opService.getService(), x, opService.getSilent());
+            currentGroup().addElement(elt);
         }
 
         @Override
         public void visit(OpDatasetNames dsNames) {
-            throw new ARQNotImplemented("OpDatasetNames") ;
+            throw new ARQNotImplemented("OpDatasetNames");
         }
 
         @Override
         public void visit(OpTable opTable) {
             // This will go in a group so simply forget it.
             if ( opTable.isJoinIdentity() )
-                return ;
+                return;
 
             // Put in a VALUES
             // This may be related to the grpup of the overall query.
 
-            ElementData el = new ElementData() ;
-            el.getVars().addAll(opTable.getTable().getVars()) ;
-            QueryIterator qIter = opTable.getTable().iterator(null) ;
+            ElementData el = new ElementData();
+            el.getVars().addAll(opTable.getTable().getVars());
+            QueryIterator qIter = opTable.getTable().iterator(null);
             while (qIter.hasNext())
-                el.getRows().add(qIter.next()) ;
-            qIter.close() ;
-            currentGroup().addElement(el) ;
+                el.getRows().add(qIter.next());
+            qIter.close();
+            currentGroup().addElement(el);
         }
 
         @Override
         public void visit(OpExt opExt) {
-            // Op op = opExt.effectiveOp() ;
+            // Op op = opExt.effectiveOp();
             // // This does not work in all cases.
-            // op.visit(this) ;
-            throw new ARQNotImplemented("OpExt") ;
+            // op.visit(this);
+            throw new ARQNotImplemented("OpExt");
         }
 
         @Override
         public void visit(OpNull opNull) {
-            throw new ARQNotImplemented("OpNull") ;
+            throw new ARQNotImplemented("OpNull");
         }
 
         @Override
         public void visit(OpLabel opLabel) {
             if ( opLabel.hasSubOp() )
-                opLabel.getSubOp().visit(this) ;
+                opLabel.getSubOp().visit(this);
         }
 
         @Override
         public void visit(OpAssign opAssign) {
-            Element e = asElement(opAssign.getSubOp()) ;
+            Element e = asElement(opAssign.getSubOp());
             // If (assign ... (table unit)), and first in group, don't add the empty group.
-            insertIntoGroup(currentGroup(), e) ;
+            insertIntoGroup(currentGroup(), e);
             processAssigns(Arrays.asList(opAssign), (var,expr)->{
-                currentGroup().addElement(new ElementAssign(var,expr)) ;
-            }) ;
+                currentGroup().addElement(new ElementAssign(var,expr));
+            });
         }
 
         @Override
         public void visit(OpExtend opExtend) {
-            Element e = asElement(opExtend.getSubOp()) ;
+            Element e = asElement(opExtend.getSubOp());
             // If (extend ... (table unit)), and first in group, don't add the empty group.
-            insertIntoGroup(currentGroup(), e) ;
+            insertIntoGroup(currentGroup(), e);
             processExtends(Arrays.asList(opExtend), (var,expr)->{
-                currentGroup().addElement(new ElementBind(var,expr)) ;
-            }) ;
+                currentGroup().addElement(new ElementBind(var,expr));
+            });
         }
 
         @Override
         public void visit(OpUnfold opUnfold) {
-            Element e = asElement(opUnfold.getSubOp()) ;
+            Element e = asElement(opUnfold.getSubOp());
             // If (unfold ... (table unit)), and first in group, don't add the empty group.
-            insertIntoGroup(currentGroup(), e) ;
-            Element elmtUnfold = new ElementUnfold( opUnfold.getExpr(), opUnfold.getVar1(), opUnfold.getVar2() ) ;
-            currentGroup().addElement(elmtUnfold) ;
+            insertIntoGroup(currentGroup(), e);
+            Element elmtUnfold = new ElementUnfold( opUnfold.getExpr(), opUnfold.getVar1(), opUnfold.getVar2() );
+            currentGroup().addElement(elmtUnfold);
         }
 
         @Override
         public void visit(OpList opList) {
-            opList.getSubOp().visit(this) ;
+            opList.getSubOp().visit(this);
         }
 
         // When some modifers (e.g. OpDistinct) are met in a pattern, they signal
@@ -875,172 +873,171 @@ public class OpAsQuery {
         // looking for the level start in
 
         private void newLevel(Op op) {
-            convertAsSubQuery(op) ;
+            convertAsSubQuery(op);
         }
 
         private void convertAsSubQuery(Op op) {
             // Reverse scoped renaming.
             // ?/x is illegal so the original query string must have had ?x at this point for any number of "/"
             Op op1 = Rename.reverseVarRename(op, true);
-            Converter subConverter = new Converter(op1) ;
-            ElementSubQuery subQuery = new ElementSubQuery(subConverter.convert()) ;
-            ElementGroup g = currentGroup() ;
-            g.addElement(subQuery) ;
+            Converter subConverter = new Converter(op1);
+            ElementSubQuery subQuery = new ElementSubQuery(subConverter.convert());
+            ElementGroup g = currentGroup();
+            g.addElement(subQuery);
         }
 
         @Override
         public void visit(OpOrder opOrder) {
-            newLevel(opOrder) ;
+            newLevel(opOrder);
         }
 
         @Override
         public void visit(OpProject opProject) {
-            newLevel(opProject) ;
+            newLevel(opProject);
         }
 
         @Override
         public void visit(OpReduced opReduced) {
-            newLevel(opReduced) ;
+            newLevel(opReduced);
         }
 
         @Override
         public void visit(OpDistinct opDistinct) {
-            newLevel(opDistinct) ;
+            newLevel(opDistinct);
         }
 
         @Override
         public void visit(OpSlice opSlice) {
-            newLevel(opSlice) ;
+            newLevel(opSlice);
         }
 
         @Override
         public void visit(OpGroup opGroup) {
-            newLevel(opGroup) ;
+            newLevel(opGroup);
         }
 
         @Override
         public void visit(OpTopN opTop) {
-            throw new ARQNotImplemented("OpTopN") ;
+            throw new ARQNotImplemented("OpTopN");
         }
 
         private static boolean emptyGroup(Element element) {
             if ( !(element instanceof ElementGroup) )
-                return false ;
-            ElementGroup eg = (ElementGroup)element ;
-            return eg.isEmpty() ;
+                return false;
+            ElementGroup eg = (ElementGroup)element;
+            return eg.isEmpty();
         }
 
         private static boolean groupOfOne(Element element) {
             if ( !(element instanceof ElementGroup) )
-                return false ;
-            ElementGroup eg = (ElementGroup)element ;
-            return eg.size() == 1 ;
+                return false;
+            ElementGroup eg = (ElementGroup)element;
+            return eg.size() == 1;
         }
 
         /** Insert into a group, skip initial empty subgroups; recombining ElementPathBlock */
         private static void insertIntoGroup(ElementGroup eg, Element e) {
             // Skip initial empty subgroup.
             if ( emptyGroup(e) && eg.isEmpty() )
-                return ;
+                return;
 
             // Empty group.
             if ( eg.isEmpty() ) {
                 eg.addElement(e);
-                return ;
+                return;
             }
 
-            Element eltTop = eg.getLast() ;
+            Element eltTop = eg.getLast();
             if ( ! ( eltTop instanceof ElementPathBlock ) ) {
                 // Not working on a ElementPathBlock - no need to group-of-one
                 // when inserting ElementPathBlock.
-                e = unwrapGroupOfOnePathBlock(e) ;
+                e = unwrapGroupOfOnePathBlock(e);
                 eg.addElement(e);
-                return ;
+                return;
             }
             if ( ! ( e  instanceof ElementPathBlock ) ) {
                 eg.addElement(e);
-                return ;
+                return;
             }
             // Combine.
-            ElementPathBlock currentPathBlock = (ElementPathBlock)eltTop ;
-            ElementPathBlock newPathBlock = (ElementPathBlock)e ;
+            ElementPathBlock currentPathBlock = (ElementPathBlock)eltTop;
+            ElementPathBlock newPathBlock = (ElementPathBlock)e;
             currentPathBlock.getPattern().addAll(newPathBlock.getPattern());
         }
 
         private static Element unwrapGroupOfOnePathBlock(Element e) {
-            Element e2 = getElementOfGroupOfOne(e) ;
+            Element e2 = getElementOfGroupOfOne(e);
             if ( e2 != null )
-                return e2 ;
-            return e ;
+                return e2;
+            return e;
         }
 
         private static Element getElementOfGroupOfOne(Element e) {
             if ( groupOfOne(e) ) {
-                ElementGroup eg = (ElementGroup)e ;
-                return eg.get(0) ;
+                ElementGroup eg = (ElementGroup)e;
+                return eg.get(0);
             }
-            return null ;
+            return null;
         }
 
         private Element lastElement() {
-            ElementGroup g = currentGroup ;
-            return g.getLast() ;
+            ElementGroup g = currentGroup;
+            return g.getLast();
         }
 
         private void startSubGroup() {
-            push(currentGroup) ;
-            ElementGroup g = new ElementGroup() ;
-            currentGroup = g ;
+            push(currentGroup);
+            ElementGroup g = new ElementGroup();
+            currentGroup = g;
         }
 
         private ElementGroup endSubGroup() {
-            ElementGroup g = pop() ;
-            ElementGroup r = currentGroup ;
-            currentGroup = g ;
-            return r ;
+            ElementGroup g = pop();
+            ElementGroup r = currentGroup;
+            currentGroup = g;
+            return r;
         }
 
         private ElementGroup currentGroup() {
             // if ( currentGroup == null )
-            // startSubGroup() ;
-            return currentGroup ;
+            // startSubGroup();
+            return currentGroup;
         }
 
         private ElementGroup peek() {
             if ( stack.size() == 0 )
-                return null ;
-            return stack.peek() ;
+                return null;
+            return stack.peek();
         }
 
         private ElementGroup pop() {
-            return stack.pop() ;
+            return stack.pop();
         }
 
         private void push(ElementGroup el) {
-            stack.push(el) ;
+            stack.push(el);
         }
     }
 
-
     /**
      * Allows multiple filters and any number of extend
      */
     private static OpGroup getGroup(Op op) {
         // Unwind tail recursion to protected against extreme queries.
-        for ( ; ; ) {
+        for ( ;; ) {
             if ( op instanceof OpGroup )
-                return (OpGroup)op ;
+                return (OpGroup)op;
             if ( op instanceof OpFilter ) {
-                OpFilter opFilter = (OpFilter)op ;
-                op = opFilter.getSubOp() ;
-                continue ;
+                OpFilter opFilter = (OpFilter)op;
+                op = opFilter.getSubOp();
+                continue;
             }
             if ( op instanceof OpExtend ) { // AS or Aggregate naming
-                OpExtend opExtend = (OpExtend)op ;
-                op = opExtend.getSubOp() ;
-                continue ;
+                OpExtend opExtend = (OpExtend)op;
+                op = opExtend.getSubOp();
+                continue;
             }
-            return null ;
+            return null;
         }
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
index eba7f1e841..bd7eeec900 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/QueryCompare.java
@@ -37,8 +37,8 @@ public class QueryCompare implements QueryVisitor
         if ( query1 == query2 )
             return true;
 
-        query1.setResultVars();
-        query2.setResultVars();
+        query1.ensureResultVars();
+        query2.ensureResultVars();
         QueryCompare visitor = new QueryCompare(query1);
         try {
             query2.visit(visitor);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java
index d2158eba3e..a0b11105ba 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java
@@ -52,7 +52,7 @@ public abstract class QueryEngineBase implements OpEval, Closeable
     protected QueryEngineBase(Query query, DatasetGraph dsg, Binding input, Context cxt) {
         this(dsg, input, cxt);
         this.query = query;
-        query.setResultVars();
+        query.ensureResultVars();
         // Unoptimized so far.
         setOp(createOp(query));
         dataset = prepareDataset(dsg, query);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java
index 8f0de2b0b1..32fdd21407 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDataset.java
@@ -178,6 +178,16 @@ public class QueryExecDataset implements QueryExec
         return RowSetStream.create(vars, queryIterator);
     }
 
+    private void fixupResultVars() {
+        if ( initialBinding != null && ! initialBinding.isEmpty()) {
+            //query = query.cloneQuery();
+            synchronized(query) {
+                query.setQueryResultStar(true);
+                //query.resetResultVars();
+            }
+        }
+    }
+
     // -- Construct
     @Override
     public Graph construct(Graph graph) {
@@ -201,7 +211,8 @@ public class QueryExecDataset implements QueryExec
             throw new QueryExecException("Attempt to get a CONSTRUCT model from a " + labelForQuery(query) + " query");
         // This causes there to be no PROJECT around the pattern.
         // That in turn, exposes the initial bindings.
-        query.setQueryResultStar(true);
+
+        fixupResultVars();
 
         startQueryIterator();
 
@@ -218,7 +229,8 @@ public class QueryExecDataset implements QueryExec
             throw new QueryExecException("Attempt to get a CONSTRUCT model from a " + labelForQuery(query) + " query");
         // This causes there to be no PROJECT around the pattern.
         // That in turn, exposes the initial bindings.
-        query.setQueryResultStar(true);
+
+        fixupResultVars();
 
         startQueryIterator();
 
@@ -248,12 +260,14 @@ public class QueryExecDataset implements QueryExec
 
         if ( !query.isDescribeType() )
             throw new QueryExecException("Attempt to get a DESCRIBE result from a " + labelForQuery(query) + " query");
-        query.setResultVars();
+
         // If there was no WhereClause, use an empty pattern (one solution, no
         // columns).
         if ( query.getQueryPattern() == null )
             query.setQueryPattern(new ElementGroup());
 
+        fixupResultVars();
+
         Set<Node> set = new HashSet<>();
 
         RowSet rows = execute();
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDatasetBuilder.java b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDatasetBuilder.java
index d2d7aa6989..4c3d84b659 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDatasetBuilder.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecDatasetBuilder.java
@@ -190,7 +190,7 @@ public class QueryExecDatasetBuilder implements QueryExecMod, QueryExecBuilder {
         Objects.requireNonNull(query, "No query for QueryExec");
         // Queries can have FROM/FROM NAMED or VALUES to get data.
         //Objects.requireNonNull(dataset, "No dataset for QueryExec");
-        query.setResultVars();
+        query.ensureResultVars();
         Context cxt = getContext();
 
         QueryEngineFactory qeFactory = QueryEngineRegistry.findFactory(query, dataset, cxt);
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/exec/http/Service.java b/jena-arq/src/main/java/org/apache/jena/sparql/exec/http/Service.java
index 2b6150c719..d65db0b21b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/exec/http/Service.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/exec/http/Service.java
@@ -169,7 +169,7 @@ public class Service {
             else {
                 query = QueryFactory.create();
                 query.setQueryPattern(el);
-                query.setResultVars();
+                query.ensureResultVars();
             }
         }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParser.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParser.java
index 27d5e9b958..0603572b5d 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParser.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParser.java
@@ -32,7 +32,6 @@ public abstract class SPARQLParser
     public final Query parse(Query query, String queryString) throws QueryParseException {
         Query parsedQuery = parse$(query, queryString);
         validateParsedQuery(parsedQuery);
-        parsedQuery.resetResultVars();
         return parsedQuery;
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java
index 13d4aaa47c..839e9dc196 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SPARQLParserBase.java
@@ -82,7 +82,9 @@ public class SPARQLParserBase extends QueryParserBase {
     // Signal start/finish of units
 
     protected void startQuery() {}
-    protected void finishQuery() {}
+    protected void finishQuery() {
+        query.ensureResultVars();
+    }
 
     protected void startUpdateRequest()    {}
     protected void finishUpdateRequest()   {}
@@ -205,8 +207,9 @@ public class SPARQLParserBase extends QueryParserBase {
 
     protected Query newSubQuery(Prologue progloue) {
         // The parser uses the same prologue throughout the parsing process.
-        // For printing purposes, the subquery must not have a prologue of its own.
-        return new Query();
+        Query subQuery = new Query();
+        subQuery.setSyntax(query.getSyntax());
+        return subQuery;
     }
 
     protected void popQuery() {
@@ -217,6 +220,8 @@ public class SPARQLParserBase extends QueryParserBase {
         Query subQuery = query;
         if ( ! subQuery.isSelectType() )
             throwParseException("Subquery not a SELECT query", line, column);
+        // Sort out SELECT *
+        subQuery.ensureResultVars();
         popQuery();
         return subQuery;
     }
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 ae0dd626e5..3bc9b71039 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
@@ -554,7 +554,7 @@ public class UpdateEngineWorker implements UpdateVisitor
         query.setQueryPattern(pattern);
         query.setQuerySelectType();
         query.setQueryResultStar(true);
-        query.resetResultVars();
+        query.ensureResultVars();
         return query;
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/PatternVars.java b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/PatternVars.java
index e0a5eefc78..fdeafc7cae 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/PatternVars.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/PatternVars.java
@@ -106,7 +106,6 @@ public class PatternVars {
 
         @Override
         public void visit(ElementSubQuery el) {
-            el.getQuery().setResultVars();
             VarExprList x = el.getQuery().getProject();
             acc.addAll(x.getVars());
         }
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
index 3067f3a938..9afc9d2472 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/QueryTransformOps.java
@@ -138,10 +138,8 @@ public class QueryTransformOps {
         if (q2.getOrderBy() != null)
             mutateSortConditions(q2.getOrderBy(), exprTransform);
         mutateQueryPattern(q2, transform, exprTransform);
-        if ( query.isQueryResultStar() ) {
-            // Reset internal to only what now can be seen.
-            q2.resetResultVars();
-        }
+        // Reset internal to only what now can be seen.
+        q2.resetResultVars();
         setAggregators(q2, query, exprTransform);
         return q2;
     }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestOpAsQuery.java b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestOpAsQuery.java
index af58d9c09d..701cabb3ac 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestOpAsQuery.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestOpAsQuery.java
@@ -667,7 +667,7 @@ public class TestOpAsQuery {
         Query outcome = r[1];
         stripNamespacesAndBase(input) ;
         stripNamespacesAndBase(outcome) ;
-        // Check it can be translated to alegbra.
+        // Check it can be translated to algebra.
         Op op1 = Algebra.compile(input);
         Op op2 = Algebra.compile(outcome);
         // Oops - breaks testModifiersOnSubQuery2 (REDUCED lost but DISTINCT is OK).
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TS_Syntax.java b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TS_Syntax.java
index a67c6562be..61f1aa65be 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TS_Syntax.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TS_Syntax.java
@@ -26,6 +26,7 @@ import org.apache.jena.sparql.syntax.syntaxtransform.*;
 @Suite
 @SelectClasses({
     TestQueryParser.class
+    , TestQueryProjectVars.class
     , TestSerialization.class
     , TestQueryShallowCopy.class
     , TestQuerySubstituteScope.class
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java
index 1f81373deb..6f447f9d61 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryParser.java
@@ -125,7 +125,7 @@ public class TestQueryParser {
 
     // Test that a URI string can be used in Turtle data
     // and in SPARQL in the same way.
-    public static void testParseIRIs(String string) {
+    private static void testParseIRIs(String string) {
         silent(()->QueryFactory.create("SELECT * { "+string+" a <http://example/TYPE> }"));
     }
 }
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryProjectVars.java b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryProjectVars.java
new file mode 100644
index 0000000000..038b86aa6e
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/syntax/TestQueryProjectVars.java
@@ -0,0 +1,58 @@
+/*
+ * 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.syntax;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryFactory;
+
+public class TestQueryProjectVars {
+
+    @Test public void testProjectVars_01() { testCalculatedProjectVars("SELECT * { SELECT * { ?s :p :o } }", "s"); }
+    @Test public void testProjectVars_02() { testCalculatedProjectVars("SELECT * { :x :y :z  { SELECT ?s { ?s :p :o } } }", "s"); }
+
+    @Test public void testProjectVars_03() { testCalculatedProjectVars("SELECT * { :x :y ?z  { SELECT ?s { ?s :p :o } } }", "s", "z"); }
+    @Test public void testProjectVars_04() { testCalculatedProjectVars("SELECT * { :x :y ?z  { SELECT * { ?s :p :o } } }", "s", "z"); }
+    @Test public void testProjectVars_05() { testCalculatedProjectVars("SELECT ?s { :x :y ?z  { SELECT ?s { ?s :p :o } } }", "s"); }
+    @Test public void testProjectVars_06() { testCalculatedProjectVars("SELECT ?z { :x :y ?z  { SELECT ?s { ?s :p :o } } }", "z"); }
+
+    @Test public void testProjectVars_07() { testCalculatedProjectVars("SELECT ?z { :x :y ?z  { SELECT ?s { ?s :p :o } GROUP BY ?s } }", "z"); }
+    @Test public void testProjectVars_08() { testCalculatedProjectVars("SELECT ?s { :x :y ?z  { SELECT ?s { ?s :p :o } GROUP BY ?s } }", "s"); }
+    @Test public void testProjectVars_09() { testCalculatedProjectVars("SELECT ?z { :x :y ?z  { SELECT * { ?s :p :o } GROUP BY ?s } }", "z"); }
+
+    // syntax-group-01.arq
+    @Test public void testProjectVars_20() { testCalculatedProjectVars("SELECT * { ?x :p ?p } GROUP BY ?p", "p"); }
+
+    private static String PREFIXES = "PREFIX : <http://example/>\n";
+    private static void testCalculatedProjectVars(String queryString, String... expected) {
+        Query query = QueryFactory.create(PREFIXES+queryString);
+
+        List<String> actualVars = query.getResultVars();
+        List<String> expectedResultVars = List.of(expected);
+
+        assertTrue( CollectionUtils.isEqualCollection(actualVars, expectedResultVars),
+                    ()->String.format("Different: query=%s, expected=%s actual=%s\n", queryString, expectedResultVars, actualVars));
+    }
+}
diff --git a/jena-examples/src/main/java/arq/examples/riot/ExRIOT5_StreamRDFCollect.java b/jena-examples/src/main/java/arq/examples/riot/ExRIOT5_StreamRDFCollect.java
index 8ed5bbebf7..97b1ecf39b 100644
--- a/jena-examples/src/main/java/arq/examples/riot/ExRIOT5_StreamRDFCollect.java
+++ b/jena-examples/src/main/java/arq/examples/riot/ExRIOT5_StreamRDFCollect.java
@@ -20,7 +20,6 @@ package arq.examples.riot;
 
 import org.apache.jena.graph.Triple;
 import org.apache.jena.riot.RDFParser;
-import org.apache.jena.riot.lang.CollectorStreamBase;
 import org.apache.jena.riot.lang.CollectorStreamRDF;
 
 /**
diff --git a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
index 50d0bf038e..eb628daa35 100644
--- a/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
+++ b/jena-extras/jena-querybuilder/src/main/java/org/apache/jena/arq/querybuilder/AbstractQueryBuilder.java
@@ -569,7 +569,7 @@ public abstract class AbstractQueryBuilder<T extends AbstractQueryBuilder<T>>
 
         handlerBlock.build();
 
-        q.resetResultVars();
+        q.ensureResultVars();
         return q;
     }
 
@@ -600,7 +600,7 @@ public abstract class AbstractQueryBuilder<T extends AbstractQueryBuilder<T>>
         final HandlerBlock hb = new HandlerBlock(retval);
         final HandlerBlock hb2 = new HandlerBlock(q2);
         hb.addAll(hb2);
-        q2.resetResultVars();
+        q2.ensureResultVars();
         return retval;
     }