Search Shortcut cmd + k | ctrl + k
ducktinycc

Compile C from SQL into scalar, aggregate, and table functions in-process with an embedded TinyCC; no compiler or runtime files needed

Maintainer(s): sounkou-bioinfo

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__new/free/get_*/set_*/off_*/addr/sizeof/alignof
c_union union__new/free/get_*/set_*/off_*/addr/sizeof/alignof
c_bitfield struct__get_*/set_*/sizeof/alignof
c_enum enum__, enum__sizeof

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, type[], type[N], struct<name:type;...>, map<key_type;value_type>, union<name:type;...>

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.