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 <[email protected]>
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;
     }
 

Reply via email to