Skip to content

Commit

Permalink
apacheGH-1615: Variable scope checking for LATERAL
Browse files Browse the repository at this point in the history
  • Loading branch information
afs committed Nov 21, 2022
1 parent 5b1ec78 commit daaa38f
Showing 1 changed file with 53 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,15 @@ public void visit(ElementGroup el) {
// Tests.
if ( e instanceof ElementBind ) {
Collection<Var> accScope = calcScopeAll(el.getElements(), i);
check(accScope, (ElementBind)e);
checkBIND(accScope, (ElementBind)e);
}

if ( e instanceof ElementService ) {
Collection<Var> accScope = calcScopeAll(el.getElements(), i);
check(accScope, (ElementService)e);
checkSERVICE(accScope, (ElementService)e);
}
if ( e instanceof ElementLateral ) {
Collection<Var> accScope = calcScopeAll(el.getElements(), i);
checkLATERAL(accScope, e);
}
}
}
Expand All @@ -246,19 +249,64 @@ private static Collection<Var> calcScope(List<Element> elements, int start, int
return accScope;
}

private static void check(Collection<Var> scope, ElementBind el) {
private static void checkBIND(Collection<Var> 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<Var> scope, ElementService el) {
private static void checkSERVICE(Collection<Var> 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<Var> 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<Var> accScope2 = accScope;
//eltSubQuery.getQuery().setResultVars();
if ( ! subQuery.isQueryResultStar() ) {
List<Var> 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);
}
}
}

0 comments on commit daaa38f

Please sign in to comment.