KDBC Overview
KDBC (Kotlin Database Connectivity) is a unified database connectivity layer that works across JVM, Linux (x64 and ARM64), Windows, macOS, iOS, and Android. Think of it as "JDBC, but cross-platform" -- it provides the same familiar interfaces (DataSource, Connection, PreparedStatement, ResultSet) on every platform Stormify supports.
Two Faces of KDBC
KDBC has two layers that can be used independently:
1. C Library (libkdbc) -- a standalone C11 library for native/embedded use. It provides a single API
that talks to SQLite, PostgreSQL, MariaDB/MySQL, Oracle, and MSSQL, loading driver libraries at runtime
via dlopen. You can use it from any C or C++ project without Kotlin.
2. Kotlin Multiplatform Wrapper -- a set of common interfaces (DataSource, Connection,
PreparedStatement, ResultSet, etc.) with platform-specific implementations:
- JVM: wraps
java.sql.*(zero overhead delegation to JDBC) - Native (Linux x64/ARM64, Windows x64, macOS): wraps the C library above via Kotlin/Native cinterop — all 5 drivers available
- Android: wraps
android.database.sqlite.SQLiteDatabase - iOS: wraps SQLite via cinterop (same C driver as other native targets)
Architecture
Opaque Handles
The C library uses three opaque handle types:
| Handle | Represents | Lifecycle |
|---|---|---|
kdbc_conn |
A database connection | kdbc_connect ... kdbc_close |
kdbc_stmt |
A prepared statement | kdbc_prepare ... kdbc_stmt_close |
kdbc_result |
A result set (rows) | kdbc_execute_query ... kdbc_result_close |
Driver Virtual Table
Each database backend implements a driver vtable. The core dispatches calls to the active driver through function pointers -- the application code never interacts with driver internals directly.
Runtime Library Loading
KDBC loads database client libraries at runtime using dlopen (Linux/macOS) or LoadLibrary (Windows).
This means:
- You compile and link against
libkdbconly -- no database client libraries needed at link time. - Missing drivers are handled gracefully:
kdbc_driver_available()returns 0 if the native library is not installed, andkdbc_connect()returns NULL with a descriptive error. - You can ship a single binary that works with whichever databases the user has installed.
Supported Databases
| Database | Driver Constant | Native Library | Platforms |
|---|---|---|---|
| SQLite | KDBC_SQLITE |
libsqlite3 | Linux, Windows, macOS, iOS, Android |
| PostgreSQL | KDBC_POSTGRES |
libpq | Linux, Windows, macOS |
| MariaDB / MySQL | KDBC_MARIADB |
libmariadb | Linux, Windows, macOS |
| Oracle | KDBC_ORACLE |
ODPI-C | Linux, Windows, macOS |
| MS SQL Server | KDBC_MSSQL |
FreeTDS (libsybdb) | Linux, Windows, macOS |
Key Design Decisions
JDBC-Style Placeholders
All SQL uses ? as parameter placeholders, regardless of the backend. KDBC automatically translates
to the native placeholder syntax:
| Database | Native Syntax | KDBC Input |
|---|---|---|
| SQLite, MariaDB, MSSQL | ? |
? (no change) |
| PostgreSQL | $1, $2, ... |
? |
| Oracle | :1, :2, ... |
? |
1-Indexed Parameters and Columns
All parameter indices and column indices are 1-based (matching JDBC convention), not 0-based.
Per-Handle Error Buffers
Each connection and statement maintains its own error buffer. There is also a thread-local global error
for cases where no handle exists yet (e.g., connection failures). Error messages from the underlying
database pass through verbatim (e.g., you'll see ORA-00942: table or view does not exist from Oracle).
Generated Key Strategies
Different databases retrieve auto-generated keys differently. KDBC abstracts this with three strategies:
| Strategy | Databases | Mechanism |
|---|---|---|
KDBC_GK_BY_INDEX |
SQLite, MySQL, MSSQL 2012+ | last_insert_id() / SCOPE_IDENTITY() |
KDBC_GK_BY_NAME |
PostgreSQL, Oracle 12c+ | RETURNING clause |
KDBC_GK_NONE |
Oracle 11g | Uses sequences (no auto-key) |
Platform Summary
| Platform | Databases | Implementation |
|---|---|---|
| JVM | Any JDBC-compatible | Wraps java.sql.* |
| Linux x64 (linuxX64) | All 5 (SQLite, PG, MariaDB, Oracle, MSSQL) | C library via cinterop |
| Linux ARM64 (linuxArm64) | All 5 | C library via cinterop |
| Windows x64 (mingwX64) | All 5 | C library via cinterop |
| Android | SQLite | Wraps android.database.sqlite.* |
| macOS (macosArm64, macosX64) | All 5 | C library via cinterop |
| iOS (iosArm64, iosX64, iosSimulatorArm64) | SQLite | C library via cinterop |
What's Next
- KDBC C Guide -- building and using the C library directly
- KDBC Kotlin Guide -- using KDBC from Kotlin Multiplatform
- C API Reference -- auto-generated Doxygen documentation