Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/src/main/java/org/apache/calcite/model/ModelHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,10 @@
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());
Comment thread
rubenada marked this conversation as resolved.
} catch (ClassNotFoundException e) {
throw new RuntimeException("UDF class '"
+ className + "' not found");
throw new RuntimeException("UDF class '" + className + "' not found");

Check warning on line 233 in core/src/main/java/org/apache/calcite/model/ModelHandler.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace generic exceptions with specific library exceptions or a custom exception.

See more on https://sonarcloud.io/project/issues?id=apache_calcite&issues=AaCfCpsV50KhFtv_DVyt&open=AaCfCpsV50KhFtv_DVyt&pullRequest=5255
}
String methodNameOrDefault = Util.first(methodName, "eval");
String actualFunctionName;
Expand Down
28 changes: 28 additions & 0 deletions core/src/test/java/org/apache/calcite/model/ModelHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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',"
Expand Down
Loading