@photostructure/sqlite 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -16
- package/README.md +5 -10
- package/binding.gyp +2 -2
- package/dist/index.cjs +314 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +346 -89
- package/dist/index.d.mts +346 -89
- package/dist/index.d.ts +346 -89
- package/dist/index.mjs +311 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +72 -63
- package/prebuilds/darwin-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/test_extension.so +0 -0
- package/prebuilds/win32-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+sqlite.glibc.node +0 -0
- package/src/aggregate_function.cpp +222 -114
- package/src/aggregate_function.h +5 -6
- package/src/binding.cpp +30 -21
- package/src/enhance.ts +552 -0
- package/src/index.ts +84 -9
- package/src/shims/node_errors.h +34 -15
- package/src/shims/sqlite_errors.h +34 -8
- package/src/sql-tag-store.ts +6 -9
- package/src/sqlite_impl.cpp +1044 -394
- package/src/sqlite_impl.h +46 -7
- package/src/transaction.ts +178 -0
- package/src/types/database-sync-instance.ts +6 -40
- package/src/types/pragma-options.ts +23 -0
- package/src/types/statement-sync-instance.ts +38 -12
- package/src/types/transaction.ts +72 -0
- package/src/upstream/node_sqlite.cc +143 -43
- package/src/upstream/node_sqlite.h +15 -11
- package/src/upstream/sqlite3.c +102 -58
- package/src/upstream/sqlite3.h +5 -5
- package/src/user_function.cpp +138 -141
- package/src/user_function.h +3 -0
package/src/sqlite_impl.h
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include <stdexcept>
|
|
15
15
|
#include <string>
|
|
16
16
|
#include <thread>
|
|
17
|
+
#include <unordered_map>
|
|
17
18
|
|
|
18
19
|
// Include our shims
|
|
19
20
|
#include "shims/base_object.h"
|
|
@@ -31,6 +32,7 @@ class DatabaseSync;
|
|
|
31
32
|
class StatementSync;
|
|
32
33
|
class StatementSyncIterator;
|
|
33
34
|
class Session;
|
|
35
|
+
class BackupJob;
|
|
34
36
|
|
|
35
37
|
// Per-worker instance data
|
|
36
38
|
struct AddonData {
|
|
@@ -43,8 +45,19 @@ struct AddonData {
|
|
|
43
45
|
Napi::FunctionReference statementSyncConstructor;
|
|
44
46
|
Napi::FunctionReference statementSyncIteratorConstructor;
|
|
45
47
|
Napi::FunctionReference sessionConstructor;
|
|
48
|
+
|
|
49
|
+
// Cached Object.create function for creating objects with null prototype
|
|
50
|
+
Napi::FunctionReference objectCreateFn;
|
|
51
|
+
|
|
52
|
+
// ValueStorage data
|
|
53
|
+
std::mutex value_storage_mutex;
|
|
54
|
+
std::unordered_map<int32_t, Napi::Reference<Napi::Value>> value_storage;
|
|
55
|
+
std::atomic<int32_t> next_value_id{0};
|
|
46
56
|
};
|
|
47
57
|
|
|
58
|
+
// Helper to create an object with null prototype (matches Node.js behavior)
|
|
59
|
+
Napi::Object CreateObjectWithNullPrototype(Napi::Env env);
|
|
60
|
+
|
|
48
61
|
// Worker thread support functions
|
|
49
62
|
void RegisterDatabaseInstance(Napi::Env env, DatabaseSync *database);
|
|
50
63
|
void UnregisterDatabaseInstance(Napi::Env env, DatabaseSync *database);
|
|
@@ -103,6 +116,9 @@ public:
|
|
|
103
116
|
bool get_enable_defensive() const { return defensive_; }
|
|
104
117
|
void set_enable_defensive(bool flag) { defensive_ = flag; }
|
|
105
118
|
|
|
119
|
+
bool get_open_uri() const { return open_uri_; }
|
|
120
|
+
void set_open_uri(bool flag) { open_uri_ = flag; }
|
|
121
|
+
|
|
106
122
|
private:
|
|
107
123
|
std::string location_;
|
|
108
124
|
bool read_only_ = false;
|
|
@@ -113,7 +129,8 @@ private:
|
|
|
113
129
|
bool return_arrays_ = false;
|
|
114
130
|
bool allow_bare_named_params_ = true;
|
|
115
131
|
bool allow_unknown_named_params_ = false;
|
|
116
|
-
bool defensive_ =
|
|
132
|
+
bool defensive_ = true; // Node.js v25+ defaults to true
|
|
133
|
+
bool open_uri_ = false;
|
|
117
134
|
};
|
|
118
135
|
|
|
119
136
|
// Main database class
|
|
@@ -175,6 +192,12 @@ public:
|
|
|
175
192
|
void RemoveSession(Session *session);
|
|
176
193
|
void DeleteAllSessions();
|
|
177
194
|
|
|
195
|
+
// Backup management - prevents use-after-free when database is closed
|
|
196
|
+
// during an active backup operation
|
|
197
|
+
void AddBackup(BackupJob *backup);
|
|
198
|
+
void RemoveBackup(BackupJob *backup);
|
|
199
|
+
void FinalizeBackups();
|
|
200
|
+
|
|
178
201
|
// Error handling for user functions
|
|
179
202
|
void SetIgnoreNextSQLiteError(bool ignore) {
|
|
180
203
|
ignore_next_sqlite_error_ = ignore;
|
|
@@ -192,7 +215,7 @@ public:
|
|
|
192
215
|
return deferred_authorizer_exception_.has_value();
|
|
193
216
|
}
|
|
194
217
|
const std::string &GetDeferredAuthorizerException() const {
|
|
195
|
-
return deferred_authorizer_exception_
|
|
218
|
+
return *deferred_authorizer_exception_;
|
|
196
219
|
}
|
|
197
220
|
|
|
198
221
|
private:
|
|
@@ -207,6 +230,8 @@ private:
|
|
|
207
230
|
std::map<std::string, std::unique_ptr<StatementSync>> prepared_statements_;
|
|
208
231
|
std::set<Session *> sessions_; // Track all active sessions
|
|
209
232
|
mutable std::mutex sessions_mutex_; // Protect sessions_ for thread safety
|
|
233
|
+
std::set<BackupJob *> backups_; // Track all active backup jobs
|
|
234
|
+
mutable std::mutex backups_mutex_; // Protect backups_ for thread safety
|
|
210
235
|
std::thread::id creation_thread_;
|
|
211
236
|
napi_env env_; // Store for cleanup purposes
|
|
212
237
|
bool ignore_next_sqlite_error_ = false; // For user function error handling
|
|
@@ -218,12 +243,16 @@ private:
|
|
|
218
243
|
// Authorization callback storage
|
|
219
244
|
std::unique_ptr<Napi::FunctionReference> authorizer_callback_;
|
|
220
245
|
|
|
246
|
+
// Environment cleanup hook - called before environment teardown
|
|
247
|
+
static void CleanupHook(void *arg);
|
|
248
|
+
|
|
221
249
|
// Store database-level defaults for statement options
|
|
222
250
|
DatabaseOpenConfiguration config_;
|
|
223
251
|
|
|
224
252
|
bool ValidateThread(Napi::Env env) const;
|
|
225
253
|
friend class Session;
|
|
226
254
|
friend class StatementSync;
|
|
255
|
+
friend class BackupJob;
|
|
227
256
|
};
|
|
228
257
|
|
|
229
258
|
// Statement class
|
|
@@ -268,10 +297,6 @@ private:
|
|
|
268
297
|
void Reset();
|
|
269
298
|
|
|
270
299
|
DatabaseSync *database_ = nullptr;
|
|
271
|
-
// Strong reference to database object to prevent GC while statement exists.
|
|
272
|
-
// This fixes use-after-free when database is GC'd before its statements.
|
|
273
|
-
// See: https://github.com/nodejs/node/pull/56840
|
|
274
|
-
Napi::ObjectReference database_ref_;
|
|
275
300
|
sqlite3_stmt *statement_ = nullptr;
|
|
276
301
|
std::string source_sql_;
|
|
277
302
|
bool finalized_ = false;
|
|
@@ -287,6 +312,7 @@ private:
|
|
|
287
312
|
std::optional<std::map<std::string, std::string>> bare_named_params_;
|
|
288
313
|
|
|
289
314
|
bool ValidateThread(Napi::Env env) const;
|
|
315
|
+
friend class DatabaseSync;
|
|
290
316
|
friend class StatementSyncIterator;
|
|
291
317
|
};
|
|
292
318
|
|
|
@@ -302,6 +328,7 @@ public:
|
|
|
302
328
|
// Iterator methods
|
|
303
329
|
Napi::Value Next(const Napi::CallbackInfo &info);
|
|
304
330
|
Napi::Value Return(const Napi::CallbackInfo &info);
|
|
331
|
+
Napi::Value ToArray(const Napi::CallbackInfo &info);
|
|
305
332
|
|
|
306
333
|
private:
|
|
307
334
|
void SetStatement(StatementSync *stmt);
|
|
@@ -363,10 +390,15 @@ public:
|
|
|
363
390
|
|
|
364
391
|
Napi::Promise GetPromise() { return deferred_.Promise(); }
|
|
365
392
|
|
|
366
|
-
|
|
393
|
+
public:
|
|
394
|
+
// Cleanup is called by FinalizeBackups when database is closing
|
|
367
395
|
void Cleanup();
|
|
396
|
+
// Called by FinalizeBackups to prevent double-unregistration in destructor
|
|
397
|
+
void ClearSource() { source_ = nullptr; }
|
|
368
398
|
|
|
399
|
+
private:
|
|
369
400
|
DatabaseSync *source_;
|
|
401
|
+
sqlite3 *source_connection_; // Captured at construction to avoid race
|
|
370
402
|
std::string destination_path_;
|
|
371
403
|
std::string source_db_;
|
|
372
404
|
std::string dest_db_;
|
|
@@ -381,6 +413,13 @@ private:
|
|
|
381
413
|
Napi::FunctionReference progress_func_;
|
|
382
414
|
Napi::Promise::Deferred deferred_;
|
|
383
415
|
|
|
416
|
+
// Error from progress callback (set on main thread, checked in OnOK)
|
|
417
|
+
std::optional<std::string> progress_error_;
|
|
418
|
+
|
|
419
|
+
// Environment cleanup hook - called before environment teardown
|
|
420
|
+
static void CleanupHook(void *arg);
|
|
421
|
+
napi_env env_;
|
|
422
|
+
|
|
384
423
|
static std::atomic<int> active_jobs_;
|
|
385
424
|
static std::mutex active_jobs_mutex_;
|
|
386
425
|
static std::set<BackupJob *> active_job_instances_;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { DatabaseSyncInstance } from "./types/database-sync-instance";
|
|
2
|
+
import type { TransactionFunction, TransactionMode } from "./types/transaction";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Internal counter for generating unique savepoint names.
|
|
6
|
+
* Using a global counter ensures uniqueness even with deeply nested transactions.
|
|
7
|
+
*/
|
|
8
|
+
let savepointCounter = 0;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a transaction-wrapped version of a function.
|
|
12
|
+
*
|
|
13
|
+
* When the returned function is called, it will:
|
|
14
|
+
* 1. Begin a transaction (or create a savepoint if already in a transaction)
|
|
15
|
+
* 2. Execute the wrapped function
|
|
16
|
+
* 3. Commit the transaction (or release the savepoint) on success
|
|
17
|
+
* 4. Rollback the transaction (or rollback to savepoint) on error
|
|
18
|
+
*
|
|
19
|
+
* The wrapped function **must not** return a Promise. SQLite transactions are
|
|
20
|
+
* synchronous, and allowing async operations would leave the transaction open
|
|
21
|
+
* across event loop ticks, which is dangerous and can cause deadlocks.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const db = new DatabaseSync(':memory:');
|
|
26
|
+
* db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)');
|
|
27
|
+
*
|
|
28
|
+
* const insert = db.prepare('INSERT INTO items (name) VALUES (?)');
|
|
29
|
+
*
|
|
30
|
+
* // Create a transaction function
|
|
31
|
+
* const insertMany = db.transaction((names: string[]) => {
|
|
32
|
+
* for (const name of names) {
|
|
33
|
+
* insert.run(name);
|
|
34
|
+
* }
|
|
35
|
+
* return names.length;
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Execute - automatically wrapped in BEGIN/COMMIT
|
|
39
|
+
* const count = insertMany(['Alice', 'Bob', 'Charlie']);
|
|
40
|
+
* console.log(count); // 3
|
|
41
|
+
*
|
|
42
|
+
* // If an error occurs, automatically rolls back
|
|
43
|
+
* try {
|
|
44
|
+
* insertMany(['Dave', 'FAIL']); // Assume this throws
|
|
45
|
+
* } catch (e) {
|
|
46
|
+
* // Transaction was rolled back, Dave was not inserted
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* // Use different transaction modes
|
|
50
|
+
* insertMany.immediate(['Eve']); // BEGIN IMMEDIATE
|
|
51
|
+
* insertMany.exclusive(['Frank']); // BEGIN EXCLUSIVE
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @param db - The database connection
|
|
55
|
+
* @param fn - The function to wrap in a transaction
|
|
56
|
+
* @returns A transaction function with `.deferred`, `.immediate`, and `.exclusive` variants
|
|
57
|
+
*/
|
|
58
|
+
export function createTransaction<F extends (...args: any[]) => any>(
|
|
59
|
+
db: DatabaseSyncInstance,
|
|
60
|
+
fn: F,
|
|
61
|
+
): TransactionFunction<F> {
|
|
62
|
+
if (typeof fn !== "function") {
|
|
63
|
+
throw new TypeError("Expected first argument to be a function");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create all four variants
|
|
67
|
+
const variants = {
|
|
68
|
+
deferred: createVariant(db, fn, "deferred"),
|
|
69
|
+
immediate: createVariant(db, fn, "immediate"),
|
|
70
|
+
exclusive: createVariant(db, fn, "exclusive"),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// The default function uses DEFERRED mode (SQLite's default)
|
|
74
|
+
const defaultFn = variants.deferred as TransactionFunction<F>;
|
|
75
|
+
|
|
76
|
+
// Set up the variant properties on each function
|
|
77
|
+
// Each variant has access to all other variants
|
|
78
|
+
for (const variant of Object.values(variants)) {
|
|
79
|
+
Object.defineProperties(variant, {
|
|
80
|
+
deferred: { value: variants.deferred, enumerable: true },
|
|
81
|
+
immediate: { value: variants.immediate, enumerable: true },
|
|
82
|
+
exclusive: { value: variants.exclusive, enumerable: true },
|
|
83
|
+
database: { value: db, enumerable: true },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return defaultFn;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Creates a single transaction variant for a specific mode.
|
|
92
|
+
*/
|
|
93
|
+
function createVariant<F extends (...args: any[]) => any>(
|
|
94
|
+
db: DatabaseSyncInstance,
|
|
95
|
+
fn: F,
|
|
96
|
+
mode: TransactionMode,
|
|
97
|
+
): (...args: Parameters<F>) => ReturnType<F> {
|
|
98
|
+
const beginStatement = getBeginStatement(mode);
|
|
99
|
+
|
|
100
|
+
return function transactionWrapper(
|
|
101
|
+
this: unknown,
|
|
102
|
+
...args: Parameters<F>
|
|
103
|
+
): ReturnType<F> {
|
|
104
|
+
// Check if we're already in a transaction (nested transaction)
|
|
105
|
+
const isNested = db.isTransaction;
|
|
106
|
+
|
|
107
|
+
let begin: string;
|
|
108
|
+
let commit: string;
|
|
109
|
+
let rollback: string;
|
|
110
|
+
|
|
111
|
+
if (isNested) {
|
|
112
|
+
// Use savepoints for nested transactions
|
|
113
|
+
// The savepoint name uses backticks to allow special characters
|
|
114
|
+
// and a counter to ensure uniqueness
|
|
115
|
+
const savepointName = `\`_txn_${++savepointCounter}\``;
|
|
116
|
+
begin = `SAVEPOINT ${savepointName}`;
|
|
117
|
+
commit = `RELEASE ${savepointName}`;
|
|
118
|
+
rollback = `ROLLBACK TO ${savepointName}`;
|
|
119
|
+
} else {
|
|
120
|
+
// Top-level transaction
|
|
121
|
+
begin = beginStatement;
|
|
122
|
+
commit = "COMMIT";
|
|
123
|
+
rollback = "ROLLBACK";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Begin the transaction or savepoint
|
|
127
|
+
db.exec(begin);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Execute the wrapped function
|
|
131
|
+
const result = fn.apply(this, args);
|
|
132
|
+
|
|
133
|
+
// Check for promises - async functions break transaction semantics
|
|
134
|
+
if (result !== null && typeof result === "object" && "then" in result) {
|
|
135
|
+
throw new TypeError(
|
|
136
|
+
"Transaction function must not return a Promise. " +
|
|
137
|
+
"SQLite transactions are synchronous and cannot span across async operations. " +
|
|
138
|
+
"Use synchronous code within transactions.",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Commit the transaction or release the savepoint
|
|
143
|
+
db.exec(commit);
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
// Only attempt rollback if we're still in a transaction
|
|
148
|
+
// (SQLite may have already rolled back due to constraint violations, etc.)
|
|
149
|
+
if (db.isTransaction) {
|
|
150
|
+
db.exec(rollback);
|
|
151
|
+
// For nested transactions, we also need to release the savepoint
|
|
152
|
+
// after rolling back to it (the savepoint still exists after ROLLBACK TO)
|
|
153
|
+
if (isNested) {
|
|
154
|
+
db.exec(commit);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Returns the appropriate BEGIN statement for a transaction mode.
|
|
165
|
+
*/
|
|
166
|
+
function getBeginStatement(mode: TransactionMode): string {
|
|
167
|
+
switch (mode) {
|
|
168
|
+
case "deferred":
|
|
169
|
+
return "BEGIN DEFERRED";
|
|
170
|
+
case "immediate":
|
|
171
|
+
return "BEGIN IMMEDIATE";
|
|
172
|
+
case "exclusive":
|
|
173
|
+
return "BEGIN EXCLUSIVE";
|
|
174
|
+
default:
|
|
175
|
+
// TypeScript should catch this, but just in case
|
|
176
|
+
throw new Error(`Unknown transaction mode: ${mode}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -88,13 +88,18 @@ export interface DatabaseSyncInstance {
|
|
|
88
88
|
* ```
|
|
89
89
|
*/
|
|
90
90
|
createTagStore(capacity?: number): SQLTagStoreInstance;
|
|
91
|
+
|
|
91
92
|
/**
|
|
92
93
|
* Apply a changeset to the database.
|
|
93
94
|
* @param changeset The changeset data to apply.
|
|
94
95
|
* @param options Optional configuration for applying the changeset.
|
|
95
96
|
* @returns true if successful, false if aborted.
|
|
96
97
|
*/
|
|
97
|
-
applyChangeset(
|
|
98
|
+
applyChangeset(
|
|
99
|
+
changeset: Uint8Array,
|
|
100
|
+
options?: ChangesetApplyOptions,
|
|
101
|
+
): boolean;
|
|
102
|
+
|
|
98
103
|
/**
|
|
99
104
|
* Enables or disables the loading of SQLite extensions.
|
|
100
105
|
* @param enable If true, enables extension loading. If false, disables it.
|
|
@@ -159,45 +164,6 @@ export interface DatabaseSyncInstance {
|
|
|
159
164
|
| null,
|
|
160
165
|
): void;
|
|
161
166
|
|
|
162
|
-
/**
|
|
163
|
-
* Makes a backup of the database. This method abstracts the sqlite3_backup_init(),
|
|
164
|
-
* sqlite3_backup_step() and sqlite3_backup_finish() functions.
|
|
165
|
-
*
|
|
166
|
-
* The backed-up database can be used normally during the backup process. Mutations
|
|
167
|
-
* coming from the same connection will be reflected in the backup right away.
|
|
168
|
-
* However, mutations from other connections will cause the backup process to restart.
|
|
169
|
-
*
|
|
170
|
-
* @param path The path where the backup will be created. If the file already exists, the contents will be overwritten.
|
|
171
|
-
* @param options Optional configuration for the backup operation.
|
|
172
|
-
* @param options.rate Number of pages to be transmitted in each batch of the backup. @default 100
|
|
173
|
-
* @param options.source Name of the source database. This can be 'main' (the default primary database) or any other database that have been added with ATTACH DATABASE. @default 'main'
|
|
174
|
-
* @param options.target Name of the target database. This can be 'main' (the default primary database) or any other database that have been added with ATTACH DATABASE. @default 'main'
|
|
175
|
-
* @param options.progress Callback function that will be called with the number of pages copied and the total number of pages.
|
|
176
|
-
* @returns A promise that resolves when the backup is completed and rejects if an error occurs.
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* // Basic backup
|
|
180
|
-
* await db.backup('./backup.db');
|
|
181
|
-
*
|
|
182
|
-
* @example
|
|
183
|
-
* // Backup with progress
|
|
184
|
-
* await db.backup('./backup.db', {
|
|
185
|
-
* rate: 10,
|
|
186
|
-
* progress: ({ totalPages, remainingPages }) => {
|
|
187
|
-
* console.log(`Progress: ${totalPages - remainingPages}/${totalPages}`);
|
|
188
|
-
* }
|
|
189
|
-
* });
|
|
190
|
-
*/
|
|
191
|
-
backup(
|
|
192
|
-
path: string | Buffer | URL,
|
|
193
|
-
options?: {
|
|
194
|
-
rate?: number;
|
|
195
|
-
source?: string;
|
|
196
|
-
target?: string;
|
|
197
|
-
progress?: (info: { totalPages: number; remainingPages: number }) => void;
|
|
198
|
-
},
|
|
199
|
-
): Promise<number>;
|
|
200
|
-
|
|
201
167
|
/** Dispose of the database resources using the explicit resource management protocol. */
|
|
202
168
|
[Symbol.dispose](): void;
|
|
203
169
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the pragma() method.
|
|
3
|
+
*
|
|
4
|
+
* @see https://sqlite.org/pragma.html
|
|
5
|
+
*/
|
|
6
|
+
export interface PragmaOptions {
|
|
7
|
+
/**
|
|
8
|
+
* When true, returns only the first column of the first row.
|
|
9
|
+
* This is useful for pragmas that return a single value.
|
|
10
|
+
*
|
|
11
|
+
* @default false
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Without simple: returns [{ cache_size: -16000 }]
|
|
16
|
+
* db.pragma('cache_size');
|
|
17
|
+
*
|
|
18
|
+
* // With simple: returns -16000
|
|
19
|
+
* db.pragma('cache_size', { simple: true });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
readonly simple?: boolean;
|
|
23
|
+
}
|
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata about a column in a prepared statement's result set.
|
|
3
|
+
* Matches Node.js sqlite module's StatementColumnMetadata.
|
|
4
|
+
*/
|
|
5
|
+
export interface StatementColumnMetadata {
|
|
6
|
+
/**
|
|
7
|
+
* The unaliased name of the column in the origin table, or `null` if the
|
|
8
|
+
* column is the result of an expression or subquery.
|
|
9
|
+
* This property is the result of `sqlite3_column_origin_name()`.
|
|
10
|
+
*/
|
|
11
|
+
column: string | null;
|
|
12
|
+
/**
|
|
13
|
+
* The unaliased name of the origin database, or `null` if the column is
|
|
14
|
+
* the result of an expression or subquery.
|
|
15
|
+
* This property is the result of `sqlite3_column_database_name()`.
|
|
16
|
+
*/
|
|
17
|
+
database: string | null;
|
|
18
|
+
/**
|
|
19
|
+
* The name assigned to the column in the result set of a SELECT statement.
|
|
20
|
+
* This property is the result of `sqlite3_column_name()`.
|
|
21
|
+
*/
|
|
22
|
+
name: string;
|
|
23
|
+
/**
|
|
24
|
+
* The unaliased name of the origin table, or `null` if the column is
|
|
25
|
+
* the result of an expression or subquery.
|
|
26
|
+
* This property is the result of `sqlite3_column_table_name()`.
|
|
27
|
+
*/
|
|
28
|
+
table: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* The declared data type of the column, or `null` if the column is
|
|
31
|
+
* the result of an expression or subquery.
|
|
32
|
+
* This property is the result of `sqlite3_column_decltype()`.
|
|
33
|
+
*/
|
|
34
|
+
type: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
1
37
|
/**
|
|
2
38
|
* A prepared SQL statement that can be executed multiple times with different parameters.
|
|
3
39
|
* This interface represents an instance of the StatementSync class.
|
|
@@ -7,8 +43,6 @@ export interface StatementSyncInstance {
|
|
|
7
43
|
readonly sourceSQL: string;
|
|
8
44
|
/** The expanded SQL string with bound parameters, if expandedSQL option was set. */
|
|
9
45
|
readonly expandedSQL: string | undefined;
|
|
10
|
-
/** Whether this statement has been finalized. */
|
|
11
|
-
readonly finalized: boolean;
|
|
12
46
|
/**
|
|
13
47
|
* This method executes a prepared statement and returns an object.
|
|
14
48
|
* @param parameters Optional named and anonymous parameters to bind to the statement.
|
|
@@ -59,15 +93,7 @@ export interface StatementSyncInstance {
|
|
|
59
93
|
setReturnArrays(returnArrays: boolean): void;
|
|
60
94
|
/**
|
|
61
95
|
* Returns an array of objects, each representing a column in the statement's result set.
|
|
62
|
-
*
|
|
63
|
-
* @returns Array of column metadata objects.
|
|
64
|
-
*/
|
|
65
|
-
columns(): Array<{ name: string; type?: string }>;
|
|
66
|
-
/**
|
|
67
|
-
* Finalizes the prepared statement and releases its resources.
|
|
68
|
-
* Called automatically by Symbol.dispose.
|
|
96
|
+
* @returns Array of column metadata objects with name, column, database, table, and type.
|
|
69
97
|
*/
|
|
70
|
-
|
|
71
|
-
/** Dispose of the statement resources using the explicit resource management protocol. */
|
|
72
|
-
[Symbol.dispose](): void;
|
|
98
|
+
columns(): StatementColumnMetadata[];
|
|
73
99
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { DatabaseSyncInstance } from "./database-sync-instance";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transaction isolation modes supported by SQLite.
|
|
5
|
+
*
|
|
6
|
+
* @see https://sqlite.org/lang_transaction.html
|
|
7
|
+
*/
|
|
8
|
+
export type TransactionMode = "deferred" | "immediate" | "exclusive";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A function wrapped in a transaction. When called, it automatically:
|
|
12
|
+
* - Begins a transaction (or savepoint if nested)
|
|
13
|
+
* - Executes the wrapped function
|
|
14
|
+
* - Commits on success, rolls back on error
|
|
15
|
+
*
|
|
16
|
+
* The function also has variants for different transaction modes accessible as
|
|
17
|
+
* properties (`.deferred`, `.immediate`, `.exclusive`).
|
|
18
|
+
*
|
|
19
|
+
* **Note:** Each variant is itself a `TransactionFunction` with circular
|
|
20
|
+
* references to all other variants. This matches better-sqlite3's behavior
|
|
21
|
+
* where you can chain variants like `txn.deferred.immediate()` - the last
|
|
22
|
+
* variant in the chain determines the actual transaction mode used.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const insertMany = db.transaction((items: Item[]) => {
|
|
27
|
+
* for (const item of items) insert.run(item);
|
|
28
|
+
* return items.length;
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Use default mode (DEFERRED)
|
|
32
|
+
* insertMany([{ id: 1 }, { id: 2 }]);
|
|
33
|
+
*
|
|
34
|
+
* // Use IMMEDIATE mode for write transactions
|
|
35
|
+
* insertMany.immediate([{ id: 3 }, { id: 4 }]);
|
|
36
|
+
*
|
|
37
|
+
* // Use EXCLUSIVE mode for exclusive access
|
|
38
|
+
* insertMany.exclusive([{ id: 5 }, { id: 6 }]);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export interface TransactionFunction<F extends (...args: any[]) => any> {
|
|
42
|
+
/**
|
|
43
|
+
* Execute the wrapped function within a transaction using the default mode (DEFERRED).
|
|
44
|
+
*/
|
|
45
|
+
(...args: Parameters<F>): ReturnType<F>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Execute with DEFERRED transaction mode.
|
|
49
|
+
* The database lock is not acquired until the first read or write operation.
|
|
50
|
+
* This is the default SQLite behavior.
|
|
51
|
+
*/
|
|
52
|
+
readonly deferred: TransactionFunction<F>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Execute with IMMEDIATE transaction mode.
|
|
56
|
+
* Acquires a write lock immediately, blocking other writers.
|
|
57
|
+
* Recommended for transactions that will write to prevent SQLITE_BUSY errors.
|
|
58
|
+
*/
|
|
59
|
+
readonly immediate: TransactionFunction<F>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Execute with EXCLUSIVE transaction mode.
|
|
63
|
+
* Acquires an exclusive lock immediately, blocking all other connections.
|
|
64
|
+
* Use sparingly as it prevents all concurrent access.
|
|
65
|
+
*/
|
|
66
|
+
readonly exclusive: TransactionFunction<F>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The database connection this transaction function is bound to.
|
|
70
|
+
*/
|
|
71
|
+
readonly database: DatabaseSyncInstance;
|
|
72
|
+
}
|