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; }
