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

arnebdt 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 6981d82b49 GH-3323 COALESCE checks variable bindings
6981d82b49 is described below

commit 6981d82b49b1ad80f60ed2f13253c2343cfe3f97
Author: arne-bdt <[email protected]>
AuthorDate: Sun Jul 20 19:20:24 2025 +0200

    GH-3323 COALESCE checks variable bindings
    
    COALESCE(?X, ...) is a common usage pattern. COALESCE now checks if a 
variable is bound before evaluating it. This avoids creating an exception for 
each unbound variable, which previously impacted performance.
    
    Added shadedJena550 and `org.apache.jena.sparql.expr.TestCoalesce` to 
implement benchmarks and evaluate the performance impacts.
---
 .../org/apache/jena/sparql/expr/E_Coalesce.java    |  10 +-
 jena-benchmarks/jena-benchmarks-jmh/pom.xml        |   7 ++
 .../org/apache/jena/sparql/expr/TestCoalesce.java  | 115 +++++++++++++++++++++
 .../jena-benchmarks-shadedJena510/pom.xml          |   2 +-
 .../pom.xml                                        |  25 +++--
 jena-benchmarks/pom.xml                            |   1 +
 6 files changed, 147 insertions(+), 13 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Coalesce.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Coalesce.java
index 23d47304d2..588c9f5e9a 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Coalesce.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_Coalesce.java
@@ -37,9 +37,15 @@ public class E_Coalesce extends ExprFunctionN {
     @Override
     public NodeValue evalSpecial(Binding binding, FunctionEnv env) {
         for ( Expr expr : super.getArgs() ) {
+            if(expr.isVariable() && (binding == null || 
!binding.contains(expr.asVar()))) {
+                // If the expression is a variable and no binding is provided,
+                // we skip it as it cannot be evaluated.
+                // COALESCE(?X, ...) is a common usage pattern and this check
+                // avoids creating an exception for each unbound variable.
+                continue;
+            }
             try {
-                NodeValue nv = expr.eval(binding, env);
-                return nv;
+                return expr.eval(binding, env);
             } catch (ExprEvalException ex) {}
         }
         throw new ExprEvalException("COALESCE: no value");
diff --git a/jena-benchmarks/jena-benchmarks-jmh/pom.xml 
b/jena-benchmarks/jena-benchmarks-jmh/pom.xml
index ff1cc0ea7d..f41c418529 100644
--- a/jena-benchmarks/jena-benchmarks-jmh/pom.xml
+++ b/jena-benchmarks/jena-benchmarks-jmh/pom.xml
@@ -100,6 +100,13 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.jena</groupId>
+            <artifactId>jena-benchmarks-shadedJena550</artifactId>
+            <version>5.6.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-slf4j2-impl</artifactId>
diff --git 
a/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/sparql/expr/TestCoalesce.java
 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/sparql/expr/TestCoalesce.java
new file mode 100644
index 0000000000..4bf57be108
--- /dev/null
+++ 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/sparql/expr/TestCoalesce.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.expr;
+
+
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.mem.graph.helper.JMHDefaultOptions;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.function.FunctionEnv;
+import org.apache.jena.sys.JenaSystem;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.runner.Runner;
+
+import java.util.Random;
+
+
+@State(Scope.Benchmark)
+public class TestCoalesce {
+
+    static {
+        JenaSystem.init();
+        org.apache.shadedJena550.sys.JenaSystem.init();
+    }
+
+    final static Random rnd = new Random();
+
+    @Benchmark
+    public Long benchmarkCoalesceJenaCurrent() {
+        var checksum = 0L;
+        // Create a Coalesce expression with some dummy expressions
+        var expressions = new ExprList();
+        expressions.add(new ExprVar("var1"));
+        expressions.add(new ExprVar("var2"));
+        expressions.add(new ExprVar("var3"));
+        expressions.add(new ExprVar("var4"));
+        expressions.add(new ExprVar("var5"));
+
+        var var3BoundLiteral =  NodeFactory.createLiteralString("Value5");
+        var bindingBuilder = Binding.builder();
+        bindingBuilder.add(expressions.get(4).asVar(), var3BoundLiteral);
+        var binding = bindingBuilder.build();
+
+        FunctionEnv env = null;
+
+        // Coalesce expression
+        var coalesceExpr = new E_Coalesce(expressions);
+
+        for(var i=0; i<1000000; i++) {
+            var result = coalesceExpr.eval(binding, env);
+            if( result != null ) {
+                checksum += i;
+            }
+        }
+        return checksum;
+    }
+
+    @Benchmark
+    public Long benchmarkCoalesceJena550() {
+        var checksum = 0L;
+        // Create a Coalesce expression with some dummy expressions
+        var expressions = new org.apache.shadedJena550.sparql.expr.ExprList();
+        expressions.add(new 
org.apache.shadedJena550.sparql.expr.ExprVar("var1"));
+        expressions.add(new 
org.apache.shadedJena550.sparql.expr.ExprVar("var2"));
+        expressions.add(new 
org.apache.shadedJena550.sparql.expr.ExprVar("var3"));
+        expressions.add(new 
org.apache.shadedJena550.sparql.expr.ExprVar("var4"));
+        expressions.add(new 
org.apache.shadedJena550.sparql.expr.ExprVar("var5"));
+
+        var var3BoundLiteral =  
org.apache.shadedJena550.graph.NodeFactory.createLiteralString("Value5");
+        var bindingBuilder = 
org.apache.shadedJena550.sparql.engine.binding.Binding.builder();
+        bindingBuilder.add(expressions.get(4).asVar(), var3BoundLiteral);
+        var binding = bindingBuilder.build();
+        org.apache.shadedJena550.sparql.function.FunctionEnv env = null;
+
+        // Coalesce expression
+        var coalesceExpr =
+                new 
org.apache.shadedJena550.sparql.expr.E_Coalesce(expressions);
+
+        for(var i=0; i<1000000; i++) {
+            var result = coalesceExpr.eval(binding, env);
+            if( result != null ) {
+                checksum += i;
+            }
+        }
+        return checksum;
+    }
+
+    @Test
+    public void benchmark() throws Exception {
+        var opt = JMHDefaultOptions.getDefaults(this.getClass())
+                .build();
+        var results = new Runner(opt).run();
+        Assert.assertNotNull(results);
+    }
+}
diff --git a/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml 
b/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml
index 8710e03627..c1ddc5bea2 100644
--- a/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml
+++ b/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml
@@ -45,7 +45,7 @@
 
     <properties>
         <build.time.xsd>${maven.build.timestamp}</build.time.xsd>
-        
<automatic.module.name>org.apache.jena.benchmarks.shadedJena480</automatic.module.name>
+        
<automatic.module.name>org.apache.jena.benchmarks.shadedJena510</automatic.module.name>
         <maven.test.skip>true</maven.test.skip>
         <scope>test</scope>
     </properties>
diff --git a/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml 
b/jena-benchmarks/jena-benchmarks-shadedJena550/pom.xml
similarity index 89%
copy from jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml
copy to jena-benchmarks/jena-benchmarks-shadedJena550/pom.xml
index 8710e03627..bad8808728 100644
--- a/jena-benchmarks/jena-benchmarks-shadedJena510/pom.xml
+++ b/jena-benchmarks/jena-benchmarks-shadedJena550/pom.xml
@@ -24,9 +24,9 @@
         <version>5.6.0-SNAPSHOT</version>
     </parent>
 
-    <name>Apache Jena - Benchmarks Shaded Jena 5.1.0</name>
-    <artifactId>jena-benchmarks-shadedJena510</artifactId>
-    <description>Shaded Apache Jena 5.1.0 for regression 
benchmarks</description>
+    <name>Apache Jena - Benchmarks Shaded Jena 5.5.0</name>
+    <artifactId>jena-benchmarks-shadedJena550</artifactId>
+    <description>Shaded Apache Jena 5.5.0 for regression 
benchmarks</description>
     <packaging>jar</packaging>
 
     <url>https://jena.apache.org/</url>
@@ -45,7 +45,7 @@
 
     <properties>
         <build.time.xsd>${maven.build.timestamp}</build.time.xsd>
-        
<automatic.module.name>org.apache.jena.benchmarks.shadedJena480</automatic.module.name>
+        
<automatic.module.name>org.apache.jena.benchmarks.shadedJena550</automatic.module.name>
         <maven.test.skip>true</maven.test.skip>
         <scope>test</scope>
     </properties>
@@ -54,27 +54,32 @@
         <dependency>
             <groupId>org.apache.jena</groupId>
             <artifactId>jena-base</artifactId>
-            <version>5.1.0</version>
+            <version>5.5.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.jena</groupId>
             <artifactId>jena-iri</artifactId>
-            <version>5.1.0</version>
+            <version>5.5.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jena</groupId>
+            <artifactId>jena-iri3986</artifactId>
+            <version>5.5.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.jena</groupId>
             <artifactId>jena-core</artifactId>
-            <version>5.1.0</version>
+            <version>5.5.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.jena</groupId>
             <artifactId>jena-arq</artifactId>
-            <version>5.1.0</version>
+            <version>5.5.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.jena</groupId>
             <artifactId>jena-geosparql</artifactId>
-            <version>5.1.0</version>
+            <version>5.5.0</version>
         </dependency>
     </dependencies>
 
@@ -106,7 +111,7 @@
                             <relocations>
                                 <relocation>
                                     <pattern>org.apache.jena</pattern>
-                                    
<shadedPattern>org.apache.shadedJena510</shadedPattern>
+                                    
<shadedPattern>org.apache.shadedJena550</shadedPattern>
                                 </relocation>
                             </relocations>
                             <filters>
diff --git a/jena-benchmarks/pom.xml b/jena-benchmarks/pom.xml
index 7a0094ded1..e44cc884ba 100644
--- a/jena-benchmarks/pom.xml
+++ b/jena-benchmarks/pom.xml
@@ -50,6 +50,7 @@
     <module>jena-benchmarks-jmh</module>
     <module>jena-benchmarks-shadedJena480</module>
     <module>jena-benchmarks-shadedJena510</module>
+    <module>jena-benchmarks-shadedJena550</module>
   </modules>
 
 </project>

Reply via email to