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


The following commit(s) were added to refs/heads/main by this push:
     new 89d9ee2c44 GH-2883: Fix for an NPE in OpAsQuery.
89d9ee2c44 is described below

commit 89d9ee2c445cc57944d50792017fa14f36e0d8ae
Author: Claus Stadler <[email protected]>
AuthorDate: Sun Dec 8 00:44:36 2024 +0100

    GH-2883: Fix for an NPE in OpAsQuery.
---
 .../org/apache/jena/sparql/algebra/OpAsQuery.java  | 19 +++++
 .../ExprTransformApplyElementTransform.java        | 11 ++-
 .../syntax/syntaxtransform/QueryTransformOps.java  |  9 ++-
 .../apache/jena/sparql/algebra/TestOpAsQuery.java  | 90 +++++++++++++++++++++-
 4 files changed, 122 insertions(+), 7 deletions(-)

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 594139040c..9be1eb36ab 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
@@ -81,6 +81,25 @@ public class OpAsQuery {
         return converter.convert() ;
     }
 
+    public static Element asElement(Op op) {
+        Query query = asQuery(op);
+        Element elt = isPrimitiveQuery(query)
+            ? query.getQueryPattern()
+            : new ElementSubQuery(query);
+        return elt;
+    }
+
+    /** Whether the query is a plain <code>SELECT * { pattern }</code>.  */
+    private static boolean isPrimitiveQuery(Query query) {
+        boolean isNonPrimitive =
+            !query.isQueryResultStar() || query.isDistinct() || 
query.isReduced() ||
+            query.hasLimit() || query.hasOffset() ||
+            query.hasAggregators() || query.hasGroupBy() || query.hasHaving() 
||
+            query.hasOrderBy() ||
+            query.hasValues();
+        return !isNonPrimitive;
+    }
+
     static class /* struct */ QueryLevelDetails {
         // The stack of processing in a query is:
         // slice-distinct/reduce-project-order-filter[having]-extend*[AS and 
aggregate naming]-group-pattern
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ExprTransformApplyElementTransform.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ExprTransformApplyElementTransform.java
index 9e4e3f5806..698f04492e 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ExprTransformApplyElementTransform.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/syntax/syntaxtransform/ExprTransformApplyElementTransform.java
@@ -20,6 +20,7 @@ package org.apache.jena.sparql.syntax.syntaxtransform;
 
 import org.apache.jena.sparql.ARQInternalErrorException;
 import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.OpAsQuery;
 import org.apache.jena.sparql.expr.*;
 import org.apache.jena.sparql.syntax.Element;
 
@@ -43,7 +44,15 @@ public class ExprTransformApplyElementTransform extends 
ExprTransformCopy
     @Override
     public Expr transform(ExprFunctionOp funcOp, ExprList args, Op opArg)
     {
-        Element el2 = ElementTransformer.transform(funcOp.getElement(), 
transform);
+        // If the element is null then an attempt is made to obtain it from 
the algebra.
+        // A given element takes precedence over the algebra.
+        Element el1 = funcOp.getElement();
+        if (el1 == null) {
+            Op op = funcOp.getGraphPattern();
+            el1 = op == null ? null : OpAsQuery.asElement(op);
+        }
+        // ElementTransformer will warn should it happen that null is passed 
to it.
+        Element el2 = ElementTransformer.transform(el1, transform, this);
 
         if ( el2 == funcOp.getElement() )
             return super.transform(funcOp, args, opArg);
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 313f75245b..b4f987a3bf 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
@@ -69,7 +69,7 @@ public class QueryTransformOps {
         Query q2 = QueryTransformOps.shallowCopy(query);
         // Mutate the q2 structures which are already allocated and no other 
code can access yet.
 
-        mutateByQueryType(q2, transform, exprTransform);
+        mutateByQueryType(q2, exprTransform);
         mutateVarExprList(q2.getGroupBy(), exprTransform);
         mutateExprList(q2.getHavingExprs(), exprTransform);
         if (q2.getOrderBy() != null)
@@ -116,7 +116,7 @@ public class QueryTransformOps {
     }
 
     // Do the result form part of the cloned query.
-    private static void mutateByQueryType(Query q2, ElementTransform 
transform, ExprTransform exprTransform) {
+    private static void mutateByQueryType(Query q2, ExprTransform 
exprTransform) {
         switch(q2.queryType()) {
             case ASK : break;
             case CONSTRUCT :
@@ -125,7 +125,7 @@ public class QueryTransformOps {
                 Template template = q2.getConstructTemplate();
                 QuadAcc acc = new QuadAcc();
                 List<Quad> quads = template.getQuads();
-                template.getQuads().forEach(q->{
+                quads.forEach(q->{
                     Node g = transform(q.getGraph(), exprTransform);
                     Node s = transform(q.getSubject(), exprTransform);
                     Node p = transform(q.getPredicate(), exprTransform);
@@ -145,7 +145,8 @@ public class QueryTransformOps {
             case CONSTRUCT_JSON :
                 throw new UnsupportedOperationException("Transform of JSON 
template queries");
             case UNKNOWN :
-                throw new JenaException("Unknown qu ery type");
+            default :
+                throw new JenaException("Unknown query type");
         }
     }
 
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 3d91b293b9..af58d9c09d 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
@@ -329,6 +329,11 @@ public class TestOpAsQuery {
         test_roundTripQuery(query) ;
     }
 
+    @Test
+    public void testSubQuery4() {
+        test_roundTripQuery("SELECT ?z { SELECT ?x {  } }");
+    }
+
     @Test
     public void testAggregatesInSubQuery1() {
         //Simplified form of a test case provided via the mailing list 
(JENA-445)
@@ -482,6 +487,83 @@ public class TestOpAsQuery {
                                                            "SELECT * { GRAPH 
?g { { ?x ?y ?z FILTER(EXISTS { ?s ?p ?o }) } ?x ?y ?z } }",
                                                               syntaxARQ); }
 
+    @Test
+    public void testExists07a() {
+        String query = """
+            SELECT ?x {
+              ?x a ?y .
+              FILTER EXISTS { ?y a ?z }
+            }
+            """;
+        test_roundTripQuery(query);
+    }
+
+    @Test
+    public void testExists07b() {
+        String input = """
+            SELECT ?x {
+              ?x a ?y
+              FILTER EXISTS { SELECT * { ?y a ?z } }
+            }
+            """;
+        // Going through the algebra is expected to lose the SELECT * { } part 
within EXISTS.
+        String expected = """
+            SELECT ?x {
+              ?x a ?y
+              FILTER EXISTS { ?y a ?z }
+            }
+            """;
+        test_roundTripQueryViaAlgebra(input, expected);
+    }
+
+    @Test
+    public void testExists08a() {
+        String input = """
+            SELECT * {
+              ?x a ?y
+              FILTER EXISTS { SELECT * { ?y a ?z } LIMIT 1 }
+            }
+            """;
+        test_roundTripQuery(input);
+        test_roundTripQueryViaAlgebra(input, input);
+    }
+
+    // Tests exists with a subquery within a subquery.
+    @Test
+    public void testExists09a() {
+        String input = """
+            SELECT ?x {
+              SELECT ?x {
+                ?x a ?y
+                FILTER EXISTS { SELECT * { ?y a ?z } }
+              }
+            }
+            """;
+        // Going through the algebra is expected to lose the SELECT * { } part 
within EXISTS.
+        String expected = """
+            SELECT ?x {
+              SELECT ?x {
+                ?x a ?y
+                FILTER EXISTS { ?y a ?z }
+              }
+            }
+            """;
+        test_roundTripQueryViaAlgebra(input, expected);
+    }
+
+    @Test
+    public void testExists09b() {
+        String queryStr = """
+            SELECT ?x {
+              SELECT ?x {
+                ?x a ?y
+                FILTER EXISTS { SELECT * { ?y a ?z } LIMIT 1 }
+              }
+            }
+            """;
+        test_roundTripQuery(queryStr, Syntax.syntaxARQ);
+    }
+
     @Test public void testNotExists01() { test_roundTripQuery("SELECT * { ?x 
?y ?z NOT EXISTS { ?s ?p ?o } }",
                                                            "SELECT * { ?x ?y 
?z FILTER NOT EXISTS { ?s ?p ?o } }",
                                                            syntaxARQ); }
@@ -503,8 +585,6 @@ public class TestOpAsQuery {
                                                            "SELECT * { GRAPH 
?g { { ?x ?y ?z FILTER(NOT EXISTS { ?s ?p ?o }) } ?x ?y ?z } }",
                                                               syntaxARQ); }
 
-
-
     @Test
     public void testTable1() {
         String query = "SELECT * WHERE { ?x ?p ?z . VALUES ?y { } }" ;
@@ -603,6 +683,12 @@ public class TestOpAsQuery {
         test_roundTripQuery(query, outcome, Syntax.syntaxSPARQL_11);
     }
 
+    /** query->algebra->OpAsQuery->assert equality with outcome */
+    private static void test_roundTripQueryViaAlgebra(String query, String 
outcome) {
+        String opStr = Algebra.compile(QueryFactory.create(query)).toString();
+        test_AlgebraToQuery(opStr, outcome);
+    }
+
     private static void test_roundTripQuery(String query, String outcome, 
Syntax syntax) {
         // This must also be true.
         test_roundTripAlegbra(query, syntax);

Reply via email to