This is an automated email from the ASF dual-hosted git repository.

afs 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 719d2669ed GH-4181: Validate ExprTripleTerm for triple term rules
719d2669ed is described below

commit 719d2669ed23c823fb87f05c7e6193eded735ef1
Author: Andy Seaborne <[email protected]>
AuthorDate: Tue Sep 1 16:49:41 2026 +0100

    GH-4181: Validate ExprTripleTerm for triple term rules
---
 .../apache/jena/sparql/expr/ExprTripleTerm.java    |  9 ++++++
 .../function/library/triple/TripleTermOps.java     |  6 ++--
 .../java/org/apache/jena/sparql/expr/TS_Expr.java  |  2 +-
 ...Terms.java => TestExprTripleTermFunctions.java} | 32 +++++++++++++++++-----
 .../apache/jena/sparql/expr/TestExpressions.java   |  9 +++---
 .../jena/sparql/expr/TestNodeFunctionsMisc.java    |  4 +--
 jena-arq/testing/ARQ/Extra/expr-triple-term.rq     |  7 +++++
 jena-arq/testing/ARQ/Extra/expr-triple-term.srj    | 24 ++++++++++++++++
 jena-arq/testing/ARQ/Extra/expr-triple-term.ttl    |  4 +++
 jena-arq/testing/ARQ/Extra/manifest.ttl            |  7 +++++
 10 files changed, 86 insertions(+), 18 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
index c03b7f844c..a5b48c71cb 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprTripleTerm.java
@@ -60,6 +60,15 @@ public class ExprTripleTerm extends ExprNode {
         Triple t1 = tripleTerm.getTriple();
         Triple t2 = Substitute.substitute(t1, binding);
         if ( t2.isConcrete() ) {
+            Node s = t2.getSubject();
+            Node p = t2.getPredicate();
+            // Check it.
+            if ( s.isTripleTerm() )
+                throw new ExprEvalException("triple term: Subject is a triple 
term: "+s);
+            if ( !s.isURI() && !s.isBlank() )
+                throw new ExprEvalException("triple term: Subject is not a URI 
or blank node: "+s);
+            if ( !p.isURI() )
+                throw new ExprEvalException("triple term: Predicate is not a 
URI: "+p);
             Node tripleTerm2 = NodeFactory.createTripleTerm(t2);
             return NodeValue.makeNode(tripleTerm2);
         }
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/triple/TripleTermOps.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/triple/TripleTermOps.java
index fa7611a682..c4db950566 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/function/library/triple/TripleTermOps.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/function/library/triple/TripleTermOps.java
@@ -26,7 +26,6 @@ import java.util.function.Function;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.NodeFactory;
 import org.apache.jena.graph.Triple;
-import org.apache.jena.query.ARQ;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 
@@ -38,11 +37,12 @@ public class TripleTermOps {
      */
     public static NodeValue fnTriple(NodeValue nv1, NodeValue nv2, NodeValue 
nv3) {
         Node s = nv1.asNode();
-        if ( ARQ.isStrictMode() ) {
+        //if ( ARQ.isStrictMode() ) {
+        if ( true ) {
             if ( s.isTripleTerm() )
                 throw new ExprEvalException("triple: Subject is a triple term: 
"+nv1);
             if ( !s.isURI() && !s.isBlank() )
-                throw new ExprEvalException("triple: Subject is not a URI or 
blank node: "+nv2);
+                throw new ExprEvalException("triple: Subject is not a URI or 
blank node: "+nv1);
         }
 
         Node p = nv2.asNode();
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
index f35dff4e51..2092860076 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TS_Expr.java
@@ -44,7 +44,7 @@ import 
org.apache.jena.sparql.expr.nodevalue.TestNodeValueSortKey;
     , TestStringArgCompatibility.class
     , TestSPARQLKeywordFunctions.class
     , TestFunctionsByURI.class
-    , TestExprTripleTerms.class
+    , TestExprTripleTermFunctions.class
     , TestLeviathanFunctions.class
     , TestNodeValueOps.class
     , TestOrdering.class
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTerms.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTermFunctions.java
similarity index 74%
rename from 
jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTerms.java
rename to 
jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTermFunctions.java
index 89da1e841b..d6dcc8a984 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTerms.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExprTripleTermFunctions.java
@@ -38,7 +38,10 @@ import org.apache.jena.sys.JenaSystem;
 /**
  * Tests for TRIPLE, SUBJECT, PREDICATE, OBJECT, isTRIPLE
  */
-public class TestExprTripleTerms {
+public class TestExprTripleTermFunctions {
+
+    // "(triple s p o)" is the function call TRIPLE(s,p,o)
+    // "(tripleterm s p o)" is syntax <<( s p o )>>
 
     static { JenaSystem.init(); }
 
@@ -48,35 +51,50 @@ public class TestExprTripleTerms {
     }
 
     @Test
-    public void tripleTerm_Bad1() {
+    public void tripleFunction_Bad1() {
         assertThrows(ExprEvalException.class, ()-> eval("triple(:s1, 'bc', 
:o1)") );
     }
 
     @Test
-    public void tripleTerm_Access1() {
+    public void tripleFunction_Access1() {
         test("subject(triple(:s1, :p1, :o1))", ":s1");
     }
 
     @Test
-    public void tripleTerm_Access2() {
+    public void tripleFunction_Access2() {
         test("predicate(triple(:s1, :p1, :o1))", ":p1");
     }
 
     @Test
-    public void tripleTerm_Access3() {
+    public void tripleFunction_Access3() {
         test("object(triple(:s1, :p1, :o1))", ":o1");
     }
 
     @Test
-    public void tripleTerm_Test1() {
+    public void tripleFunction_Test1() {
         test("isTriple(triple(:s1, :p1, :o1))", "true");
     }
 
     @Test
-    public void tripleTerm_Test2() {
+    public void tripleFunction_Test2() {
         test("isTriple(:x)", "false");
     }
 
+    @Test
+    public void tripleTerm_BadSubjectLiteral() {
+        assertThrows(ExprEvalException.class, ()-> eval("triple('abc', :p1, 
:o1)") );
+    }
+
+    @Test
+    public void tripleTerm_BadSubjectTripleTerm() {
+        assertThrows(ExprEvalException.class, ()-> eval("triple(triple(:s1, 
:p1, :o1), :p2, :o2)") );
+    }
+
+    @Test
+    public void tripleTerm_NotConcrete() {
+        assertThrows(ExprEvalException.class, ()-> eval("triple(:s, :p1, 
?var)") );
+    }
+
     private static Node eval(String string) {
         Expr expr = ExprUtils.parse(string, pmap);
         NodeValue nv = expr.eval(null, new FunctionEnvBase());
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions.java
index 5716836fb4..f8d5bb8ee9 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestExpressions.java
@@ -404,12 +404,12 @@ public class TestExpressions
     @Test public void tripleterm_02() { testEval("TRIPLE(BNODE(), <x:p>, 
123)"); }
     @Test public void tripleterm_03() { testEval("TRIPLE(<x:s>, <x:p>, 
TRIPLE(<x:s1>, <x:p1>, <x:o1>))"); }
 
-    // Not symmetric RDF
+    // Not (symmetric) RDF
     @Test public void tripleterm_10() { assertThrows(ExprEvalException.class, 
()->testEval("TRIPLE(<x:s>, BNODE(), <x:o>)")); }
 
-    // TRIPLE generates symmetric RDF (non-strict)
-    @Test public void tripleterm_20() { testEval("TRIPLE(123, <x:p>, <x:o>)"); 
}
-    @Test public void tripleterm_21() { testEval("TRIPLE(TRIPLE(<x:s>, <x:p>, 
<x:o>), <x:p>, <x:o>)"); }
+    // If TRIPLE generates only legal RDF (not symmetric) on normal mode.
+    @Test public void tripleterm_20() { assertThrows(ExprEvalException.class, 
()-> testEval("TRIPLE(123, <x:p>, <x:o>)")); }
+    @Test public void tripleterm_21() { assertThrows(ExprEvalException.class, 
()-> testEval("TRIPLE(TRIPLE(<x:s>, <x:p>, <x:o>), <x:p>, <x:o>)")); }
 
     // TRIPLE generates RDF triples (strict)
     @Test public void tripleterm_30() {
@@ -435,7 +435,6 @@ public class TestExpressions
         assertThrows(QueryParseException.class, ()-> testURI("PROPERTY( 
TRIPLE(<x:s>, <x:p>, 123) )", "x:p") );
     }
 
-
     @Test public void boolean_129() { testBoolean("isURI(?x)", true, env); }
     @Test public void boolean_130() { testBoolean("isURI(?a)", false, env); }
     @Test public void boolean_131() { testBoolean("isURI(?b)", false, env); }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java
index 80465d7a88..ac803d2947 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/sparql/expr/TestNodeFunctionsMisc.java
@@ -69,13 +69,13 @@ public class TestNodeFunctionsMisc {
     @Test public void testRDFtermEquals5() {
         Node n1 = SSE.parseNode("<<(:s :p 123)>>");
         Node n2 = SSE.parseNode("<<(:s :p 123)>>");
-        assertTrue(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+        assertTrue(n1.sameTermAs(n2));
     }
 
     @Test public void testRDFtermEquals6() {
         Node n1 = SSE.parseNode("<<(:s :p1 123)>>");
         Node n2 = SSE.parseNode("<<(:s :p2 123)>>");
-        assertFalse(NodeFunctions.rdfTermEqual11_legacy(n1, n2));
+        assertFalse(n1.sameTermAs(n2));
     }
 
     @Test public void testRDFtermEquals7() {
diff --git a/jena-arq/testing/ARQ/Extra/expr-triple-term.rq 
b/jena-arq/testing/ARQ/Extra/expr-triple-term.rq
new file mode 100644
index 0000000000..368932b467
--- /dev/null
+++ b/jena-arq/testing/ARQ/Extra/expr-triple-term.rq
@@ -0,0 +1,7 @@
+PREFIX : <http://example/>
+
+SELECT * WHERE {
+  ?s :p ?o .
+  BIND( <<( ?o :q :z )>> AS ?tt )
+}
+
diff --git a/jena-arq/testing/ARQ/Extra/expr-triple-term.srj 
b/jena-arq/testing/ARQ/Extra/expr-triple-term.srj
new file mode 100644
index 0000000000..ae1b656fa2
--- /dev/null
+++ b/jena-arq/testing/ARQ/Extra/expr-triple-term.srj
@@ -0,0 +1,24 @@
+{ "head": {
+    "vars": [ "s" , "o" , "tt" ]
+  } ,
+  "results": {
+    "bindings": [
+      { 
+        "s": { "type": "uri" , "value": "http://example/s2"; } ,
+        "o": { "type": "uri" , "value": "http://example/s2"; } ,
+        "tt": {
+          "type": "triple" , 
+          "value": {
+            "subject":  { "type": "uri" , "value": "http://example/s2"; } ,
+            "predicate": { "type": "uri" , "value": "http://example/q"; } ,
+            "object":   { "type": "uri" , "value": "http://example/z"; }
+          }
+        }
+      } ,
+      { 
+        "s": { "type": "uri" , "value": "http://example/s1"; } ,
+        "o": { "type": "literal" , "value": "s1" }
+      }
+    ]
+  }
+}
diff --git a/jena-arq/testing/ARQ/Extra/expr-triple-term.ttl 
b/jena-arq/testing/ARQ/Extra/expr-triple-term.ttl
new file mode 100644
index 0000000000..5e694ff1a5
--- /dev/null
+++ b/jena-arq/testing/ARQ/Extra/expr-triple-term.ttl
@@ -0,0 +1,4 @@
+PREFIX : <http://example/>
+
+:s1 :p  "s1" .
+:s2 :p  :s2 .
diff --git a/jena-arq/testing/ARQ/Extra/manifest.ttl 
b/jena-arq/testing/ARQ/Extra/manifest.ttl
index 8695534f65..4948f6e001 100644
--- a/jena-arq/testing/ARQ/Extra/manifest.ttl
+++ b/jena-arq/testing/ARQ/Extra/manifest.ttl
@@ -44,4 +44,11 @@
               qt:data   <data-count-distinct-expr.ttl> ] ;
         mf:result  <count-distinct-expr.srj>
       ]
+      [  mf:name    "expr-triple-term" ;
+         rdf:type   mfx:TestQuery ; 
+         mf:action
+            [ qt:query  <expr-triple-term.rq> ;
+              qt:data   <expr-triple-term.ttl> ] ;
+        mf:result  <expr-triple-term.srj>
+      ]
     ).

Reply via email to