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

rubenada pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 795361702d [CALCITE-7772] ModelHandler.addFunctions should defer UDF 
class initialization until after shape validation
795361702d is described below

commit 795361702d573a31efffd6edbf23d23b5bb70de2
Author: Ruben Quesada Lopez <[email protected]>
AuthorDate: Fri Sep 11 16:34:07 2026 +0100

    [CALCITE-7772] ModelHandler.addFunctions should defer UDF class 
initialization until after shape validation
---
 .../org/apache/calcite/model/ModelHandler.java     |  6 ++---
 .../org/apache/calcite/model/ModelHandlerTest.java | 28 ++++++++++++++++++++++
 2 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/model/ModelHandler.java 
b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
index c30cfc2815..d9b064392a 100644
--- a/core/src/main/java/org/apache/calcite/model/ModelHandler.java
+++ b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
@@ -227,10 +227,10 @@ public static void addFunctions(ClassNameFilter filter, 
SchemaPlus schema,
     filter.check(className);
     final Class<?> clazz;
     try {
-      clazz = Class.forName(className);
+      // Defer class initialization until it is checked to be a valid function 
(see below)
+      clazz = Class.forName(className, false, 
ModelHandler.class.getClassLoader());
     } catch (ClassNotFoundException e) {
-      throw new RuntimeException("UDF class '"
-          + className + "' not found");
+      throw new RuntimeException("UDF class '" + className + "' not found");
     }
     String methodNameOrDefault = Util.first(methodName, "eval");
     String actualFunctionName;
diff --git a/core/src/test/java/org/apache/calcite/model/ModelHandlerTest.java 
b/core/src/test/java/org/apache/calcite/model/ModelHandlerTest.java
index 7d0e00eda2..258c0d8bf0 100644
--- a/core/src/test/java/org/apache/calcite/model/ModelHandlerTest.java
+++ b/core/src/test/java/org/apache/calcite/model/ModelHandlerTest.java
@@ -36,6 +36,7 @@
 import java.sql.DriverManager;
 import java.util.Properties;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
@@ -120,6 +121,33 @@ public class ModelHandlerTest {
     assertThat(e.getMessage(), containsString("javax.naming."));
   }
 
+  /** Set by {@link NotAFunction}'s static initializer. */
+  static final AtomicBoolean NOT_A_FUNCTION_INITIALIZED = new 
AtomicBoolean(false);
+
+  /** Not a valid function class; records whether its static initializer ran. 
*/
+  public static class NotAFunction {
+    static {
+      NOT_A_FUNCTION_INITIALIZED.set(true);
+    }
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7772";>[CALCITE-7772] 
ModelHandler
+   * addFunctions should defer UDF class initialization until after shape 
validation</a>.
+   * A class that passes the name filter but fails the function-shape
+   * checks must be rejected without its static initializer running. */
+  @Test void testRejectedUdfClassIsNotInitialized() {
+    SchemaPlus root = CalciteSchema.createRootSchema(false, false).plus();
+    ClassNameFilter permissive = ClassNameFilter.of("", "org.apache.calcite.");
+    RuntimeException e =
+        assertThrows(RuntimeException.class, () ->
+            ModelHandler.addFunctions(permissive, root, "f",
+                NotAFunction.class.getName(), null, false));
+    assertThat(e.getMessage(), containsString("Not a valid function class"));
+    assertThat("static initializer of a rejected UDF class must not run",
+        NOT_A_FUNCTION_INITIALIZED.get(), is(false));
+  }
+
   @Test void testDenyFactory() {
     String model = "inline:{"
         + "  version: '1.0',"

Reply via email to