Fun API Documentation 0.42.1
The programming language that makes you have fun!
Loading...
Searching...
No Matches
sqlite.c
Go to the documentation of this file.
1/*
2 * This file is part of the Fun programming language.
3 * https://fun-lang.xyz/
4 *
5 * Copyright 2025 Johannes Findeisen <you@hanez.org>
6 * Licensed under the terms of the Apache-2.0 license.
7 * https://opensource.org/license/apache-2-0
8 */
9
10/**
11 * @file sqlite.c
12 * @brief SQLite handle registry and helper utilities for the Fun VM extension.
13 *
14 * This translation unit implements a tiny in-process registry for SQLite
15 * connection handles that can be used by the VM opcodes living under
16 * src/vm/sqlite/*.c. The registry abstracts over raw sqlite3* pointers and
17 * assigns small positive integer identifiers to each connection. VM opcodes can
18 * then pass these identifiers around instead of raw pointers.
19 *
20 * Build-time feature flag
21 * -----------------------
22 * The code is compiled only when the CMake option FUN_WITH_SQLITE is enabled
23 * (i.e., the preprocessor symbol FUN_WITH_SQLITE is defined). When disabled,
24 * this file contributes no symbols and the corresponding opcodes should be
25 * compiled out or provide appropriate fallbacks.
26 *
27 * Ownership and lifetime
28 * ----------------------
29 * - The registry does NOT open or close databases. It merely stores pointers
30 * that were created elsewhere (e.g., via sqlite3_open()).
31 * - Adding an entry does not transfer ownership of the sqlite3 connection.
32 * Callers remain responsible for invoking sqlite3_close() at the appropriate
33 * time.
34 * - Removing an entry from the registry does NOT close the connection; it only
35 * forgets the mapping between id and pointer.
36 * - Integer identifiers are monotonically increasing per process. Once a handle
37 * id is deleted, it will not be reused within the same process lifetime.
38 *
39 * Error handling
40 * --------------
41 * Functions in this module perform only basic validation and memory allocation.
42 * Allocation failures return NULL (for lookups/additions) or are silently
43 * ignored (for deletions of non-existent ids). No SQLite API calls are made
44 * here, so no sqlite error codes are produced by this module itself.
45 *
46 * Thread-safety
47 * -------------
48 * The registry is implemented as a simple singly-linked list with no
49 * synchronization. It is NOT thread-safe. If the VM uses SQLite from multiple
50 * threads, the caller must provide external synchronization around all calls to
51 * these helpers.
52 *
53 * Example
54 * -------
55 * @code{.c}
56 * // Open a database elsewhere:
57 * sqlite3 *db = NULL;
58 * if (sqlite3_open(":memory:", &db) == SQLITE_OK) {
59 * // Register and get an id:
60 * SqlHandle *h = sql_reg_add(db);
61 * int id = h ? h->id : -1;
62 *
63 * // Later look it up:
64 * SqlHandle *same = sql_reg_get(id);
65 * if (same) {
66 * // use same->db with SQLite APIs
67 * }
68 *
69 * // When finished, drop the registry entry and close manually:
70 * sql_reg_del(id);
71 * sqlite3_close(db);
72 * }
73 * @endcode
74 */
75#ifdef FUN_WITH_SQLITE
76#include <sqlite3.h>
77
78/**
79 * @brief Node in a singly-linked list of registered SQLite handles.
80 *
81 * Each node associates a monotonically increasing positive integer identifier
82 * with a raw sqlite3* pointer. The list head is stored in a file-static global
83 * (g_sql_handles).
84 */
85typedef struct SqlHandle {
86 int id; /**< Positive identifier assigned by the registry. */
87 sqlite3 *db; /**< Opaque pointer to an opened sqlite3 connection. */
88 struct SqlHandle *next; /**< Next entry in the singly-linked list. */
90
91/**
92 * @brief Global head of the SQLite handle list.
93 *
94 * NULL denotes an empty list. The list is modified only by the helpers in this
95 * file and is never exposed directly to callers.
96 */
97static SqlHandle *g_sql_handles = NULL;
98/**
99 * @brief Next positive identifier to assign to a newly added handle.
100 *
101 * Starts at 1 and increases monotonically. Identifiers are not reused across
102 * deletions within a process lifetime.
103 */
104static int g_sql_next_id = 1;
105
106/**
107 * @brief Add a sqlite3 handle to the registry.
108 *
109 * Allocates a new list node, assigns a fresh positive id, and prepends it to
110 * the internal registry list. Ownership of the sqlite3 connection remains with
111 * the caller; this registry does not close the handle during deletion.
112 *
113 * @param db Valid pointer to an opened sqlite3 connection.
114 * @return Pointer to the newly created SqlHandle entry on success; NULL on
115 * allocation failure. The returned pointer remains owned by the
116 * registry; do not free it directly.
117 *
118 * @note This function does not take ownership of the sqlite3 connection in the
119 * sense of closing it; removal from the registry will not call
120 * sqlite3_close().
121 */
122static SqlHandle *sql_reg_add(sqlite3 *db) {
123 SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle));
124 if (!h) return NULL;
125 h->id = g_sql_next_id++;
126 h->db = db;
127 h->next = g_sql_handles;
128 g_sql_handles = h;
129 return h;
130}
131
132/**
133 * @brief Look up a registered SQLite handle by id.
134 *
135 * Performs a linear search over the internal list to find a matching id.
136 *
137 * @param id Positive identifier previously returned by sql_reg_add().
138 * @return Pointer to the SqlHandle entry if found; NULL otherwise.
139 *
140 * @note The returned pointer is owned by the registry and must not be freed by
141 * the caller.
142 */
143static SqlHandle *sql_reg_get(int id) {
144 for (SqlHandle *p = g_sql_handles; p; p = p->next)
145 if (p->id == id) return p;
146 return NULL;
147}
148
149/**
150 * @brief Remove a SQLite handle entry from the registry.
151 *
152 * Deletes the list node associated with the given id.
153 *
154 * @param id Positive identifier of the entry to remove.
155 *
156 * @note This function does not close the underlying sqlite3 connection; the
157 * caller is responsible for calling sqlite3_close() if appropriate.
158 * @note If the id does not exist, the function is a no-op.
159 */
160static void sql_reg_del(int id) {
161 SqlHandle **pp = &g_sql_handles;
162 while (*pp) {
163 if ((*pp)->id == id) {
164 SqlHandle *d = *pp;
165 *pp = d->next;
166 free(d);
167 return;
168 }
169 pp = &(*pp)->next;
170 }
171}
172#endif
static void sql_reg_del(int id)
Remove a SQLite handle entry from the registry.
Definition sqlite.c:160
static SqlHandle * sql_reg_get(int id)
Look up a registered SQLite handle by id.
Definition sqlite.c:143
static SqlHandle * g_sql_handles
Global head of the SQLite handle list.
Definition sqlite.c:97
static int g_sql_next_id
Next positive identifier to assign to a newly added handle.
Definition sqlite.c:104
static SqlHandle * sql_reg_add(sqlite3 *db)
Add a sqlite3 handle to the registry.
Definition sqlite.c:122
Node in a singly-linked list of registered SQLite handles.
Definition sqlite.c:85
int id
Definition sqlite.c:86
struct SqlHandle * next
Definition sqlite.c:88
sqlite3 * db
Definition sqlite.c:87