Compile C from SQL into scalar, aggregate, and table functions in-process with an embedded TinyCC; no compiler or runtime files needed
Installing and Loading
INSTALL ducktinycc FROM community;
LOAD ducktinycc;
Example
LOAD ducktinycc;
-- Compile a C function and register it as collatz_steps(BIGINT) -> BIGINT
SELECT ok, code
FROM tcc_module(
mode := 'quick_compile',
source := 'int64_t collatz_steps(int64_t n) {
int64_t steps = 0;
if (n < 1) return -1;
while (n != 1) { n = (n & 1) ? 3 * n + 1 : n / 2; steps++; }
return steps;
}',
symbol := 'collatz_steps',
sql_name := 'collatz_steps',
return_type := 'i64',
arg_types := ['i64']
);
-- Longest Collatz chain below one million: 837799, 524 steps
SELECT arg_max(range, collatz_steps(range)) AS n, max(collatz_steps(range)) AS steps
FROM range(1, 1000000);
-- Every DuckTinyCC function, mode, type token, and C helper
SELECT kind, name, description FROM tcc_help();
About ducktinycc
DuckTinyCC compiles C from SQL using TinyCC (libtcc), in-process, and registers it as a scalar,
aggregate (kind := 'aggregate'), or table function (kind := 'table').
SELECT * FROM tcc_help() lists every function, tcc_module mode, type token, and C helper
with an example. Worked examples: https://sounkou-bioinfo.github.io/DuckTinyCC/cookbook.html
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 |
| tcc_help() | List every function, mode, type token, and C helper with an example |
Compile/codegen and C-type helper modes (via tcc_module):
| Mode | Purpose |
|---|---|
| quick_compile | One-shot source + codegen + compile + register; kind := 'scalar', 'aggregate', or 'table' |
| 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, i8/u8/i16/u16/i32/u32/i64/u64, f32/f64, ptr, varchar, blob, uuid, date, time, timestamp, interval, decimal |
| Composites (recursive) | list |
SQL <-> C bridge correspondences:
| SQL token family | C bridge type |
|---|---|
| varchar | const char * |
| blob | 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 | 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 |
| tcc_help() | kind VARCHAR, name VARCHAR, signature VARCHAR, description VARCHAR, example VARCHAR |
Codegen/runtime model:
- wrappers are generated from return_type/arg_types and registered through ducktinycc_register_signature(…)
- wrapper_mode is row (default) or chunk_scalar_loop, a generated C loop over each data chunk
- returned strings, blobs, and list/struct payloads should be allocated with ducktinycc_result_alloc(size); the memory belongs to the executing chunk and is freed after DuckDB copies the results
- code is compiled + relocated in-memory (no separate shared-library artifact)
- registering the same sql_name twice in a session returns false/E_INIT_FAILED (consistent across platforms); use tcc_new_state to reset before re-registering
Aggregate and table functions:
- kind := 'aggregate' uses
_state, _step, _combine, _final(state, R *out) returning 0 for NULL, and optional _init / _destroy - kind := 'table' uses
_state, _init(state, args...), _next(state, C1 *, ..., Cm *) returning 0 at the end, and optional _destroy; columns are the fields of a struct<...> return_type - ducktinycc_malloc / ducktinycc_realloc / ducktinycc_free give heap memory for states without linking libc
- DuckDB crashes C-API aggregates under agg(x ORDER BY y) and OVER () frames (duckdb/duckdb#26109); avoid those forms
Scalar UDF stability:
-
compile, quick_compile, and codegen_preview accept stability := 'consistent' 'volatile' - tinycc_bind can stage stability for a later compile; an explicit compile/quick_compile/codegen_preview stability value overrides the staged value
- use volatile for RNGs, counters, clocks, allocation, I/O, callbacks, or reads from mutable external memory so DuckDB re-runs the function and avoids constant-folding side effects
- generated helper modes set explicit helper stability internally: pure metadata/enum helpers are consistent, while allocation/free/setter/mutable-memory getter helpers are volatile
Embedded runtime (self-contained):
- libtcc1.a and all TinyCC include headers (stdarg.h, stddef.h, tccdefs.h, etc.) are baked into the extension binary at build time
- on the first compile or quick_compile call, tcc_ensure_embedded_runtime() extracts them to a content-hash-keyed temp directory (e.g. /tmp/ducktinycc_
/) - subsequent calls in the same process reuse that directory without re-extracting
- no separate TinyCC installation or runtime path configuration is needed after deployment
- tcc_system_paths() shows the active runtime path and whether it resolved from the embedded extraction
Project details and examples: https://github.com/sounkou-bioinfo/DuckTinyCC
Community package excludes WASM targets.
Additional Notes: Generated and helper functions are SQL scalar UDFs unless compiled with kind := 'aggregate' or 'table'; the extension's own table functions are tcc_module(…), tcc_system_paths(…), tcc_library_probe(…), and tcc_help(). For library linking, we can pass short names (m, z, c), explicit filenames (libfoo.so, foo.dll, .a, .lib), or path-like values. Because DuckTinyCC uses -nostdlib by default, use library := 'c' when generated code needs libc symbols that are not otherwise injected. TinyCC's libtcc1.a (compiler support: 64-bit float conversion, va_arg, stdatomic) is always linked. Pointer helpers are low-level interop tools; for most workflows, handle-based access is safer than raw tcc_dataptr
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_help | table | 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 |
Overloaded Functions
This extension does not add any function overloads.
Added Types
This extension does not add any types.
Added Settings
This extension does not add any settings.