From daaa38ff412a7ba1bb4a09fc783148275a8b23cf Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Sat, 19 Nov 2022 21:48:12 +0000 Subject: [PATCH] GH-1615: Variable scope checking for LATERAL --- .../jena/sparql/lang/SyntaxVarScope.java | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java index 38d7f960afd..cb79fd01ce8 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/SyntaxVarScope.java @@ -222,12 +222,15 @@ public void visit(ElementGroup el) { // Tests. if ( e instanceof ElementBind ) { Collection accScope = calcScopeAll(el.getElements(), i); - check(accScope, (ElementBind)e); + checkBIND(accScope, (ElementBind)e); } - if ( e instanceof ElementService ) { Collection accScope = calcScopeAll(el.getElements(), i); - check(accScope, (ElementService)e); + checkSERVICE(accScope, (ElementService)e); + } + if ( e instanceof ElementLateral ) { + Collection accScope = calcScopeAll(el.getElements(), i); + checkLATERAL(accScope, e); } } } @@ -246,19 +249,64 @@ private static Collection calcScope(List elements, int start, int return accScope; } - private static void check(Collection scope, ElementBind el) { + private static void checkBIND(Collection scope, ElementBind el) { Var var = el.getVar(); if ( scope.contains(var) ) throw new QueryParseException("BIND: Variable used when already in-scope: " + var + " in " + el, -1, -1); checkExpr(scope, el.getExpr(), var); } - private static void check(Collection scope, ElementService el) { + private static void checkSERVICE(Collection scope, ElementService el) { if ( ARQ.isStrictMode() && el.getServiceNode().isVariable() ) { Var var = Var.alloc(el.getServiceNode()); if ( !scope.contains(var) ) throw new QueryParseException("SERVICE: Variable not already in-scope: " + var + " in " + el, -1, -1); } } + + private void checkLATERAL(Collection accScope, Element el) { + // Look for BIND/VALUES/SELECT-AS inside LATERAL + ElementVisitor checker = new ElementVisitorBase() { + @Override + public void visit(ElementBind eltBind) { + if ( accScope.contains(eltBind.getVar()) ) + throw new QueryParseException("BIND: Variable " + eltBind.getVar() + " defined", -1, -1); + } + // @Override public void visit(ElementAssign eltAssign) {} -- LET - + // always OK + + @Override + public void visit(ElementData eltData) { + eltData.getVars().forEach(v -> { + if ( accScope.contains(v) ) + throw new QueryParseException("VALUES: Variable " + v + " defined", -1, -1); + }); + } + + @Override + public void visit(ElementSubQuery eltSubQuery) { + // Only called when there is an expression. + eltSubQuery.getQuery().getProject().forEachExpr((var, expr) -> { + if ( accScope.contains(var) ) + throw new QueryParseException("SELECT: Variable " + var + " defined", -1 ,-1); + }); + // Check inside query pattern + Query subQuery = eltSubQuery.getQuery(); + Collection accScope2 = accScope; + //eltSubQuery.getQuery().setResultVars(); + if ( ! subQuery.isQueryResultStar() ) { + List projectVars = eltSubQuery.getQuery().getProject().getVars(); + // Calculate variables passed down : scope which are in the project vars. + accScope2 = new ArrayList<>(accScope); + accScope2.removeIf(v->!projectVars.contains(v)); + } + Element el2 = eltSubQuery.getQuery().getQueryPattern(); + checkLATERAL(accScope2, el2); + } + }; + + // Does not walk into subqueries but we need to change the scoep for sub-queries. + ElementWalker.walk(el, checker); + } } }