DuckDB C extension for in-process JIT compiled C UDFs via TinyCC
Maintainer(s):
Sounkou Mahamane Toure
Installing and Loading
INSTALL ducktinycc FROM community;
LOAD ducktinycc;
Example
-- Load the extension
LOAD ducktinycc;
-- Compile and register a simple C function as a SQL UDF
SELECT ok, mode, code
FROM tcc_module(
mode := 'quick_compile',
source := 'const char *hello_from_c(void){ return "hello from C"; }',
symbol := 'hello_from_c',
sql_name := 'hello_from_c',
return_type := 'cstring',
arg_types := []
);
-- Call the registered C UDF
SELECT hello_from_c() AS msg;
About ducktinycc
DuckTinyCC compiles and registers C scalar UDFs from SQL using TinyCC (libtcc), in-process.
Main SQL entrypoints:
| Function | Purpose |
|---|---|
| tcc_module(…) | Session config, build staging, codegen, compile, registration |
| tcc_system_paths(…) | Show effective TinyCC include/library search paths |
| tcc_library_probe(…) | Probe candidate library files and normalized link names |
Compile/codegen and C-type helper modes (via tcc_module):
| Mode | Purpose |
|---|---|
| quick_compile | One-shot source + codegen + compile + register |
| compile | Compile/register from staged session sources/bindings |
| codegen_preview | Emit generated wrapper C source without compile/load |
| c_struct | Generate + register struct helper UDFs from field specs |
| c_union | Generate + register union helper UDFs from field specs |
| c_bitfield | Generate + register bitfield struct helper UDFs |
| c_enum | Generate + register enum constant helper UDFs |
Generated helper naming:
| Helper mode | Generated SQL function pattern |
|---|---|
| c_struct | struct_ |
| c_union | union_ |
| c_bitfield | struct_ |
| c_enum | enum_ |
Type signature support (return_type / arg_types):
| Token family | Examples |
|---|---|
| Scalars | void (return only), bool/boolean, i8/u8/i16/u16/i32/u32/i64/u64, f32/f64, ptr/pointer/c_ptr, varchar/text/string/cstring, blob/bytea/binary/varbinary/buffer/bytes, uuid, date, time, timestamp/datetime, interval, decimal/numeric |
| Composites (recursive) | list |
| Legacy compatibility | list_i64 style list tokens are still accepted |
SQL <-> C bridge correspondences:
| SQL token family | C bridge type |
|---|---|
| varchar/text/string/cstring | const char * |
| blob/bytea/binary/varbinary/buffer/bytes | ducktinycc_blob_t |
| list<…>, type[] | ducktinycc_list_t |
| type[N] | ducktinycc_array_t |
| struct<…> | ducktinycc_struct_t |
| map<…> | ducktinycc_map_t |
| union<…> | ducktinycc_union_t |
| decimal/numeric | ducktinycc_decimal_t (DuckDB DECIMAL(18,3) at registration) |
Function result schemas:
| Function | Result columns |
|---|---|
| tcc_module(…) | ok BOOLEAN, mode VARCHAR, phase VARCHAR, code VARCHAR, message VARCHAR, detail VARCHAR, sql_name VARCHAR, symbol VARCHAR, artifact_id VARCHAR, connection_scope VARCHAR |
| tcc_system_paths(…) | kind VARCHAR, key VARCHAR, value VARCHAR, exists BOOLEAN, detail VARCHAR |
| tcc_library_probe(…) | kind VARCHAR, key VARCHAR, value VARCHAR, exists BOOLEAN, detail VARCHAR |
Codegen/runtime model:
- wrappers are generated from return_type/arg_types and registered through ducktinycc_register_signature(…)
- wrapper_mode supports row and batch execution paths
- code is compiled + relocated in-memory (no separate shared-library artifact)
Compile/runtime assets:
- compile/quick_compile use a TinyCC runtime path (headers + libraries)
- libtcc1.a is not strictly required for every compiled function; it is needed when generated code references TinyCC runtime helper symbols
- extern/system-library bindings can work without libtcc1.a when those helper symbols are not required and libraries are resolvable
Project details and examples: https://github.com/sounkou-bioinfo/DuckTinyCC
Community package excludes WASM and Windows targets.
Added Functions
| function_name | function_type | description | comment | examples |
|---|---|---|---|---|
| tcc_alloc | scalar | NULL | NULL | |
| tcc_dataptr | scalar | NULL | NULL | |
| tcc_free_ptr | scalar | NULL | NULL | |
| tcc_library_probe | table | NULL | NULL | |
| tcc_module | table | NULL | NULL | |
| tcc_ptr_add | scalar | NULL | NULL | |
| tcc_ptr_size | scalar | NULL | NULL | |
| tcc_read_bytes | scalar | NULL | NULL | |
| tcc_read_f32 | scalar | NULL | NULL | |
| tcc_read_f64 | scalar | NULL | NULL | |
| tcc_read_i16 | scalar | NULL | NULL | |
| tcc_read_i32 | scalar | NULL | NULL | |
| tcc_read_i64 | scalar | NULL | NULL | |
| tcc_read_i8 | scalar | NULL | NULL | |
| tcc_read_u16 | scalar | NULL | NULL | |
| tcc_read_u32 | scalar | NULL | NULL | |
| tcc_read_u64 | scalar | NULL | NULL | |
| tcc_read_u8 | scalar | NULL | NULL | |
| tcc_system_paths | table | NULL | NULL | |
| tcc_write_bytes | scalar | NULL | NULL | |
| tcc_write_f32 | scalar | NULL | NULL | |
| tcc_write_f64 | scalar | NULL | NULL | |
| tcc_write_i16 | scalar | NULL | NULL | |
| tcc_write_i32 | scalar | NULL | NULL | |
| tcc_write_i64 | scalar | NULL | NULL | |
| tcc_write_i8 | scalar | NULL | NULL | |
| tcc_write_u16 | scalar | NULL | NULL | |
| tcc_write_u32 | scalar | NULL | NULL | |
| tcc_write_u64 | scalar | NULL | NULL | |
| tcc_write_u8 | scalar | NULL | NULL |