Csilk 0.2.1
A lightweight, high-performance C HTTP web framework
Loading...
Searching...
No Matches
db.c File Reference

Unified database interface implementation. More...

#include "csilk/drivers/db.h"
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
#include "cJSON.h"
Include dependency graph for db.c:

Functions

void csilk_db_get_stats (csilk_db_stats_t *stats)
 Get current database statistics.
 
static cJSON * csilk_db_query_json_locked (csilk_db_pool_t *pool, const char *sql)
 Internal: execute a query and return the result as a cJSON array.
 
csilk_db_pool_tcsilk_db_pool_new (const char *driver_name, const char *dsn)
 Create a new database connection pool with the named driver.
 
void csilk_db_pool_free (csilk_db_pool_t *pool)
 Free a database pool and its underlying connection.
 
cJSON * csilk_db_query_json (csilk_db_pool_t *pool, const char *sql)
 Execute a SQL query and return the result as a cJSON array.
 
int csilk_db_exec (csilk_db_pool_t *pool, const char *sql)
 Execute a SQL statement (INSERT, UPDATE, DELETE, DDL) that does not return rows.
 
cJSON * csilk_db_query_param_json (csilk_db_pool_t *pool, const char *sql, const char **params)
 Execute a parameterized SQL query and return JSON result.
 
static void ensure_registry_init (void)
 
void csilk_db_init (void)
 Initialise the database subsystem.
 
int csilk_db_register_driver (const char *name, csilk_db_driver_t *driver)
 Statically-sized registry of registered database drivers (max 16).
 
csilk_db_driver_t * csilk_db_get_driver (const char *name)
 Look up a registered driver by name.
 

Variables

static atomic_uint_fast64_t db_queries_total = 0
 
static atomic_uint_fast64_t db_execs_total = 0
 
static atomic_uint_fast64_t db_errors_total = 0
 
static atomic_uint_fast64_t db_duration_us_total = 0
 
static csilk_db_driver_t * drivers [16]
 Statically-sized registry of registered database drivers (max 16).
 
static int driver_count = 0
 
static uv_mutex_t registry_mutex
 
static int registry_initialized = 0
 

Detailed Description

Unified database interface implementation.

Architecture

The DB layer uses a pluggable driver registry pattern:

csilk_db_driver_t (abstract interface) ├── connect(pool, dsn) → establishes connection ├── query(pool, sql, result) → returns tabular results ├── exec(pool, sql) → executes non-query statements ├── free_result(result) → releases query results └── disconnect(pool) → tears down connection

Drivers self-register at startup (e.g., csilk_db_sqlite_init() calls csilk_db_register_driver()). The registry is a simple fixed-size array protected by a mutex.

Pool lifecycle

csilk_db_pool_new() → driver->connect() → [query/exec] → pool_free() → driver->disconnect()

The pool itself is a thin wrapper: it holds a driver pointer, a mutex (for serializing access to the single connection), and driver-specific state in an opaque handle field.

JSON result conversion

csilk_db_query_json() and friends convert the driver's tabular result (csilk_db_result_t) into a cJSON array of objects — one object per row, with column names as keys. This is the format expected by the HTTP layer for JSON API responses.

Function Documentation

◆ csilk_db_exec()

int csilk_db_exec ( csilk_db_pool_t pool,
const char *  sql 
)

Execute a SQL statement (INSERT, UPDATE, DELETE, DDL) that does not return rows.

Execute a statement that produces no result rows.

Execute a statement that returns no result rows.

◆ csilk_db_get_driver()

csilk_db_driver_t * csilk_db_get_driver ( const char *  name)

Look up a registered driver by name.

Parameters
nameDriver identifier string.
Returns
Pointer to the registered csilk_db_driver_t, or NULL if not found.

◆ csilk_db_get_stats()

void csilk_db_get_stats ( csilk_db_stats_t stats)

Get current database statistics.

Parameters
stats[out] Pointer to stats struct to populate.

◆ csilk_db_init()

void csilk_db_init ( void  )

Initialise the database subsystem.

Internal: Register the built-in MySQL driver.

Registers all built-in drivers (SQLite3, MySQL, PostgreSQL, etc.). Must be called once before any csilk_db_pool_new call. Safe to call multiple times.

◆ csilk_db_pool_free()

void csilk_db_pool_free ( csilk_db_pool_t pool)

Free a database pool and its underlying connection.

Destroy a database pool and disconnect.

Free a database pool and disconnect.

Teardown

  1. Call driver->disconnect() — closes the database connection.
  2. Destroy the pool mutex.
  3. Free the pool struct.

The driver's free_result is not called here — any outstanding results must have been freed by the caller.

◆ csilk_db_pool_new()

csilk_db_pool_t * csilk_db_pool_new ( const char *  driver_name,
const char *  dsn 
)

Create a new database connection pool with the named driver.

Create a new database pool and connect using the named driver.

Create a new database connection pool.

Pool creation

  1. Look up the driver by name in the global registry.
  2. calloc the pool struct, init the pool mutex.
  3. Call driver->connect() with the DSN — this may block for network round-trips.
  4. On success: return the pool. On failure: destroy mutex, free pool.

The pool holds exactly ONE connection (mutex-serialized access). This is intentional — the caller is expected to manage a pool of pools if concurrency is needed.

Parameters
driver_nameRegistered driver name (e.g., "sqlite3").
dsnData source name (e.g., "/tmp/test.db" or "host=...").

◆ csilk_db_query_json()

cJSON * csilk_db_query_json ( csilk_db_pool_t pool,
const char *  sql 
)

Execute a SQL query and return the result as a cJSON array.

Execute a SELECT query and return the rows as a JSON array.

Execute a SELECT query and return the result as a JSON array.

◆ csilk_db_query_json_locked()

static cJSON * csilk_db_query_json_locked ( csilk_db_pool_t pool,
const char *  sql 
)
static

Internal: execute a query and return the result as a cJSON array.

Row-to-JSON conversion

  1. Call driver->query() to get the raw tabular result (rows + columns).
  2. Create an empty cJSON array.
  3. For each row (result.rows[i]): a. Create a cJSON object. b. For each field (row->values[j]):
    • Add (column_name[j], field_value) as a string key-value pair.
    • If column_names is NULL, use "col" as the key.
    • NULL values are skipped (not added to the object). c. Append the object to the array.
  4. Call driver->free_result() to release the driver's memory.
  5. Return the cJSON array (caller must cJSON_Delete()).

All values are represented as cJSON strings — no type inference is attempted. The caller can parse numerics/bools with cJSON_GetNumberValue etc. if needed.

Parameters
poolDatabase pool with an active connection.
sqlSQL query string.
Returns
A cJSON array of row objects, or NULL on failure.
Note
The returned cJSON must be freed by the caller with cJSON_Delete().
Warning
The pool mutex must be held for the duration of this call.

◆ csilk_db_query_param_json()

cJSON * csilk_db_query_param_json ( csilk_db_pool_t pool,
const char *  sql,
const char **  params 
)

Execute a parameterized SQL query and return JSON result.

Execute a parameterised SELECT query with ? placeholders.

String substitution algorithm

  1. Pre-compute the final SQL length: original SQL + sum of all param lengths + 2 bytes per param for surrounding single quotes.
  2. Allocate a buffer of that size.
  3. Walk the original SQL character by character:
    • '?' → replace with 'value' (single-quote wrapped)
    • any other char → copy verbatim.
  4. Execute the constructed SQL via csilk_db_query_json_locked().

Security caveat

This is naive string substitution — NOT prepared-statement binding. Parameter values are NOT escaped for SQL special characters. A parameter containing "' OR '1'='1" will inject into the SQL verbatim.

Parameters
poolDatabase pool.
sqlSQL pattern with ? placeholders.
paramsNULL-terminated array of string values.

◆ csilk_db_register_driver()

int csilk_db_register_driver ( const char *  name,
csilk_db_driver_t *  driver 
)

Statically-sized registry of registered database drivers (max 16).

Register a database driver implementation.

◆ ensure_registry_init()

static void ensure_registry_init ( void  )
static

Variable Documentation

◆ db_duration_us_total

atomic_uint_fast64_t db_duration_us_total = 0
static

◆ db_errors_total

atomic_uint_fast64_t db_errors_total = 0
static

◆ db_execs_total

atomic_uint_fast64_t db_execs_total = 0
static

◆ db_queries_total

atomic_uint_fast64_t db_queries_total = 0
static

◆ driver_count

int driver_count = 0
static

◆ drivers

csilk_db_driver_t* drivers[16]
static

Statically-sized registry of registered database drivers (max 16).

◆ registry_initialized

int registry_initialized = 0
static

◆ registry_mutex

uv_mutex_t registry_mutex
static