pedrumj2 opened a new issue, #13014:
URL: https://github.com/apache/gluten/issues/13014
### Description
### Description
**Proposal: Infer the Java signature from the Velox C++ UDF**
**Why?** The velox C++ UDF implementation already contains the necessary
information needed to derive the signature, defining the java class introduces
an extra step for the user that can be avoided. From a user experience point of
view, user can then write a Velox C++ udf once and have it available to
different backends out of the box (i.e they get spark out of the box for free).
The following section shares the current steps needed to define a UDF. After
this proposal:
- Step1 would no longer be needed
- Step4 would no longer be needed
- Step5 would no longer need the `CREATE TEMPORARY FUNCTION my_new_udf AS
'com.example.udf.MyNewUdf';`
- Step6 would no longer need the `--jars "$BUNDLE",out/mynewudf.jar ` and
` --driver-class-path "$BUNDLE:out/mynewudf.jar" `
## Current State
- A UDF that only ever runs on Gluten needs a Java class written, compiled,
jarred and shipped purely as a name.
- The Java body and the C++ body must be kept in sync by hand; nothing
enforces it.
- Any change to the signature has to be made twice, in two languages.
### [step1] Write the Java UDF
> Not needed after this proposal.
```java
package com.example.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class MyNewUdf extends UDF {
public Text evaluate(Text a, Text b) {
throw new UnsupportedOperationException(
"com.example.udf.MyNewUdf is implemented natively in Velox; "
+ "reaching this method means the query fell back to the JVM.");
}
}
```
### [step2] Write the C++ Velox UDF
Put `MyNewUdf.cc` in `cpp/velox/udf/examples/` so it inherits the flags the
shipped examples use.
```cpp
#include <velox/functions/Macros.h>
#include <velox/functions/Registerer.h>
#include "udf/Udf.h"
#include "udf/examples/UdfCommon.h" // gluten::UdfRegisterer
using namespace facebook::velox;
namespace {
static const char* kVarChar = "varchar";
template <typename T>
struct MyNewUdfFunction {
VELOX_DEFINE_FUNCTION_TYPES(T);
FOLLY_ALWAYS_INLINE void call(
out_type<Varchar>& result,
const arg_type<Varchar>& a,
const arg_type<Varchar>& b) {
result.append(a.data());
result.append(" ");
result.append(b.data());
}
};
class MyNewUdfRegisterer final : public gluten::UdfRegisterer {
public:
int getNumUdf() override { return 1; }
void populateUdfEntries(int& i, gluten::UdfEntry* e) override {
e[i++] = {name_.c_str(), kVarChar, 2, arg_, false, true};
}
void registerSignatures() override {
registerFunction<MyNewUdfFunction, Varchar, Varchar, Varchar>({name_});
}
private:
const std::string name_ = "com.example.udf.MyNewUdf";
const char* arg_[2] = {kVarChar, kVarChar};
};
} // namespace
...
// globalRegisters(), setupRegisterers(), and the DEFINE_GET_NUM_UDF /
// DEFINE_GET_UDF_ENTRIES / DEFINE_REGISTER_UDF macros: copy verbatim from
MyUDF.cc.
```
`name_` must equal the Java class name — that is the binding this proposal
removes.
References:
[MyUDF.cc](https://github.com/apache/gluten/blob/main/cpp/velox/udf/examples/MyUDF.cc),
[UdfCommon.h](https://github.com/apache/gluten/blob/main/cpp/velox/udf/examples/UdfCommon.h),
[Udf.h](https://github.com/apache/gluten/blob/main/cpp/velox/udf/Udf.h),
[VeloxUDF.md](https://github.com/apache/gluten/blob/main/docs/developers/VeloxUDF.md)
### [step3] Build the `.so`
Append to
[cpp/velox/udf/examples/CMakeLists.txt](https://github.com/apache/gluten/blob/main/cpp/velox/udf/examples/CMakeLists.txt):
```cmake
add_library(mynewudf SHARED "MyNewUdf.cc")
target_link_libraries(mynewudf velox)
```
```bash
docker run --rm -v "$PWD":/work/gluten:z apache/gluten:centos-9-jdk8 bash -c
'
source /opt/rh/gcc-toolset-12/enable
export VELOX_BUILD_SHARED=ON
cd /work/gluten && ./dev/builddeps-veloxbe.sh --build_examples=ON
'
```
`--build_examples=ON` is what pulls `udf/examples` into the build.
Incremental on an already-built tree: 24 seconds. Produces
`cpp/build/velox/udf/examples/libmynewudf.so`.
Use the `centos-9` image; `centos-8` ships an fbthrift predating
`readLEFromBuffer` and Velox's Parquet reader will not compile against it.
### [step4] Compile the Java UDF into a jar
> Not needed after this proposal.
```bash
javac -cp "$SPARK_HOME/jars/*" -d out/classes MyNewUdf.java
jar cf out/mynewudf.jar -C out/classes .
```
### [step5] Write the query
> After this proposal the `CREATE TEMPORARY FUNCTION` line would no longer
be needed.
```sql
CREATE TEMPORARY FUNCTION my_new_udf AS 'com.example.udf.MyNewUdf';
CREATE TABLE IF NOT EXISTS greetings AS
SELECT * FROM VALUES ('hello'), ('bonjour'), ('hola') AS t(word);
SELECT word, my_new_udf(word, 'world') AS greeting FROM greetings ORDER BY
word;
EXPLAIN SELECT my_new_udf(word, 'world') FROM greetings;
```
### [step6] Run the query
> After this proposal the bundle jar would no longer need to go on the
classpath.
```bash
$SPARK_HOME/bin/spark-sql \
--master 'local[2]' \
--jars "$BUNDLE",out/mynewudf.jar \
--driver-class-path "$BUNDLE:out/mynewudf.jar" \
--conf spark.plugins=org.apache.gluten.GlutenPlugin \
--conf spark.memory.offHeap.enabled=true \
--conf spark.memory.offHeap.size=4g \
--conf
spark.shuffle.manager=org.apache.spark.shuffle.sort.ColumnarShuffleManager \
--conf
spark.gluten.sql.columnar.backend.velox.udfLibraryPaths=file:///abs/path/libmynewudf.so
\
--conf
spark.gluten.sql.columnar.backend.velox.driver.udfLibraryPaths=file:///abs/path/libmynewudf.so
\
--conf spark.sql.catalogImplementation=hive \
-f demo.sql
```
#### Results
Verified end to end on 2026-09-13 against `main` at commit `84a2b7a`, Spark
3.5.9.
```
bonjour bonjour world
hello hello world
hola hola world
Time taken: 1.789 seconds, Fetched 3 row(s)
```
```
== Physical Plan ==
VeloxColumnarToRow
+- ^(1) ProjectExecTransformer
[HiveSimpleUDF#com.example.udf.MyNewUdf(word#12,world) AS my_new_udf(word,
world)#13]
+- ^(1) InputIteratorTransformer[word#12]
+- RowToVeloxColumnar
+- Scan hive spark_catalog.default.greetings [word#12],
HiveTableRelation [`spark_catalog`.`default`.`greetings`,
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Data Cols: [word#12],
Partition Cols: []]
```
### Gluten version
main branch
### Gluten version
main branch
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]