Support virtual columns in Java table functions, including empty projection for COUNT(*)
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start with DuckDBFunctions.tableFunction(), the projection-pushdown APIs, and the C API's TableFunction::get_virtual_columns and COLUMN_IDENTIFIER_EMPTY behavior. Run the supplied count_star_projection_probe query to confirm the current columnCount and columnIndex. Done means Java table functions can declare and receive supported virtual columns, including the empty projection, without materializing an unused regular column.
Written by the indexing model from the issue text.
Description
What happens?
Java table functions can declare regular result columns and enable projection pushdown, but they cannot declare virtual columns.
This is particularly relevant for queries such as:
SELECT count(*) FROM java_table_function()
No user-visible column is required to evaluate this query. However, because the Java table function cannot advertise DuckDB's internal empty virtual column, DuckDB falls back to projecting the first regular result column.
In a minimal test with projection pushdown enabled, init() receives:
columnCount=1
columnIndex=0
The function must therefore produce the first physical column even though the query only needs the number of rows.
Why is this needed?
Some Java table functions read from external cursors, remote services, files, or other expensive sources. Producing a regular column may require decoding, allocating, converting, or transferring values.
For COUNT(*), the function should be able to advance the source and return the chunk cardinality without materializing an otherwise unused payload column.
More generally, exposing virtual columns would allow Java table functions to provide metadata or synthetic columns that are not part of the physical source.
Reproduction
DuckDBFunctions.tableFunction()
.withName("count_star_projection_probe")
.withProjectionPushdown()
.withFunction(new DuckDBTableFunction<Object, AtomicBoolean, Object>() {
@Override
public Object bind(DuckDBTableFunctionBindInfo info) {
info.addResultColumn("expensive_payload", String.class);
return null;
}
@Override
public AtomicBoolean init(DuckDBTableFunctionInitInfo info) {
System.out.println("columnCount=" + info.getColumnCount());
System.out.println("columnIndex=" + info.getColumnIndex(0));
return new AtomicBoolean();
}
@Override
public long apply(
DuckDBTableFunctionCallInfo info,
DuckDBDataChunkWriter output) {
AtomicBoolean done = info.getInitData();
if (done.getAndSet(true)) {
return 0;
}
// Required today even though COUNT(*) does not use this value.
for (long row = 0; row < 3; row++) {
output.vector(0).setString(row, "payload");
}
return 3;
}
})
.register(connection);
Running:
SELECT count(*) FROM count_star_projection_probe();
currently reports:
columnCount=1
columnIndex=0
Expected behavior
The Java table-function API should provide a way to declare supported virtual columns, including the internal empty projection used for COUNT(*).
When that capability is available, a query that does not reference any regular column should not force the function to materialize one. The function should be able to produce only the row count for each output chunk.
C API dependency
The DuckDB core already supports TableFunction::get_virtual_columns and uses COLUMN_IDENTIFIER_EMPTY when resolving an empty projection. However, the public C table-function API does not appear to expose a way to register or return virtual columns.
I understand that implementing this in duckdb-java may therefore require a C API addition first.
- Dominant language
- C++
- Stars
- 127
- Forks
- 80
- Avg merge
- 13h 41m
- Merged PRs (30d)
- 44
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from duckdb/duckdb-java
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
duckdb/duckdb-java#832 · 2 comments ·
-
Native SIGSEGV in DuckDB JDBC when repeatedly executing queries with window functions and LIMIT 0 Open
Difficulty 4/5 3-5 days Newbie friendliness 48/100
duckdb/duckdb-java#871 · 4 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
duckdb/duckdb-java#872 ·
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
duckdb/duckdb-java#837 ·
-
executeBatch()` on a prepared INSERT runs s are ~1–2 orders of magnitude slower than the Appender Open
Difficulty 5/5 Over a week Newbie friendliness 42/100
duckdb/duckdb-java#815 · 1 comment ·
All issues in duckdb/duckdb-java
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
AXERA-TECH/ax-llm#77 ·
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
games-on-whales/wolf#509 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
bug-unconfirmed
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
NVIDIA/cuda-samples#453 ·