From a5d6e71cc0725146db589a0f7cb30b87401e3178 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 11 Mar 2026 04:11:35 -0400 Subject: [PATCH 1/6] chore: split up symbols into their own file --- src/ffi.ts | 585 +------------------------------------------------ src/symbols.ts | 583 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 584 insertions(+), 584 deletions(-) create mode 100644 src/symbols.ts diff --git a/src/ffi.ts b/src/ffi.ts index 80d8ca8..bd1e0c5 100644 --- a/src/ffi.ts +++ b/src/ffi.ts @@ -1,589 +1,6 @@ import meta from "../deno.json" with { type: "json" }; import { dlopen } from "../deps.ts"; - -const symbols = { - sqlite3_open_v2: { - parameters: [ - "buffer", // const char *filename - "buffer", // sqlite3 **ppDb - "i32", // int flags - "pointer", // const char *zVfs - ], - result: "i32", - }, - - sqlite3_close_v2: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_changes: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_total_changes: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_last_insert_rowid: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_get_autocommit: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_prepare_v2: { - parameters: [ - "pointer", // sqlite3 *db - "buffer", // const char *zSql - "i32", // int nByte - "buffer", // sqlite3_stmt **ppStmt - "pointer", // const char **pzTail - ], - result: "i32", - }, - - sqlite3_reset: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_clear_bindings: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_step: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_column_count: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_column_type: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "i32", - }, - - sqlite3_column_text: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "pointer", - }, - sqlite3_column_value: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "pointer", - }, - - sqlite3_finalize: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_exec: { - parameters: [ - "pointer", // sqlite3 *db - "buffer", // const char *sql - "pointer", // sqlite3_callback callback - "pointer", // void *arg - "buffer", // char **errmsg - ], - result: "i32", - }, - - sqlite3_free: { - parameters: [ - "pointer", // void *p - ], - result: "void", - }, - - sqlite3_column_int: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "i32", - }, - - sqlite3_column_double: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "f64", - }, - - sqlite3_column_blob: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "pointer", - }, - - sqlite3_column_bytes: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "i32", - }, - - sqlite3_column_name: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "pointer", - }, - - sqlite3_column_decltype: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "u64", - }, - - sqlite3_bind_parameter_index: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "buffer", // const char *zName - ], - result: "i32", - }, - - sqlite3_bind_text: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - "buffer", // const char *zData - "i32", // int nData - "pointer", // void (*xDel)(void*) - ], - result: "i32", - }, - - sqlite3_bind_blob: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - "buffer", // const void *zData - "i32", // int nData - "pointer", // void (*xDel)(void*) - ], - result: "i32", - }, - - sqlite3_bind_double: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - "f64", // double rValue - ], - result: "i32", - }, - - sqlite3_bind_int: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - "i32", // int iValue - ], - result: "i32", - }, - - sqlite3_bind_int64: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - "i64", // i64 iValue - ], - result: "i32", - }, - - sqlite3_bind_null: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "i32", - }, - - sqlite3_expanded_sql: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "pointer", - }, - - sqlite3_bind_parameter_count: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_complete: { - parameters: [ - "buffer", // const char *sql - ], - result: "i32", - }, - - sqlite3_sourceid: { - parameters: [], - result: "pointer", - }, - - sqlite3_libversion: { - parameters: [], - result: "pointer", - }, - - sqlite3_blob_open: { - parameters: [ - "pointer", /* sqlite3 *db */ - "buffer", /* const char *zDb */ - "buffer", /* const char *zTable */ - "buffer", /* const char *zColumn */ - "i64", /* sqlite3_int64 iRow */ - "i32", /* int flags */ - "buffer", /* sqlite3_blob **ppBlob */ - ], - result: "i32", - }, - - sqlite3_blob_read: { - parameters: [ - "pointer", /* sqlite3_blob *blob */ - "buffer", /* void *Z */ - "i32", /* int N */ - "i32", /* int iOffset */ - ], - result: "i32", - }, - - sqlite3_blob_write: { - parameters: [ - "pointer", /* sqlite3_blob *blob */ - "buffer", /* const void *z */ - "i32", /* int n */ - "i32", /* int iOffset */ - ], - result: "i32", - }, - - sqlite3_blob_bytes: { - parameters: ["pointer" /* sqlite3_blob *blob */], - result: "i32", - }, - - sqlite3_blob_close: { - parameters: ["pointer" /* sqlite3_blob *blob */], - result: "i32", - }, - - sqlite3_sql: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "pointer", - }, - - sqlite3_stmt_readonly: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - ], - result: "i32", - }, - - sqlite3_bind_parameter_name: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "pointer", - }, - - sqlite3_errcode: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "i32", - }, - - sqlite3_errmsg: { - parameters: [ - "pointer", // sqlite3 *db - ], - result: "pointer", - }, - - sqlite3_errstr: { - parameters: [ - "i32", // int rc - ], - result: "pointer", - }, - - sqlite3_column_int64: { - parameters: [ - "pointer", // sqlite3_stmt *pStmt - "i32", // int iCol - ], - result: "i64", - }, - - sqlite3_backup_init: { - parameters: [ - "pointer", // sqlite3 *pDest - "buffer", // const char *zDestName - "pointer", // sqlite3 *pSource - "buffer", // const char *zSourceName - ], - result: "pointer", - }, - - sqlite3_backup_step: { - parameters: [ - "pointer", // sqlite3_backup *p - "i32", // int nPage - ], - result: "i32", - }, - - sqlite3_backup_finish: { - parameters: [ - "pointer", // sqlite3_backup *p - ], - result: "i32", - }, - - sqlite3_backup_remaining: { - parameters: [ - "pointer", // sqlite3_backup *p - ], - result: "i32", - }, - - sqlite3_backup_pagecount: { - parameters: [ - "pointer", // sqlite3_backup *p - ], - result: "i32", - }, - - sqlite3_create_function: { - parameters: [ - "pointer", // sqlite3 *db - "buffer", // const char *zFunctionName - "i32", // int nArg - "i32", // int eTextRep - "pointer", // void *pApp - "pointer", // void (*xFunc)(sqlite3_context*,int,sqlite3_value**) - "pointer", // void (*xStep)(sqlite3_context*,int,sqlite3_value**) - "pointer", // void (*xFinal)(sqlite3_context*) - ], - result: "i32", - optional: true, - }, - - sqlite3_result_blob: { - parameters: [ - "pointer", // sqlite3_context *p - "buffer", // const void *z - "i32", // int n - "isize", // void (*xDel)(void*) - ], - result: "void", - }, - - sqlite3_result_double: { - parameters: [ - "pointer", // sqlite3_context *p - "f64", // double rVal - ], - result: "void", - }, - - sqlite3_result_error: { - parameters: [ - "pointer", // sqlite3_context *p - "buffer", // const char *z - "i32", // int n - ], - result: "void", - }, - - sqlite3_result_int: { - parameters: [ - "pointer", // sqlite3_context *p - "i32", // int iVal - ], - result: "void", - }, - - sqlite3_result_int64: { - parameters: [ - "pointer", // sqlite3_context *p - "i64", // sqlite3_int64 iVal - ], - result: "void", - }, - - sqlite3_result_null: { - parameters: [ - "pointer", // sqlite3_context *p - ], - result: "void", - }, - - sqlite3_result_text: { - parameters: [ - "pointer", // sqlite3_context *p - "buffer", // const char *z - "i32", // int n - "isize", // void (*xDel)(void*) - ], - result: "void", - }, - - sqlite3_value_type: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "i32", - }, - sqlite3_value_subtype: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "i32", - }, - - sqlite3_value_blob: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "pointer", - }, - - sqlite3_value_double: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "f64", - }, - - sqlite3_value_int: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "i32", - }, - - sqlite3_value_int64: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "i64", - }, - - sqlite3_value_text: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "pointer", - }, - - sqlite3_value_bytes: { - parameters: [ - "pointer", // sqlite3_value *pVal - ], - result: "i32", - }, - - sqlite3_aggregate_context: { - parameters: [ - "pointer", // sqlite3_context *p - "i32", // int nBytes - ], - result: "pointer", - optional: true, - }, - - sqlite3_enable_load_extension: { - parameters: [ - "pointer", // sqlite3 *db - "i32", // int onoff - ], - result: "i32", - optional: true, - }, - - sqlite3_load_extension: { - parameters: [ - "pointer", // sqlite3 *db - "buffer", // const char *zFile - "buffer", // const char *zProc - "buffer", // const char **pzErrMsg - ], - result: "i32", - optional: true, - }, - - sqlite3_initialize: { - parameters: [], - result: "i32", - }, - - sqlite3_update_hook: { - parameters: [ - "pointer", // sqlite3 *db - "pointer", // void *pArg - "pointer", // void (*xUpdate)(sqlite3_context*,int,sqlite3_value**,sqlite3_value**) - ], - result: "pointer", - }, -} as const satisfies Deno.ForeignLibraryInterface; +import { symbols } from "./symbols.ts"; let lib: Deno.DynamicLibrary["symbols"]; diff --git a/src/symbols.ts b/src/symbols.ts new file mode 100644 index 0000000..0ece419 --- /dev/null +++ b/src/symbols.ts @@ -0,0 +1,583 @@ +export const symbols = { + sqlite3_open_v2: { + parameters: [ + "buffer", // const char *filename + "buffer", // sqlite3 **ppDb + "i32", // int flags + "pointer", // const char *zVfs + ], + result: "i32", + }, + + sqlite3_close_v2: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_changes: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_total_changes: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_last_insert_rowid: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_get_autocommit: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_prepare_v2: { + parameters: [ + "pointer", // sqlite3 *db + "buffer", // const char *zSql + "i32", // int nByte + "buffer", // sqlite3_stmt **ppStmt + "pointer", // const char **pzTail + ], + result: "i32", + }, + + sqlite3_reset: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_clear_bindings: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_step: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_column_count: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_column_type: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "i32", + }, + + sqlite3_column_text: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "pointer", + }, + sqlite3_column_value: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "pointer", + }, + + sqlite3_finalize: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_exec: { + parameters: [ + "pointer", // sqlite3 *db + "buffer", // const char *sql + "pointer", // sqlite3_callback callback + "pointer", // void *arg + "buffer", // char **errmsg + ], + result: "i32", + }, + + sqlite3_free: { + parameters: [ + "pointer", // void *p + ], + result: "void", + }, + + sqlite3_column_int: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "i32", + }, + + sqlite3_column_double: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "f64", + }, + + sqlite3_column_blob: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "pointer", + }, + + sqlite3_column_bytes: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "i32", + }, + + sqlite3_column_name: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "pointer", + }, + + sqlite3_column_decltype: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "u64", + }, + + sqlite3_bind_parameter_index: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "buffer", // const char *zName + ], + result: "i32", + }, + + sqlite3_bind_text: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + "buffer", // const char *zData + "i32", // int nData + "pointer", // void (*xDel)(void*) + ], + result: "i32", + }, + + sqlite3_bind_blob: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + "buffer", // const void *zData + "i32", // int nData + "pointer", // void (*xDel)(void*) + ], + result: "i32", + }, + + sqlite3_bind_double: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + "f64", // double rValue + ], + result: "i32", + }, + + sqlite3_bind_int: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + "i32", // int iValue + ], + result: "i32", + }, + + sqlite3_bind_int64: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + "i64", // i64 iValue + ], + result: "i32", + }, + + sqlite3_bind_null: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "i32", + }, + + sqlite3_expanded_sql: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "pointer", + }, + + sqlite3_bind_parameter_count: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_complete: { + parameters: [ + "buffer", // const char *sql + ], + result: "i32", + }, + + sqlite3_sourceid: { + parameters: [], + result: "pointer", + }, + + sqlite3_libversion: { + parameters: [], + result: "pointer", + }, + + sqlite3_blob_open: { + parameters: [ + "pointer", /* sqlite3 *db */ + "buffer", /* const char *zDb */ + "buffer", /* const char *zTable */ + "buffer", /* const char *zColumn */ + "i64", /* sqlite3_int64 iRow */ + "i32", /* int flags */ + "buffer", /* sqlite3_blob **ppBlob */ + ], + result: "i32", + }, + + sqlite3_blob_read: { + parameters: [ + "pointer", /* sqlite3_blob *blob */ + "buffer", /* void *Z */ + "i32", /* int N */ + "i32", /* int iOffset */ + ], + result: "i32", + }, + + sqlite3_blob_write: { + parameters: [ + "pointer", /* sqlite3_blob *blob */ + "buffer", /* const void *z */ + "i32", /* int n */ + "i32", /* int iOffset */ + ], + result: "i32", + }, + + sqlite3_blob_bytes: { + parameters: ["pointer" /* sqlite3_blob *blob */], + result: "i32", + }, + + sqlite3_blob_close: { + parameters: ["pointer" /* sqlite3_blob *blob */], + result: "i32", + }, + + sqlite3_sql: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "pointer", + }, + + sqlite3_stmt_readonly: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + ], + result: "i32", + }, + + sqlite3_bind_parameter_name: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "pointer", + }, + + sqlite3_errcode: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "i32", + }, + + sqlite3_errmsg: { + parameters: [ + "pointer", // sqlite3 *db + ], + result: "pointer", + }, + + sqlite3_errstr: { + parameters: [ + "i32", // int rc + ], + result: "pointer", + }, + + sqlite3_column_int64: { + parameters: [ + "pointer", // sqlite3_stmt *pStmt + "i32", // int iCol + ], + result: "i64", + }, + + sqlite3_backup_init: { + parameters: [ + "pointer", // sqlite3 *pDest + "buffer", // const char *zDestName + "pointer", // sqlite3 *pSource + "buffer", // const char *zSourceName + ], + result: "pointer", + }, + + sqlite3_backup_step: { + parameters: [ + "pointer", // sqlite3_backup *p + "i32", // int nPage + ], + result: "i32", + }, + + sqlite3_backup_finish: { + parameters: [ + "pointer", // sqlite3_backup *p + ], + result: "i32", + }, + + sqlite3_backup_remaining: { + parameters: [ + "pointer", // sqlite3_backup *p + ], + result: "i32", + }, + + sqlite3_backup_pagecount: { + parameters: [ + "pointer", // sqlite3_backup *p + ], + result: "i32", + }, + + sqlite3_create_function: { + parameters: [ + "pointer", // sqlite3 *db + "buffer", // const char *zFunctionName + "i32", // int nArg + "i32", // int eTextRep + "pointer", // void *pApp + "pointer", // void (*xFunc)(sqlite3_context*,int,sqlite3_value**) + "pointer", // void (*xStep)(sqlite3_context*,int,sqlite3_value**) + "pointer", // void (*xFinal)(sqlite3_context*) + ], + result: "i32", + optional: true, + }, + + sqlite3_result_blob: { + parameters: [ + "pointer", // sqlite3_context *p + "buffer", // const void *z + "i32", // int n + "isize", // void (*xDel)(void*) + ], + result: "void", + }, + + sqlite3_result_double: { + parameters: [ + "pointer", // sqlite3_context *p + "f64", // double rVal + ], + result: "void", + }, + + sqlite3_result_error: { + parameters: [ + "pointer", // sqlite3_context *p + "buffer", // const char *z + "i32", // int n + ], + result: "void", + }, + + sqlite3_result_int: { + parameters: [ + "pointer", // sqlite3_context *p + "i32", // int iVal + ], + result: "void", + }, + + sqlite3_result_int64: { + parameters: [ + "pointer", // sqlite3_context *p + "i64", // sqlite3_int64 iVal + ], + result: "void", + }, + + sqlite3_result_null: { + parameters: [ + "pointer", // sqlite3_context *p + ], + result: "void", + }, + + sqlite3_result_text: { + parameters: [ + "pointer", // sqlite3_context *p + "buffer", // const char *z + "i32", // int n + "isize", // void (*xDel)(void*) + ], + result: "void", + }, + + sqlite3_value_type: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "i32", + }, + sqlite3_value_subtype: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "i32", + }, + + sqlite3_value_blob: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "pointer", + }, + + sqlite3_value_double: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "f64", + }, + + sqlite3_value_int: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "i32", + }, + + sqlite3_value_int64: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "i64", + }, + + sqlite3_value_text: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "pointer", + }, + + sqlite3_value_bytes: { + parameters: [ + "pointer", // sqlite3_value *pVal + ], + result: "i32", + }, + + sqlite3_aggregate_context: { + parameters: [ + "pointer", // sqlite3_context *p + "i32", // int nBytes + ], + result: "pointer", + optional: true, + }, + + sqlite3_enable_load_extension: { + parameters: [ + "pointer", // sqlite3 *db + "i32", // int onoff + ], + result: "i32", + optional: true, + }, + + sqlite3_load_extension: { + parameters: [ + "pointer", // sqlite3 *db + "buffer", // const char *zFile + "buffer", // const char *zProc + "buffer", // const char **pzErrMsg + ], + result: "i32", + optional: true, + }, + + sqlite3_initialize: { + parameters: [], + result: "i32", + }, + + sqlite3_update_hook: { + parameters: [ + "pointer", // sqlite3 *db + "pointer", // void *pArg + "pointer", // void (*xUpdate)(sqlite3_context*,int,sqlite3_value**,sqlite3_value**) + ], + result: "pointer", + }, +} as const satisfies Deno.ForeignLibraryInterface; From 4f1efd3d603218419756ddb9a20e5261fbb60559 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 11 Mar 2026 04:19:23 -0400 Subject: [PATCH 2/6] test: add test for string parameter handling with null values --- src/statement.ts | 5 ++++- test/test.ts | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/statement.ts b/src/statement.ts index e6ff4e3..ea95479 100644 --- a/src/statement.ts +++ b/src/statement.ts @@ -91,7 +91,10 @@ function getColumn( case SQLITE_TEXT: { const ptr = sqlite3_column_text(handle, i); if (ptr === null) return null; - const text = readCstr(ptr, 0); + const bytes = sqlite3_column_bytes(handle, i); + const text = new TextDecoder().decode( + Deno.UnsafePointerView.getArrayBuffer(ptr, bytes), + ); const value = sqlite3_column_value(handle, i); const subtype = sqlite3_value_subtype(value); if (subtype === JSON_SUBTYPE && parseJson) { diff --git a/test/test.ts b/test/test.ts index 4dc1e61..c4bf4b3 100644 --- a/test/test.ts +++ b/test/test.ts @@ -582,6 +582,23 @@ Deno.test("sqlite", async (t) => { db.exec("drop table tbl_fts"); }); + await t.step("string param with null", () => { + const db = new Database("./example.db"); + db.run( + "CREATE TABLE IF NOT EXISTS Data(key TEXT NOT NULL PRIMARY KEY, value TEXT NOT NULL)", + ); + const readStatement = db.prepare("SELECT value FROM Data WHERE key = ?"); + const writeStatement = db.prepare( + "REPLACE INTO Data(key, value) VALUES(?, ?)", + ); + + writeStatement.run("foo", "bar\x00baz"); + const [value] = readStatement.value<[string]>("foo")!; + assertEquals(value, "bar\x00baz"); + db.exec("drop table Data"); + db.close(); + }); + await t.step("drop table", () => { db.exec("drop table test"); db.exec("drop table blobs"); From c16077a0dafbd908034d935b60a492f4665ad285 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 11 Mar 2026 04:37:34 -0400 Subject: [PATCH 3/6] feat: add setUpdateHook method for row-level update observation --- doc.md | 27 +++++++++++++++++++ src/database.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ test/test.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/doc.md b/doc.md index bf14d0e..4e8f9dc 100644 --- a/doc.md +++ b/doc.md @@ -349,6 +349,33 @@ runTransaction.deferred([ ]); ``` +## Update hooks + +Use `setUpdateHook()` to observe row-level `INSERT`, `UPDATE`, and `DELETE` +operations on the current database connection. + +Pass a callback to enable the hook: + +```ts +db.setUpdateHook((type, dbName, tableName, rowId) => { + console.log({ type, dbName, tableName, rowId }); +}); +``` + +The callback receives: + +- `type: number` - SQLite update type. Insert is `18`, delete is `9`, and + update is `23`. +- `dbName: string` - Database name, usually `"main"`. +- `tableName: string` - Name of the table that changed. +- `rowId: bigint` - Row ID of the affected row. + +Pass `null` to remove the current hook: + +```ts +db.setUpdateHook(null); +``` + ## Binding Parameters Parameters can be bound both by name and positiion. To bind by name, just pass diff --git a/src/database.ts b/src/database.ts index e315373..c344b9b 100644 --- a/src/database.ts +++ b/src/database.ts @@ -110,6 +110,7 @@ const { sqlite3_backup_step, sqlite3_backup_finish, sqlite3_errcode, + sqlite3_update_hook, } = ffi; /** SQLite version string */ @@ -126,6 +127,12 @@ export function isComplete(statement: string): boolean { return Boolean(sqlite3_complete(toCString(statement))); } +export enum SqliteUpdateType { + SQLITE_INSERT = 18, + SQLITE_DELETE = 9, + SQLITE_UPDATE = 23, +} + const BIG_MAX = BigInt(Number.MAX_SAFE_INTEGER); /** @@ -778,6 +785,68 @@ export class Database { unwrap(result, this.#handle); } + #updateHook?: Deno.UnsafeCallback<{ + readonly parameters: readonly [ + "pointer", + "i32", + "pointer", + "pointer", + "i64", + ]; + readonly result: "void"; + }>; + + /** + * Sets a callback function that is invoked whenever a row is updated, inserted or deleted. + * + * The callback function receives the type of update (insert, update, or delete), the database name, the table name, and the row ID of the row being modified. + * + * Example: + * ```ts + * db.setUpdateHook((type, dbName, tableName, rowId) => { + * console.log(`Row with ID ${rowId} in table ${tableName} was modified in database ${dbName}. Update type: ${type}`); + * }); + * ``` + */ + setUpdateHook( + hook: + | (( + type: SqliteUpdateType, + dbName: string, + tableName: string, + rowId: bigint, + ) => void) + | null, + ): void { + if (hook === null) { + sqlite3_update_hook(this.#handle, null, null); + if (this.#updateHook) { + this.#updateHook.close(); + this.#updateHook = undefined; + } + return; + } + + const updateHook = new Deno.UnsafeCallback( + { + parameters: ["pointer", "i32", "pointer", "pointer", "i64"], + result: "void", + } as const, + (_, type, pDbName, pTableName, rowId) => { + const dbName = readCstr(pDbName!); + const tableName = readCstr(pTableName!); + hook(type, dbName, tableName, rowId); + }, + ); + + sqlite3_update_hook(this.#handle, updateHook.pointer, null); + + if (this.#updateHook) { + this.#updateHook.close(); + } + this.#updateHook = updateHook; + } + /** * Closes the database connection. * @@ -794,6 +863,9 @@ export class Database { for (const cb of this.#callbacks) { cb.close(); } + if (this.#updateHook) { + this.#updateHook.close(); + } unwrap(sqlite3_close_v2(this.#handle)); this.#open = false; } diff --git a/test/test.ts b/test/test.ts index c4bf4b3..1b091cf 100644 --- a/test/test.ts +++ b/test/test.ts @@ -5,6 +5,7 @@ import { SQLITE_VERSION, SqliteError, } from "../mod.ts"; +import { SqliteUpdateType } from "../src/database.ts"; import { assert, assertEquals, assertThrows } from "./deps.ts"; console.log("sqlite version:", SQLITE_VERSION); @@ -582,6 +583,73 @@ Deno.test("sqlite", async (t) => { db.exec("drop table tbl_fts"); }); + await t.step("update hook", () => { + db.exec(` + create table hook_test ( + id integer primary key, + value text not null + ) + `); + + const events: Array<{ + type: SqliteUpdateType; + dbName: string; + tableName: string; + rowId: bigint; + }> = []; + + db.setUpdateHook((type, dbName, tableName, rowId) => { + events.push({ type, dbName, tableName, rowId }); + }); + + db.exec("insert into hook_test (id, value) values (?, ?)", 10, "before"); + db.exec("update hook_test set value = ? where id = ?", "after", 10); + db.exec("delete from hook_test where id = ?", 10); + + assertEquals(events, [ + { + type: SqliteUpdateType.SQLITE_INSERT, + dbName: "main", + tableName: "hook_test", + rowId: 10n, + }, + { + type: SqliteUpdateType.SQLITE_UPDATE, + dbName: "main", + tableName: "hook_test", + rowId: 10n, + }, + { + type: SqliteUpdateType.SQLITE_DELETE, + dbName: "main", + tableName: "hook_test", + rowId: 10n, + }, + ]); + + db.setUpdateHook(null); + db.exec("insert into hook_test (id, value) values (?, ?)", 11, "disabled"); + assertEquals(events.length, 3); + + const firstHookCalls: bigint[] = []; + const secondHookCalls: bigint[] = []; + + db.setUpdateHook((_, __, ___, rowId) => { + firstHookCalls.push(rowId); + }); + db.setUpdateHook((_, __, ___, rowId) => { + secondHookCalls.push(rowId); + }); + + db.exec("insert into hook_test (id, value) values (?, ?)", 12, "replaced"); + + assertEquals(firstHookCalls, []); + assertEquals(secondHookCalls, [12n]); + + db.setUpdateHook(null); + db.exec("drop table hook_test"); + }); + await t.step("string param with null", () => { const db = new Database("./example.db"); db.run( From 8f9335e21560bf20fa0b5adcafbf176acdd02883 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Wed, 11 Mar 2026 05:02:22 -0400 Subject: [PATCH 4/6] feat: add database export and size methods, update copyright year --- LICENSE | 2 +- README.md | 4 +-- doc.md | 67 +++++++++++++++++++++++++++++++++++-- src/constants.ts | 3 ++ src/database.ts | 57 +++++++++++++++++++++++++++++++ src/statement.ts | 16 ++++++--- src/symbols.ts | 11 ++++++ test/test.ts | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 238 insertions(+), 9 deletions(-) diff --git a/LICENSE b/LICENSE index d3de418..f4ee008 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2022 DjDeveloperr + Copyright 2026 DjDeveloperr Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index de149a9..02eff44 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![License](https://img.shields.io/github/license/denodrivers/sqlite3)](https://github.com/denodrivers/sqlite3/blob/master/LICENSE) [![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/DjDeveloperr) -The fastest and correct module for SQLite3 in Deno. +The fastest and correct SQLite3 client for Deno. ## Example @@ -92,4 +92,4 @@ DENO_SQLITE_LOCAL=1 deno task bench Apache-2.0. Check [LICENSE](./LICENSE) for details. -Copyright © 2023 DjDeveloperr +Copyright © 2026 DjDeveloperr diff --git a/doc.md b/doc.md index 4e8f9dc..dd87c05 100644 --- a/doc.md +++ b/doc.md @@ -63,6 +63,46 @@ const db = new Database("test.db", { create: false }); a dynamic library, this needs to be set to true for the method `loadExtension` to work. Defaults to `false`. +## Exporting Database Bytes + +Use `db.export()` to serialize a schema into a `Uint8Array`. + +For on-disk databases, this is equivalent to the database file bytes. For +in-memory databases, it is the byte sequence that would be written to disk. + +```ts +const db = new Database(":memory:"); +db.exec("create table test (id integer primary key, value text)"); +db.exec("insert into test (value) values (?)", "hello"); + +const bytes = db.export(); +console.log(bytes.byteLength); +``` + +You can optionally pass a schema name, such as `"main"` or an attached database +name: + +```ts +const mainBytes = db.export("main"); +``` + +## Getting Database Size + +Use `db.size()` to get the serialized size of a schema in bytes. + +```ts +const db = new Database(":memory:"); +db.exec("create table test (id integer primary key, value text)"); + +console.log(db.size()); // serialized byte size +``` + +As with `export()`, you may pass a schema name: + +```ts +const mainSize = db.size("main"); +``` + ## Loading extensions Loading SQLite3 extensions is enabled through the `enableLoadExtension` property @@ -197,6 +237,9 @@ execute the statement, and return an array of rows as objects. const rows = stmt.all(...params); ``` +`all()` loads the entire result set into memory. For large result sets, prefer +iterating the statement row by row instead. + To get rows in array form, use `values()` method. ```ts @@ -305,11 +348,31 @@ memory at once. Since it does not accept any parameters, you must bind the parameters before iterating using `bind` method. ```ts +const stmt = db.prepare("SELECT * FROM logs WHERE created_at >= ?"); +stmt.bind("2026-01-01"); + for (const row of stmt) { console.log(row); } ``` +This processes rows lazily, one row at a time, without building a large array +first. + +You can also call `iter(...params)` directly when you want to provide parameters +for that specific iteration. + +```ts +const stmt = db.prepare("SELECT * FROM logs WHERE created_at >= ?"); + +for (const row of stmt.iter("2026-01-01")) { + console.log(row); +} +``` + +Use `all()` when you explicitly want an in-memory array. Use `for...of` or +`iter()` when you want to stream through a large dataset. + ## Transactions To start a transaction, use the `transaction()` method. This method takes a @@ -364,8 +427,8 @@ db.setUpdateHook((type, dbName, tableName, rowId) => { The callback receives: -- `type: number` - SQLite update type. Insert is `18`, delete is `9`, and - update is `23`. +- `type: number` - SQLite update type. Insert is `18`, delete is `9`, and update + is `23`. - `dbName: string` - Database name, usually `"main"`. - `tableName: string` - Name of the table that changed. - `rowId: bigint` - Row ID of the affected row. diff --git a/src/constants.ts b/src/constants.ts index aa67db7..26d16e7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -59,6 +59,9 @@ export const SQLITE3_PREPARE_PERSISTENT = 0x00000001; export const SQLITE3_PREPARE_NORMALIZE = 0x00000002; export const SQLITE3_PREPARE_NO_VTAB = 0x00000004; +// Serialize Flags +export const SQLITE_SERIALIZE_NOCOPY = 0x001; + // Fundamental Datatypes export const SQLITE_INTEGER = 1; export const SQLITE_FLOAT = 2; diff --git a/src/database.ts b/src/database.ts index c344b9b..16730fc 100644 --- a/src/database.ts +++ b/src/database.ts @@ -9,6 +9,7 @@ import { SQLITE_FLOAT, SQLITE_INTEGER, SQLITE_NULL, + SQLITE_SERIALIZE_NOCOPY, SQLITE_TEXT, } from "./constants.ts"; import { readCstr, toCString, unwrap } from "./util.ts"; @@ -87,6 +88,7 @@ const { sqlite3_free, sqlite3_libversion, sqlite3_sourceid, + sqlite3_serialize, sqlite3_complete, sqlite3_finalize, sqlite3_result_blob, @@ -890,6 +892,61 @@ export class Database { } } + #serialize(name: string, flags: number): [Deno.PointerValue, number] { + if (sqlite3_serialize === null) { + throw new Error( + "Database serialization is not supported by the shared library that was used.", + ); + } + + const size = new BigInt64Array(1); + const ptr = sqlite3_serialize(this.#handle, toCString(name), size, flags); + const bytes = size[0]; + + if (bytes < 0) { + throw new Error("Failed to serialize database"); + } + + if (bytes > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new RangeError("Database is too large to represent in JavaScript"); + } + + return [ptr, Number(bytes)]; + } + + /** + * Export a database schema as serialized bytes. + * + * For on-disk databases this is equivalent to the database file contents. + * For in-memory databases this is the same byte sequence that would be + * written if the database were backed up to disk. + * + * @param name Schema name to export. Defaults to "main". + */ + export(name = "main"): Uint8Array { + const [ptr, size] = this.#serialize(name, 0); + if (ptr === null) { + throw new Error("Failed to serialize database"); + } + + try { + return new Uint8Array( + Deno.UnsafePointerView.getArrayBuffer(ptr, size).slice(0), + ); + } finally { + sqlite3_free(ptr); + } + } + + /** + * Get the serialized size of a database schema in bytes. + * + * @param name Schema name to measure. Defaults to "main". + */ + size(name = "main"): number { + return this.#serialize(name, SQLITE_SERIALIZE_NOCOPY)[1]; + } + [Symbol.for("Deno.customInspect")](): string { return `SQLite3.Database { path: ${this.path} }`; } diff --git a/src/statement.ts b/src/statement.ts index ea95479..facb5a8 100644 --- a/src/statement.ts +++ b/src/statement.ts @@ -442,7 +442,9 @@ export class Statement> { #runWithArgs(...params: RestBindParameters): number { const handle = this.#handle; this.#begin(); - this.#bindAll(params); + if (params.length) { + this.#bindAll(params); + } const status = sqlite3_step(handle); if (!this.#hasNoArgs && !this.#bound && params.length) { this.#bindRefs.clear(); @@ -495,7 +497,9 @@ export class Statement> { ): T[] { const handle = this.#handle; this.#begin(); - this.#bindAll(params); + if (params.length) { + this.#bindAll(params); + } const columnCount = sqlite3_column_count(handle); const result: T[] = []; const getRowArray = new Function( @@ -588,7 +592,9 @@ export class Statement> { const int64 = this.int64 ?? this.db.int64; const parseJson = this.parseJson ?? this.db.parseJson; this.#begin(); - this.#bindAll(params); + if (params.length) { + this.#bindAll(params); + } const getRowObject = this.getRowObject(); const result: T[] = []; let status = sqlite3_step(handle); @@ -758,7 +764,9 @@ export class Statement> { /** Iterate over resultant rows from query. */ *iter(...params: RestBindParameters): IterableIterator { this.#begin(); - this.#bindAll(params); + if (params.length) { + this.#bindAll(params); + } const getRowObject = this.getRowObject(); const int64 = this.int64 ?? this.db.int64; const parseJson = this.parseJson ?? this.db.parseJson; diff --git a/src/symbols.ts b/src/symbols.ts index 0ece419..758fc46 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -131,6 +131,17 @@ export const symbols = { result: "void", }, + sqlite3_serialize: { + parameters: [ + "pointer", // sqlite3 *db + "buffer", // const char *zSchema + "buffer", // sqlite3_int64 *piSize + "u32", // unsigned int mFlags + ], + result: "pointer", + optional: true, + }, + sqlite3_column_int: { parameters: [ "pointer", // sqlite3_stmt *pStmt diff --git a/test/test.ts b/test/test.ts index 1b091cf..73172eb 100644 --- a/test/test.ts +++ b/test/test.ts @@ -60,6 +60,62 @@ Deno.test("sqlite", async (t) => { assertEquals(db.exec("pragma temp_store = memory"), 0); }); + await t.step("export and size (in-memory)", async () => { + const memoryDb = new Database(":memory:"); + const exportPath = await Deno.makeTempFile({ suffix: ".db" }); + + try { + memoryDb.exec( + "create table export_test (id integer primary key, value text)", + ); + memoryDb.exec( + "insert into export_test (value) values (?)", + "hello export", + ); + + const exported = memoryDb.export(); + assertEquals(memoryDb.size(), exported.byteLength); + + await Deno.writeFile(exportPath, exported); + + const restored = new Database(exportPath); + try { + const row = restored.prepare( + "select id, value from export_test", + ).get<{ id: number; value: string }>()!; + assertEquals(row, { id: 1, value: "hello export" }); + } finally { + restored.close(); + } + } finally { + memoryDb.close(); + await Deno.remove(exportPath).catch(() => {}); + } + }); + + await t.step("export and size (file-backed)", async () => { + const filePath = await Deno.makeTempFile({ suffix: ".db" }); + const fileDb = new Database(filePath); + + try { + fileDb.exec( + "create table export_test (id integer primary key, value text)", + ); + fileDb.exec("insert into export_test (value) values (?)", "hello file"); + + const exported = fileDb.export(); + assertEquals(fileDb.size(), exported.byteLength); + + fileDb.close(); + + const fileBytes = await Deno.readFile(filePath); + assertEquals(exported, fileBytes); + } finally { + if (fileDb.open) fileDb.close(); + await Deno.remove(filePath).catch(() => {}); + } + }); + await t.step("select version (row as array)", () => { const [version] = db.prepare("select sqlite_version()").value<[string]>()!; assertEquals(version, SQLITE_VERSION); @@ -210,6 +266,37 @@ Deno.test("sqlite", async (t) => { } }); + await t.step("bound statement works across execution helpers", () => { + interface Row { + integer: number; + text: string; + } + + const stmt = db.prepare( + "select integer, text from test where integer > ? order by integer", + ); + + stmt.bind(7); + + assertEquals( + stmt.all(), + [ + { integer: 8, text: "hello 8" }, + { integer: 9, text: "hello 9" }, + ], + ); + + assertEquals( + Array.from(stmt as Iterable), + [ + { integer: 8, text: "hello 8" }, + { integer: 9, text: "hello 9" }, + ], + ); + + stmt.finalize(); + }); + await t.step("query json", () => { const row = db .prepare( From 07f5a24bc1811cab7ca766a2dd47121a92328477 Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Wed, 11 Mar 2026 05:29:34 -0400 Subject: [PATCH 5/6] fix: use i64 return type for last insert rowid Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/symbols.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symbols.ts b/src/symbols.ts index 758fc46..4807f7c 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -34,7 +34,7 @@ export const symbols = { parameters: [ "pointer", // sqlite3 *db ], - result: "i32", + result: "i64", }, sqlite3_get_autocommit: { From 87c326fab53cf53ad9795c06d3813e73fdf480a7 Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Wed, 11 Mar 2026 05:30:27 -0400 Subject: [PATCH 6/6] fix: use temp file for test than polluting workspace Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/test.ts | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/test/test.ts b/test/test.ts index 73172eb..00ae6fe 100644 --- a/test/test.ts +++ b/test/test.ts @@ -737,21 +737,28 @@ Deno.test("sqlite", async (t) => { db.exec("drop table hook_test"); }); - await t.step("string param with null", () => { - const db = new Database("./example.db"); - db.run( - "CREATE TABLE IF NOT EXISTS Data(key TEXT NOT NULL PRIMARY KEY, value TEXT NOT NULL)", - ); - const readStatement = db.prepare("SELECT value FROM Data WHERE key = ?"); - const writeStatement = db.prepare( - "REPLACE INTO Data(key, value) VALUES(?, ?)", - ); + await t.step("string param with null", async () => { + const dbPath = await Deno.makeTempFile({ suffix: ".db" }); + const db = new Database(dbPath); + try { + db.run( + "CREATE TABLE IF NOT EXISTS Data(key TEXT NOT NULL PRIMARY KEY, value TEXT NOT NULL)", + ); + const readStatement = db.prepare("SELECT value FROM Data WHERE key = ?"); + const writeStatement = db.prepare( + "REPLACE INTO Data(key, value) VALUES(?, ?)", + ); - writeStatement.run("foo", "bar\x00baz"); - const [value] = readStatement.value<[string]>("foo")!; - assertEquals(value, "bar\x00baz"); - db.exec("drop table Data"); - db.close(); + writeStatement.run("foo", "bar\x00baz"); + const [value] = readStatement.value<[string]>("foo")!; + assertEquals(value, "bar\x00baz"); + db.exec("drop table Data"); + } finally { + db.close(); + try { + await Deno.remove(dbPath); + } catch (_) { /* ignore */ } + } }); await t.step("drop table", () => {