AtlatestRepositorysigil-sqlite
1/*
2 * Sigil SQLite Bindings
3 *
4 * Provides SQLite database access for Sigil applications.
5 * Uses SQLite amalgamation for easy embedding.
6 */
7
8#include <sigil/sigil.h>
9#include "../vendor/sqlite3.h"
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
14/*
15 * Database handle structure
16 */
17typedef struct {
18 sqlite3 *db;
19 int closed;
20 /* Cooperative connection lock. A SQLite connection is a SERIAL resource:
21 * its implicit read transaction ends only when no statement is active, so
22 * two fibers interleaving on one connection can pin a snapshot. The
23 * scheme layer holds this across a whole primitive, yields included.
24 * Single-threaded cooperative runtime, so a plain int is atomic enough:
25 * only a yield can interleave, and this is never held across one without
26 * the holder intending it. */
27 int locked;
28} SqliteDB;
30/*
31 * Statement handle structure
32 */
33typedef struct {
34 sqlite3_stmt *stmt;
35 sqlite3 *db; /* Reference to parent database */
36 int finalized;
37} SqliteStmt;
39/* Type tags for foreign objects */
40static Value db_type_tag = SIGIL_UNDEFINED;
41static Value stmt_type_tag = SIGIL_UNDEFINED;
43/*
44 * Finalizer for database handles
45 */
46static void db_finalizer(void *data)
48 SqliteDB *handle = (SqliteDB *)data;
49 if (handle && !handle->closed && handle->db) {
50 sqlite3_close(handle->db);
51 }
52 free(handle);
55/*
56 * Finalizer for statement handles
57 */
58static void stmt_finalizer(void *data)
60 SqliteStmt *handle = (SqliteStmt *)data;
61 if (handle && !handle->finalized && handle->stmt) {
62 sqlite3_finalize(handle->stmt);
63 }
64 free(handle);
67/*
68 * Helper: Check if value is a database handle
69 */
70static int is_db(Value v)
72 if (!sigil_is_foreign(v)) return 0;
73 return sigil_foreign_type(v) == db_type_tag;
76/*
77 * Helper: Check if value is a statement handle
78 */
79static int is_stmt(Value v)
81 if (!sigil_is_foreign(v)) return 0;
82 return sigil_foreign_type(v) == stmt_type_tag;
85/*
86 * Helper: Get database data from value
87 */
88static SqliteDB *as_db(Value v)
90 return (SqliteDB *)sigil_foreign_data(v);
93/*
94 * Helper: Get statement data from value
95 */
96static SqliteStmt *as_stmt(Value v)
98 return (SqliteStmt *)sigil_foreign_data(v);
101/*
102 * Helper: Extract null-terminated string from Sigil string
103 * Caller must free the returned string.
104 */
105static char *extract_string(Value v)
107 SigilString *s = (SigilString *)sigil_as_ptr(v);
108 char *result = malloc(s->byte_length + 1);
109 if (!result) return NULL;
110 memcpy(result, s->data, s->byte_length);
111 result[s->byte_length] = '\0';
112 return result;
115/*
116 * sqlite-open path -> db | #f
117 * Open a database file. Creates if it doesn't exist.
118 */
119static Value native_sqlite_open(SigilVM *vm, int argc, Value *args)
121 (void)argc;
123 if (!sigil_is_string(args[0])) {
124 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-open: expected string for path");
125 return SIGIL_UNDEFINED;
126 }
128 char *path = extract_string(args[0]);
129 if (!path) return SIGIL_FALSE;
131 sqlite3 *db;
132 int rc = sqlite3_open(path, &db);
133 free(path);
135 if (rc != SQLITE_OK) {
136 if (db) sqlite3_close(db);
137 return SIGIL_FALSE;
138 }
140 SqliteDB *handle = malloc(sizeof(SqliteDB));
141 if (!handle) {
142 sqlite3_close(db);
143 return SIGIL_FALSE;
144 }
146 handle->db = db;
147 handle->closed = 0;
148 handle->locked = 0;
150 return sigil_make_foreign(vm, db_type_tag, handle, db_finalizer, sizeof(SqliteDB));
153/*
154 * sqlite-close db -> boolean
155 * Close a database connection.
156 */
157static Value native_sqlite_close(SigilVM *vm, int argc, Value *args)
159 (void)argc;
161 if (!is_db(args[0])) {
162 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-close: expected database handle");
163 return SIGIL_UNDEFINED;
164 }
166 SqliteDB *handle = as_db(args[0]);
167 if (handle->closed) {
168 return SIGIL_TRUE; /* Already closed */
169 }
171 int rc = sqlite3_close(handle->db);
172 if (rc != SQLITE_OK) {
173 return SIGIL_FALSE;
174 }
176 handle->db = NULL;
177 handle->closed = 1;
178 return SIGIL_TRUE;
181/*
182 * sqlite-db? value -> boolean
183 * Check if value is a database handle.
184 */
185static Value native_sqlite_dbp(SigilVM *vm, int argc, Value *args)
187 (void)vm;
188 (void)argc;
189 return sigil_bool(is_db(args[0]));
192/*
193 * sqlite-stmt? value -> boolean
194 * Check if value is a statement handle.
195 */
196static Value native_sqlite_stmtp(SigilVM *vm, int argc, Value *args)
198 (void)vm;
199 (void)argc;
200 return sigil_bool(is_stmt(args[0]));
203/*
204 * sqlite-exec db sql -> boolean
205 * Execute SQL that doesn't return rows (CREATE, INSERT, UPDATE, DELETE).
206 */
207static Value native_sqlite_exec(SigilVM *vm, int argc, Value *args)
209 (void)argc;
211 if (!is_db(args[0])) {
212 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-exec: expected database handle");
213 return SIGIL_UNDEFINED;
214 }
215 if (!sigil_is_string(args[1])) {
216 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-exec: expected string for SQL");
217 return SIGIL_UNDEFINED;
218 }
220 SqliteDB *handle = as_db(args[0]);
221 if (handle->closed) {
222 sigil__vm_error(vm, SIGIL_ERR_IO, "sqlite-exec: database is closed");
223 return SIGIL_UNDEFINED;
224 }
226 char *sql = extract_string(args[1]);
227 if (!sql) return SIGIL_FALSE;
229 /* Force non-blocking: never let sqlite3_exec block the single cooperative
230 * scheduler thread waiting on a lock. A contended lock returns SQLITE_BUSY
231 * immediately, which we surface as the symbol 'busy so the Scheme wrapper
232 * can yield to the scheduler + retry cooperatively (letting the lock holder
233 * run and commit). This overrides any PRAGMA busy_timeout the caller set —
234 * that intent is honored by the wrapper's retry budget instead. */
235 sqlite3_busy_timeout(handle->db, 0);
237 char *errmsg = NULL;
238 int rc = sqlite3_exec(handle->db, sql, NULL, NULL, &errmsg);
239 free(sql);
240 if (errmsg) sqlite3_free(errmsg);
242 if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
243 return sigil_intern_symbol(vm, "busy", 4);
244 }
245 if (rc != SQLITE_OK) {
246 return SIGIL_FALSE;
247 }
248 return SIGIL_TRUE;
251/*
252 * sqlite-prepare db sql -> stmt | #f
253 * Prepare a SQL statement for execution.
254 */
255static Value native_sqlite_prepare(SigilVM *vm, int argc, Value *args)
257 (void)argc;
259 if (!is_db(args[0])) {
260 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-prepare: expected database handle");
261 return SIGIL_UNDEFINED;
262 }
263 if (!sigil_is_string(args[1])) {
264 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-prepare: expected string for SQL");
265 return SIGIL_UNDEFINED;
266 }
268 SqliteDB *db_handle = as_db(args[0]);
269 if (db_handle->closed) {
270 sigil__vm_error(vm, SIGIL_ERR_IO, "sqlite-prepare: database is closed");
271 return SIGIL_UNDEFINED;
272 }
274 SigilString *sql_str = (SigilString *)sigil_as_ptr(args[1]);
276 sqlite3_stmt *stmt;
277 int rc = sqlite3_prepare_v2(db_handle->db, sql_str->data, sql_str->byte_length,
278 &stmt, NULL);
280 if (rc != SQLITE_OK || !stmt) {
281 return SIGIL_FALSE;
282 }
284 SqliteStmt *handle = malloc(sizeof(SqliteStmt));
285 if (!handle) {
286 sqlite3_finalize(stmt);
287 return SIGIL_FALSE;
288 }
290 handle->stmt = stmt;
291 handle->db = db_handle->db;
292 handle->finalized = 0;
294 return sigil_make_foreign(vm, stmt_type_tag, handle, stmt_finalizer, sizeof(SqliteStmt));
297/*
298 * sqlite-bind stmt index value -> boolean
299 * Bind a value to a parameter in a prepared statement.
300 * Index is 1-based.
301 */
302static Value native_sqlite_bind(SigilVM *vm, int argc, Value *args)
304 (void)argc;
306 if (!is_stmt(args[0])) {
307 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-bind: expected statement handle");
308 return SIGIL_UNDEFINED;
309 }
310 if (!sigil_is_fixnum(args[1])) {
311 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-bind: expected integer for index");
312 return SIGIL_UNDEFINED;
313 }
315 SqliteStmt *handle = as_stmt(args[0]);
316 if (handle->finalized) {
317 sigil__vm_error(vm, SIGIL_ERR_IO, "sqlite-bind: statement is finalized");
318 return SIGIL_UNDEFINED;
319 }
321 int index = (int)sigil_as_fixnum(args[1]);
322 Value value = args[2];
323 int rc;
325 if (sigil_is_null(value) || sigil_is_false(value)) {
326 /* Bind NULL */
327 rc = sqlite3_bind_null(handle->stmt, index);
328 } else if (sigil_is_fixnum(value)) {
329 /* Bind integer */
330 rc = sqlite3_bind_int64(handle->stmt, index, sigil_as_fixnum(value));
331 } else if (sigil_is_flonum(value)) {
332 /* Bind double */
333 rc = sqlite3_bind_double(handle->stmt, index, sigil_as_flonum(value));
334 } else if (sigil_is_string(value)) {
335 /* Bind text */
336 SigilString *s = (SigilString *)sigil_as_ptr(value);
337 rc = sqlite3_bind_text(handle->stmt, index, s->data, s->byte_length,
338 SQLITE_TRANSIENT);
339 } else if (sigil_is_bytevector(value)) {
340 /* Bind blob */
341 SigilBytevector *bv = (SigilBytevector *)sigil_as_ptr(value);
342 rc = sqlite3_bind_blob(handle->stmt, index, bv->data, bv->length,
343 SQLITE_TRANSIENT);
344 } else {
345 sigil__vm_error(vm, SIGIL_ERR_TYPE,
346 "sqlite-bind: value must be null, boolean, integer, real, string, or bytevector");
347 return SIGIL_UNDEFINED;
348 }
350 return sigil_bool(rc == SQLITE_OK);
353/*
354 * sqlite-step stmt -> 'row | 'done | #f
355 * Execute one step of a prepared statement.
356 * Returns 'row if a row is available, 'done if finished, #f on error.
357 */
358static Value native_sqlite_step(SigilVM *vm, int argc, Value *args)
360 (void)argc;
362 if (!is_stmt(args[0])) {
363 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-step: expected statement handle");
364 return SIGIL_UNDEFINED;
365 }
367 SqliteStmt *handle = as_stmt(args[0]);
368 if (handle->finalized) {
369 sigil__vm_error(vm, SIGIL_ERR_IO, "sqlite-step: statement is finalized");
370 return SIGIL_UNDEFINED;
371 }
373 /* Force non-blocking (see native_sqlite_exec): a contended lock surfaces as
374 * 'busy for the Scheme wrapper to yield + retry, rather than blocking the
375 * scheduler thread inside sqlite3_step. */
376 sqlite3_busy_timeout(sqlite3_db_handle(handle->stmt), 0);
378 int rc = sqlite3_step(handle->stmt);
380 if (rc == SQLITE_ROW) {
381 return sigil_intern_symbol(vm, "row", 3);
382 } else if (rc == SQLITE_DONE) {
383 return sigil_intern_symbol(vm, "done", 4);
384 } else if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
385 return sigil_intern_symbol(vm, "busy", 4);
386 } else {
387 return SIGIL_FALSE;
388 }
391/*
392 * sqlite-column-count stmt -> integer
393 * Get the number of columns in the result set.
394 */
395static Value native_sqlite_column_count(SigilVM *vm, int argc, Value *args)
397 (void)argc;
399 if (!is_stmt(args[0])) {
400 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-column-count: expected statement handle");
401 return SIGIL_UNDEFINED;
402 }
404 SqliteStmt *handle = as_stmt(args[0]);
405 if (handle->finalized) {
406 return sigil_fixnum(0);
407 }
409 return sigil_fixnum(sqlite3_column_count(handle->stmt));
412/*
413 * sqlite-column-name stmt index -> string
414 * Get the name of a column (0-based index).
415 */
416static Value native_sqlite_column_name(SigilVM *vm, int argc, Value *args)
418 (void)argc;
420 if (!is_stmt(args[0])) {
421 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-column-name: expected statement handle");
422 return SIGIL_UNDEFINED;
423 }
424 if (!sigil_is_fixnum(args[1])) {
425 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-column-name: expected integer for index");
426 return SIGIL_UNDEFINED;
427 }
429 SqliteStmt *handle = as_stmt(args[0]);
430 if (handle->finalized) {
431 return SIGIL_FALSE;
432 }
434 int index = (int)sigil_as_fixnum(args[1]);
435 const char *name = sqlite3_column_name(handle->stmt, index);
437 if (!name) return SIGIL_FALSE;
438 return sigil_make_string(vm, name, strlen(name));
441/*
442 * sqlite-column stmt index -> value
443 * Get the value of a column in the current row (0-based index).
444 * Returns appropriate Sigil type based on SQLite column type.
445 */
446static Value native_sqlite_column(SigilVM *vm, int argc, Value *args)
448 (void)argc;
450 if (!is_stmt(args[0])) {
451 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-column: expected statement handle");
452 return SIGIL_UNDEFINED;
453 }
454 if (!sigil_is_fixnum(args[1])) {
455 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-column: expected integer for index");
456 return SIGIL_UNDEFINED;
457 }
459 SqliteStmt *handle = as_stmt(args[0]);
460 if (handle->finalized) {
461 return SIGIL_FALSE;
462 }
464 int index = (int)sigil_as_fixnum(args[1]);
465 int type = sqlite3_column_type(handle->stmt, index);
467 switch (type) {
468 case SQLITE_NULL:
469 return SIGIL_FALSE; /* Use #f for NULL */
471 case SQLITE_INTEGER:
472 return sigil_fixnum(sqlite3_column_int64(handle->stmt, index));
474 case SQLITE_FLOAT:
475 return sigil_flonum(sqlite3_column_double(handle->stmt, index));
477 case SQLITE_TEXT: {
478 const char *text = (const char *)sqlite3_column_text(handle->stmt, index);
479 int len = sqlite3_column_bytes(handle->stmt, index);
480 return sigil_make_string(vm, text, len);
481 }
483 case SQLITE_BLOB: {
484 const void *data = sqlite3_column_blob(handle->stmt, index);
485 int len = sqlite3_column_bytes(handle->stmt, index);
486 Value bv = sigil_make_bytevector(vm, (size_t)len);
487 if (sigil_is_bytevector(bv)) {
488 memcpy(sigil_bytevector_data(bv), data, len);
489 }
490 return bv;
491 }
493 default:
494 return SIGIL_FALSE;
495 }
498/*
499 * sqlite-reset stmt -> boolean
500 * Reset a prepared statement to its initial state.
501 */
502static Value native_sqlite_reset(SigilVM *vm, int argc, Value *args)
504 (void)argc;
506 if (!is_stmt(args[0])) {
507 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-reset: expected statement handle");
508 return SIGIL_UNDEFINED;
509 }
511 SqliteStmt *handle = as_stmt(args[0]);
512 if (handle->finalized) {
513 return SIGIL_FALSE;
514 }
516 int rc = sqlite3_reset(handle->stmt);
517 return sigil_bool(rc == SQLITE_OK);
520/*
521 * sqlite-finalize stmt -> boolean
522 * Finalize a prepared statement, freeing resources.
523 */
524static Value native_sqlite_finalize(SigilVM *vm, int argc, Value *args)
526 (void)argc;
528 if (!is_stmt(args[0])) {
529 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-finalize: expected statement handle");
530 return SIGIL_UNDEFINED;
531 }
533 SqliteStmt *handle = as_stmt(args[0]);
534 if (handle->finalized) {
535 return SIGIL_TRUE; /* Already finalized */
536 }
538 int rc = sqlite3_finalize(handle->stmt);
539 handle->stmt = NULL;
540 handle->finalized = 1;
542 return sigil_bool(rc == SQLITE_OK);
545/*
546 * sqlite-last-insert-rowid db -> integer
547 * Get the rowid of the last inserted row.
548 */
549static Value native_sqlite_last_insert_rowid(SigilVM *vm, int argc, Value *args)
551 (void)argc;
553 if (!is_db(args[0])) {
554 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-last-insert-rowid: expected database handle");
555 return SIGIL_UNDEFINED;
556 }
558 SqliteDB *handle = as_db(args[0]);
559 if (handle->closed) {
560 return sigil_fixnum(0);
561 }
563 return sigil_fixnum(sqlite3_last_insert_rowid(handle->db));
566/*
567 * sqlite-changes db -> integer
568 * Get the number of rows changed by the last statement.
569 */
570static Value native_sqlite_changes(SigilVM *vm, int argc, Value *args)
572 (void)argc;
574 if (!is_db(args[0])) {
575 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-changes: expected database handle");
576 return SIGIL_UNDEFINED;
577 }
579 SqliteDB *handle = as_db(args[0]);
580 if (handle->closed) {
581 return sigil_fixnum(0);
582 }
584 return sigil_fixnum(sqlite3_changes(handle->db));
587/*
588 * sqlite-errmsg db -> string
589 * Get the error message for the most recent error.
590 */
591static Value native_sqlite_errmsg(SigilVM *vm, int argc, Value *args)
593 (void)argc;
595 if (!is_db(args[0])) {
596 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-errmsg: expected database handle");
597 return SIGIL_UNDEFINED;
598 }
600 SqliteDB *handle = as_db(args[0]);
601 if (handle->closed) {
602 return sigil_make_string(vm, "database is closed", 18);
603 }
605 const char *msg = sqlite3_errmsg(handle->db);
606 return sigil_make_string(vm, msg, strlen(msg));
609/*
610 * %sqlite-try-acquire! db -> boolean
611 * %sqlite-release! db -> unspecified
612 *
613 * A cooperative, per-connection mutual-exclusion flag. NOT a thread lock: the
614 * runtime is single-threaded, so a plain int cannot be torn. Its whole job is
615 * to stop two FIBERS interleaving inside one connection.
616 *
617 * Why a connection needs one at all: SQLite ends a connection's implicit read
618 * transaction only when no statement on it is active. If fiber A has a
619 * statement open (parked in the busy-retry, or simply mid-result-set) and
620 * fiber B runs a query on the same connection, B's read transaction cannot be
621 * released when B finalizes -- A is still using the btree. The connection is
622 * then pinned to B's snapshot until A completes, and every later read on it
623 * misses any commit made in between. See docs/concurrency.md.
624 */
625static Value native_sqlite_try_acquire(SigilVM *vm, int argc, Value *args)
627 (void)argc;
628 if (!is_db(args[0])) {
629 sigil__vm_error(vm, SIGIL_ERR_TYPE, "%sqlite-try-acquire!: expected database handle");
630 return SIGIL_UNDEFINED;
631 }
632 SqliteDB *handle = as_db(args[0]);
633 if (handle->locked) return SIGIL_FALSE;
634 handle->locked = 1;
635 return SIGIL_TRUE;
638static Value native_sqlite_release(SigilVM *vm, int argc, Value *args)
640 (void)argc;
641 if (!is_db(args[0])) {
642 sigil__vm_error(vm, SIGIL_ERR_TYPE, "%sqlite-release!: expected database handle");
643 return SIGIL_UNDEFINED;
644 }
645 as_db(args[0])->locked = 0;
646 return SIGIL_TRUE;
649/*
650 * sqlite-connection-locked? db -> boolean
651 * Diagnostic only: is a fiber currently holding this connection?
652 */
653static Value native_sqlite_connection_locked(SigilVM *vm, int argc, Value *args)
655 (void)argc;
656 if (!is_db(args[0])) {
657 sigil__vm_error(vm, SIGIL_ERR_TYPE, "sqlite-connection-locked?: expected database handle");
658 return SIGIL_UNDEFINED;
659 }
660 return sigil_bool(as_db(args[0])->locked != 0);
663/*
664 * Helper macro for module-scoped registration with export
665 */
666#define REGISTER_AND_EXPORT(name, func, arity, doc) \
667 sigil_module_register_native(vm, name, func, arity, doc); \
668 sigil_module_export(vm, name)
670/*
671 * Initialize the (sigil sqlite) module.
672 */
673void sigil__init_sigil_sqlite_module(SigilVM *vm)
675 /* Initialize type tags */
676 db_type_tag = sigil_intern_symbol(vm, "sqlite-db", 9);
677 stmt_type_tag = sigil_intern_symbol(vm, "sqlite-stmt", 11);
679 SigilModule *module = sigil_begin_module(vm, "(sigil sqlite)");
680 if (!module) return;
682 /* Type predicates */
683 REGISTER_AND_EXPORT("sqlite-db?", native_sqlite_dbp,
684 SIGIL_ARITY_EXACT(1), "Check if value is a database handle");
685 REGISTER_AND_EXPORT("sqlite-stmt?", native_sqlite_stmtp,
686 SIGIL_ARITY_EXACT(1), "Check if value is a statement handle");
688 /* Database operations */
689 REGISTER_AND_EXPORT("sqlite-open", native_sqlite_open,
690 SIGIL_ARITY_EXACT(1), "Open database file");
691 REGISTER_AND_EXPORT("sqlite-close", native_sqlite_close,
692 SIGIL_ARITY_EXACT(1), "Close database connection");
693 /* Internal: returns 'busy on a contended lock (never blocks). The public
694 * sqlite-exec (Scheme, in sqlite.sgl) wraps this with cooperative retry. */
695 REGISTER_AND_EXPORT("%sqlite-exec-native", native_sqlite_exec,
696 SIGIL_ARITY_EXACT(2), "Execute SQL without results (non-blocking)");
698 /* Prepared statements */
699 REGISTER_AND_EXPORT("sqlite-prepare", native_sqlite_prepare,
700 SIGIL_ARITY_EXACT(2), "Prepare SQL statement");
701 REGISTER_AND_EXPORT("sqlite-bind", native_sqlite_bind,
702 SIGIL_ARITY_EXACT(3), "Bind parameter value");
703 /* Internal: returns 'busy on a contended lock (never blocks). The public
704 * sqlite-step (Scheme, in sqlite.sgl) wraps this with cooperative retry. */
705 REGISTER_AND_EXPORT("%sqlite-step-native", native_sqlite_step,
706 SIGIL_ARITY_EXACT(1), "Execute statement step (non-blocking)");
707 REGISTER_AND_EXPORT("sqlite-reset", native_sqlite_reset,
708 SIGIL_ARITY_EXACT(1), "Reset statement to initial state");
709 REGISTER_AND_EXPORT("sqlite-finalize", native_sqlite_finalize,
710 SIGIL_ARITY_EXACT(1), "Finalize and free statement");
712 /* Column access */
713 REGISTER_AND_EXPORT("sqlite-column-count", native_sqlite_column_count,
714 SIGIL_ARITY_EXACT(1), "Get number of result columns");
715 REGISTER_AND_EXPORT("sqlite-column-name", native_sqlite_column_name,
716 SIGIL_ARITY_EXACT(2), "Get column name");
717 REGISTER_AND_EXPORT("sqlite-column", native_sqlite_column,
718 SIGIL_ARITY_EXACT(2), "Get column value");
720 /* Database info */
721 REGISTER_AND_EXPORT("sqlite-last-insert-rowid", native_sqlite_last_insert_rowid,
722 SIGIL_ARITY_EXACT(1), "Get last inserted rowid");
723 REGISTER_AND_EXPORT("sqlite-changes", native_sqlite_changes,
724 SIGIL_ARITY_EXACT(1), "Get rows changed by last statement");
725 REGISTER_AND_EXPORT("sqlite-errmsg", native_sqlite_errmsg,
726 SIGIL_ARITY_EXACT(1), "Get last error message");
728 /* Cooperative per-connection lock (see docs/concurrency.md). */
729 REGISTER_AND_EXPORT("%sqlite-try-acquire!", native_sqlite_try_acquire,
730 SIGIL_ARITY_EXACT(1), "Try to acquire the connection");
731 REGISTER_AND_EXPORT("%sqlite-release!", native_sqlite_release,
732 SIGIL_ARITY_EXACT(1), "Release the connection");
733 REGISTER_AND_EXPORT("sqlite-connection-locked?", native_sqlite_connection_locked,
734 SIGIL_ARITY_EXACT(1), "Is a fiber holding this connection?");
736 sigil_end_module(vm);
739#undef REGISTER_AND_EXPORT