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',"