From 334042e792da60c9dfc18ec6bf29c9a1c67eb652 Mon Sep 17 00:00:00 2001 From: "damyan.ognyanov" Date: Tue, 31 Oct 2023 12:53:08 +0200 Subject: [PATCH] GH-4811 process also optional TEs when handling BIND Signed-off-by: damyan.ognyanov --- .../query/parser/sparql/GraphPattern.java | 18 +++++++--- .../query/parser/sparql/TupleExprBuilder.java | 2 ++ .../parser/sparql/TupleExprBuilderTest.java | 35 +++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/GraphPattern.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/GraphPattern.java index fb3c20a8b76..a2f9eff342f 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/GraphPattern.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/GraphPattern.java @@ -103,6 +103,7 @@ public void addRequiredTE(TupleExpr te) { */ void clearRequiredTEs() { requiredTEs.clear(); + optionalTEs.clear(); } public void addRequiredSP(Var subjVar, Var predVar, Var objVar) { @@ -173,6 +174,18 @@ public void clear() { public TupleExpr buildTupleExpr() { TupleExpr result = buildJoinFromRequiredTEs(); + result = buildOptionalTE(result); + + for (ValueExpr constraint : constraints) { + result = new Filter(result, constraint); + } + return result; + } + + /** + * Build optionals to the supplied TE + */ + public TupleExpr buildOptionalTE(TupleExpr result) { for (Map.Entry> entry : optionalTEs) { List constraints = entry.getValue(); if (constraints != null && !constraints.isEmpty()) { @@ -186,11 +199,6 @@ public TupleExpr buildTupleExpr() { result = new LeftJoin(result, entry.getKey()); } } - - for (ValueExpr constraint : constraints) { - result = new Filter(result, constraint); - } - return result; } diff --git a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java index 77e5ede5b6c..abb0fac84ff 100644 --- a/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java +++ b/core/queryparser/sparql/src/main/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilder.java @@ -2378,6 +2378,8 @@ public Object visit(ASTBind node, Object data) throws VisitorException { // get a tupleExpr that represents the basic graph pattern, sofar. TupleExpr arg = graphPattern.buildJoinFromRequiredTEs(); + // apply optionals, if any + arg = graphPattern.buildOptionalTE(arg); // check if alias is not previously used in the BGP if (arg.getBindingNames().contains(alias)) { diff --git a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java index afaca7d955e..427f69cf449 100644 --- a/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java +++ b/core/queryparser/sparql/src/test/java/org/eclipse/rdf4j/query/parser/sparql/TupleExprBuilderTest.java @@ -266,6 +266,41 @@ public void testServiceGraphPatternChopping() throws Exception { } + @Test + public void testOtionalBindCoalesce() throws Exception { + StringBuilder qb = new StringBuilder(); + qb.append("SELECT ?result \n"); + qb.append("WHERE { \n"); + qb.append("OPTIONAL {\n" + + " OPTIONAL {\n" + + " BIND(\"value\" AS ?foo)\n" + + " }\n" + + " BIND(COALESCE(?foo, \"no value\") AS ?result)\n" + + " }"); + qb.append(" } "); + + ASTQueryContainer qc = SyntaxTreeBuilder.parseQuery(qb.toString()); + TupleExpr result = builder.visit(qc, null); + String expected = "Projection\n" + + " ProjectionElemList\n" + + " ProjectionElem \"result\"\n" + + " LeftJoin\n" + + " SingletonSet\n" + + " Extension\n" + + " LeftJoin\n" + + " SingletonSet\n" + + " Extension\n" + + " SingletonSet\n" + + " ExtensionElem (foo)\n" + + " ValueConstant (value=\"value\")\n" + + " ExtensionElem (result)\n" + + " Coalesce\n" + + " Var (name=foo)\n" + + " ValueConstant (value=\"no value\")\n"; + assertEquals(expected.replace("\r\n", "\n"), result.toString().replace("\r\n", "\n")); +// System.out.println(result); + } + private class ServiceNodeFinder extends AbstractASTVisitor { private final List graphPatterns = new ArrayList<>();