Skip to content

Commit 5135e1b

Browse files
committed
Add Java table functions support (1.5)
This is a backport of the PR duckdb#644 to `v1.5-variegata` stable branch. This PR is a continuation of duckdb#630, it adds support for writing DuckDB table functions in Java. Documentation is added to UDF.md. Testing: new test added
1 parent 4ac94d2 commit 5135e1b

36 files changed

Lines changed: 2938 additions & 242 deletions

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,12 @@ add_library(duckdb_java SHARED
596596
src/jni/bindings_data_chunk.cpp
597597
src/jni/bindings_logical_type.cpp
598598
src/jni/bindings_scalar_function.cpp
599+
src/jni/bindings_table_function.cpp
600+
src/jni/bindings_table_function_bind.cpp
601+
src/jni/bindings_table_function_init.cpp
599602
src/jni/bindings_validity.cpp
600603
src/jni/bindings_vector.cpp
604+
src/jni/bindings_value.cpp
601605
src/jni/config.cpp
602606
src/jni/duckdb_java.cpp
603607
src/jni/functions.cpp

CMakeLists.txt.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,12 @@ add_library(duckdb_java SHARED
114114
src/jni/bindings_data_chunk.cpp
115115
src/jni/bindings_logical_type.cpp
116116
src/jni/bindings_scalar_function.cpp
117+
src/jni/bindings_table_function.cpp
118+
src/jni/bindings_table_function_bind.cpp
119+
src/jni/bindings_table_function_init.cpp
117120
src/jni/bindings_validity.cpp
118121
src/jni/bindings_vector.cpp
122+
src/jni/bindings_value.cpp
119123
src/jni/config.cpp
120124
src/jni/duckdb_java.cpp
121125
src/jni/functions.cpp

UDF.MD

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@ Common class mappings include:
8787

8888
Notes:
8989

90-
- `UHUGEINT` is supported through explicit `DuckDBColumnType.UHUGEINT`/`DuckDBLogicalType` declarations.
91-
- Java class auto-mapping for `BigInteger` remains `HUGEINT`.
90+
- `UHUGEINT` is supported through explicit `DuckDBColumnType.UHUGEINT`/`DuckDBLogicalType` declarations
91+
- Java class auto-mapping for `BigInteger` remains `HUGEINT`
92+
- composite types like `STRUCT` or `LIST` are currently not supported, this intended to be added in future versions
9293

9394
Use `DuckDBLogicalType.decimal(width, scale)` for explicit DECIMAL precision/scale.
9495

@@ -153,9 +154,6 @@ This registry provides bookkeeping for functions registered through the JDBC API
153154

154155
- `name()`
155156
- `functionKind()`
156-
- `isScalar()`
157-
- parameter and return type metadata
158-
- callback and flags used at registration time
159157

160158
To inspect Java-side registrations:
161159

@@ -207,3 +205,68 @@ try (Connection conn = DriverManager.getConnection("jdbc:duckdb:"); Statement st
207205
```sql
208206
SELECT java_event_label(TIMESTAMP '2026-04-04 12:00:00', 'launch', 4.5);
209207
```
208+
209+
## Table functions (`DuckDBTableFunction`)
210+
211+
Similar to scalar functions, a table function can be registered using `DuckDBFunctions.tableFunction()`.
212+
213+
Table function callback must implement `DuckDBTableFunction` interface:
214+
215+
- `bind` - called during `PREPARE` stage, must register output columns; has access to input parameters using `.getParameter()` and `.getNamedParameter()`; can return a "bind data" object
216+
that can be used during function execution
217+
- `init` - called once before the function execution, has access to bind data, returns an object used to track the state during function execution
218+
- `localInit` (optional) - called once for every thread before the function execution, returns an object used to track thread-local state during function execution
219+
- `apply` - has access to bind data, global init data and local init date, writes results into output vectors, returns a number of written row, is called by the engine repeatedly until it return zero rows
220+
221+
All callback arguments (including parameter objects) are only valid during the callback execution.
222+
223+
In general, Java table functions interface exposes the [C API table function interface](https://github.com/duckdb/duckdb-java/blob/2f4ed44df2fd1f89594cc7af28f9f8daba5fa582/src/duckdb/src/include/duckdb.h#L4145-L4478)
224+
as close as possible.
225+
226+
Example:
227+
228+
```java
229+
try (Connection conn = DriverManager.getConnection("jdbc:duckdb:"); Statement stmt = conn.createStatement()) {
230+
DuckDBFunctions.tableFunction()
231+
.withName("java_table_basic")
232+
.withParameter(int.class)
233+
.withNamedParameter("param1", String.class)
234+
.withFunction(new DuckDBTableFunction<Integer, AtomicBoolean, Object>() {
235+
@Override
236+
public Integer bind(DuckDBTableFunctionBindInfo info) throws Exception {
237+
info.addResultColumn("col1", Integer.TYPE).addResultColumn("col2", String.class);
238+
DuckDBValue param = info.getParameter(0);
239+
assertThrows(param::getBoolean, FunctionException.class);
240+
DuckDBValue namedParam = info.getNamedParameter("param1");
241+
assertEquals(namedParam.getString(), "foobar");
242+
return param.getInt();
243+
}
244+
245+
@Override
246+
public AtomicBoolean init(DuckDBTableFunctionInitInfo info) throws Exception {
247+
return new AtomicBoolean(false);
248+
}
249+
250+
@Override
251+
public long apply(DuckDBTableFunctionCallInfo info, DuckDBDataChunkWriter output) throws Exception {
252+
Integer bindData = info.getBindData();
253+
AtomicBoolean done = info.getInitData();
254+
if (done.get()) {
255+
return 0;
256+
}
257+
output.vector(0).setInt(0, bindData);
258+
output.vector(1).setString(0, "foo");
259+
output.vector(0).setNull(1);
260+
output.vector(1).setString(1, "bar");
261+
done.set(true);
262+
return 2;
263+
}
264+
})
265+
.register(conn);
266+
...
267+
}
268+
```
269+
```sql
270+
FROM java_table_basic(42, param1='foobar')
271+
```
272+

duckdb_java.def

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,67 @@ Java_org_duckdb_DuckDBBindings_duckdb_1appender_1column_1type
114114
Java_org_duckdb_DuckDBBindings_duckdb_1append_1data_1chunk
115115
Java_org_duckdb_DuckDBBindings_duckdb_1append_1default_1to_1chunk
116116

117+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1add_1result_1column
118+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter_1count
119+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter
120+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1named_1parameter
121+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1bind_1data
122+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1cardinality
123+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1error
124+
125+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1bind_1data
126+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1init_1data
127+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1count
128+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1index
129+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1max_1threads
130+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1error
131+
132+
Java_org_duckdb_DuckDBBindings_duckdb_1create_1table_1function
133+
Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1table_1function
134+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1name
135+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1parameter
136+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1named_1parameter
137+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1extra_1info
138+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1bind
139+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1init
140+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1local_1init
141+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1function
142+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1supports_1projection_1pushdown
143+
Java_org_duckdb_DuckDBBindings_duckdb_1register_1table_1function
144+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1bind_1data
145+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1init_1data
146+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1local_1init_1data
147+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1set_1error
148+
149+
Java_org_duckdb_DuckDBBindings_duckdb_1is_1null_1value
150+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1value_1type
151+
Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1value
152+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1bool
153+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int8
154+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint8
155+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int16
156+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint16
157+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int32
158+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint32
159+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int64
160+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint64
161+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1hugeint
162+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uhugeint
163+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1bignum
164+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1decimal
165+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1float
166+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1double
167+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1date
168+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time
169+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1ns
170+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1tz
171+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp
172+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1tz
173+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1s
174+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ms
175+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ns
176+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1varchar
177+
117178
duckdb_adbc_init
118179
duckdb_add_aggregate_function_to_set
119180
duckdb_add_replacement_scan

duckdb_java.exp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,67 @@ _Java_org_duckdb_DuckDBBindings_duckdb_1appender_1column_1type
111111
_Java_org_duckdb_DuckDBBindings_duckdb_1append_1data_1chunk
112112
_Java_org_duckdb_DuckDBBindings_duckdb_1append_1default_1to_1chunk
113113

114+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1add_1result_1column
115+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter_1count
116+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter
117+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1named_1parameter
118+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1bind_1data
119+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1cardinality
120+
_Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1error
121+
122+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1bind_1data
123+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1init_1data
124+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1count
125+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1index
126+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1max_1threads
127+
_Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1error
128+
129+
_Java_org_duckdb_DuckDBBindings_duckdb_1create_1table_1function
130+
_Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1table_1function
131+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1name
132+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1parameter
133+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1named_1parameter
134+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1extra_1info
135+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1bind
136+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1init
137+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1local_1init
138+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1function
139+
_Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1supports_1projection_1pushdown
140+
_Java_org_duckdb_DuckDBBindings_duckdb_1register_1table_1function
141+
_Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1bind_1data
142+
_Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1init_1data
143+
_Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1local_1init_1data
144+
_Java_org_duckdb_DuckDBBindings_duckdb_1function_1set_1error
145+
146+
_Java_org_duckdb_DuckDBBindings_duckdb_1is_1null_1value
147+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1value_1type
148+
_Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1value
149+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1bool
150+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1int8
151+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint8
152+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1int16
153+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint16
154+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1int32
155+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint32
156+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1int64
157+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint64
158+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1hugeint
159+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1uhugeint
160+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1bignum
161+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1decimal
162+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1float
163+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1double
164+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1date
165+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1time
166+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1ns
167+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1tz
168+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp
169+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1tz
170+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1s
171+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ms
172+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ns
173+
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1varchar
174+
114175
_duckdb_adbc_init
115176
_duckdb_add_aggregate_function_to_set
116177
_duckdb_add_replacement_scan

duckdb_java.map

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,67 @@ DUCKDB_JAVA {
113113
Java_org_duckdb_DuckDBBindings_duckdb_1append_1data_1chunk;
114114
Java_org_duckdb_DuckDBBindings_duckdb_1append_1default_1to_1chunk;
115115

116+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1add_1result_1column;
117+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter_1count;
118+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1parameter;
119+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1get_1named_1parameter;
120+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1bind_1data;
121+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1cardinality;
122+
Java_org_duckdb_DuckDBBindings_duckdb_1bind_1set_1error;
123+
124+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1bind_1data;
125+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1init_1data;
126+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1count;
127+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1get_1column_1index;
128+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1max_1threads;
129+
Java_org_duckdb_DuckDBBindings_duckdb_1init_1set_1error;
130+
131+
Java_org_duckdb_DuckDBBindings_duckdb_1create_1table_1function;
132+
Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1table_1function;
133+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1name;
134+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1parameter;
135+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1add_1named_1parameter;
136+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1extra_1info;
137+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1bind;
138+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1init;
139+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1local_1init;
140+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1set_1function;
141+
Java_org_duckdb_DuckDBBindings_duckdb_1table_1function_1supports_1projection_1pushdown;
142+
Java_org_duckdb_DuckDBBindings_duckdb_1register_1table_1function;
143+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1bind_1data;
144+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1init_1data;
145+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1get_1local_1init_1data;
146+
Java_org_duckdb_DuckDBBindings_duckdb_1function_1set_1error;
147+
148+
Java_org_duckdb_DuckDBBindings_duckdb_1is_1null_1value;
149+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1value_1type;
150+
Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1value;
151+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1bool;
152+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int8;
153+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint8;
154+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int16;
155+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint16;
156+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int32;
157+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint32;
158+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1int64;
159+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uint64;
160+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1hugeint;
161+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1uhugeint;
162+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1bignum;
163+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1decimal;
164+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1float;
165+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1double;
166+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1date;
167+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time;
168+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1ns;
169+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1time_1tz;
170+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp;
171+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1tz;
172+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1s;
173+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ms;
174+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1timestamp_1ns;
175+
Java_org_duckdb_DuckDBBindings_duckdb_1get_1varchar;
176+
116177
duckdb_adbc_init;
117178
duckdb_add_aggregate_function_to_set;
118179
duckdb_add_replacement_scan;

0 commit comments

Comments
 (0)