Skip to content

Commit c5b1536

Browse files
authored
feat(isthmus): add dynamic function conversion capabilities (#457)
1 parent 7a240ec commit c5b1536

22 files changed

+1037
-29
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.substrait.isthmus;
2+
3+
import io.substrait.extension.SimpleExtension;
4+
import io.substrait.isthmus.expression.FunctionMappings;
5+
import java.util.List;
6+
import java.util.Locale;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
9+
10+
public class ExtensionUtils {
11+
12+
/**
13+
* Extracts dynamic extensions from a collection of extensions.
14+
*
15+
* <p>A <b>dynamic extension</b> is a user-defined function (UDF) that is not part of the standard
16+
* Substrait function catalog. These are custom functions that users define and provide at
17+
* runtime, extending the built-in function set with domain-specific or application-specific
18+
* operations.
19+
*
20+
* <p>This method filters out all functions that are already known to the Calcite operator table
21+
* (the standard/built-in functions) and returns only the custom functions that represent new
22+
* capabilities not available in the default function set.
23+
*
24+
* <p><b>Example:</b> If a user defines a custom UDF "my_hash_function" that computes a
25+
* proprietary hash, this would be a dynamic extension since it's not part of the standard
26+
* Substrait specification.
27+
*
28+
* @param extensions the complete collection of extensions (both standard and custom)
29+
* @return a new ExtensionCollection containing only the dynamic (custom/user-defined) functions
30+
* that are not present in the standard Substrait function catalog
31+
*/
32+
public static SimpleExtension.ExtensionCollection getDynamicExtensions(
33+
SimpleExtension.ExtensionCollection extensions) {
34+
Set<String> knownFunctionNames =
35+
FunctionMappings.SCALAR_SIGS.stream()
36+
.map(FunctionMappings.Sig::name)
37+
.collect(Collectors.toSet());
38+
39+
List<SimpleExtension.ScalarFunctionVariant> customFunctions =
40+
extensions.scalarFunctions().stream()
41+
.filter(f -> !knownFunctionNames.contains(f.name().toLowerCase(Locale.ROOT)))
42+
.collect(Collectors.toList());
43+
44+
return SimpleExtension.ExtensionCollection.builder()
45+
.scalarFunctions(customFunctions)
46+
// TODO: handle aggregates and other functions
47+
.build();
48+
}
49+
}

isthmus/src/main/java/io/substrait/isthmus/FeatureBoard.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,19 @@ public abstract class FeatureBoard {
1717
public Casing unquotedCasing() {
1818
return Casing.TO_UPPER;
1919
}
20+
21+
/**
22+
* Controls whether to support dynamic user-defined functions (UDFs) during SQL to Substrait plan
23+
* conversion.
24+
*
25+
* <p>When enabled, custom functions defined in extension YAML files are available for use in SQL
26+
* queries. These functions will be dynamically converted to SQL operators during plan conversion.
27+
* This feature must be explicitly enabled by users and is disabled by default.
28+
*
29+
* @return true if dynamic UDFs should be supported; false otherwise (default)
30+
*/
31+
@Value.Default
32+
public boolean allowDynamicUdfs() {
33+
return false;
34+
}
2035
}

0 commit comments

Comments
 (0)