@procedures/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 +57 -0
- package/README.md +93 -32
- package/dist/.tsbuildinfo +1 -1
- package/dist/engines.d.ts +68 -0
- package/dist/engines.d.ts.map +1 -0
- package/dist/engines.js +268 -0
- package/dist/engines.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +13 -3
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +60 -27
- package/dist/module.js.map +1 -1
- package/dist/procedures/db/backup.d.ts +1 -1
- package/dist/procedures/db/backup.js +6 -6
- package/dist/procedures/db/backup.js.map +1 -1
- package/dist/procedures/db/migrate.d.ts +1 -1
- package/dist/procedures/db/migrate.js +8 -8
- package/dist/procedures/db/migrate.js.map +1 -1
- package/dist/procedures/db/schema.d.ts +1 -1
- package/dist/procedures/db/schema.js +3 -3
- package/dist/procedures/db/schema.js.map +1 -1
- package/dist/procedures/db/shared.d.ts +1 -1
- package/dist/procedures/db/shared.js +1 -1
- package/dist/procedures/db/tables.d.ts +1 -1
- package/dist/procedures/db/tables.js +2 -2
- package/dist/procedures/db/tables.js.map +1 -1
- package/dist/procedures/db/transaction.d.ts +1 -1
- package/dist/procedures/db/transaction.js +2 -2
- package/dist/procedures/db/transaction.js.map +1 -1
- package/dist/storage.d.ts +57 -16
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +255 -148
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/package.json +6 -5
- package/src/engines.ts +360 -0
- package/src/index.ts +12 -4
- package/src/module.ts +65 -28
- package/src/procedures/db/backup.ts +6 -6
- package/src/procedures/db/migrate.ts +8 -8
- package/src/procedures/db/schema.ts +3 -3
- package/src/procedures/db/shared.ts +1 -1
- package/src/procedures/db/tables.ts +2 -2
- package/src/procedures/db/transaction.ts +2 -2
- package/src/storage.ts +335 -176
- package/src/types.ts +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @procedures/sqlite
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 89481b0: `SqliteStorage` has two engines. The new option `engine` is `"auto"` (the default), `"node"` or `"sqljs"`.
|
|
8
|
+
|
|
9
|
+
- The engine `node` is `node:sqlite` of Node.js 22.13 and later. SQLite writes only the changed pages of the file. Before, sql.js wrote the whole file after each write, so a write to a large collection was slow.
|
|
10
|
+
- In the nightly benchmark, 1,000 writes one after the other to a file of 1,000 items take 141 ms with `node` and 1,246 ms with `sqljs`.
|
|
11
|
+
- With `node`, the file is in WAL mode with a busy timeout (the new option `busyTimeout`, 5 seconds by default). Two processes can use one file: a write waits for the lock of the other process, and the versions of the change log agree.
|
|
12
|
+
- The engine `sqljs` is sql.js, as before. The engine `auto` uses it when the import of `node:sqlite` fails, for example on Node.js 22.12 and before.
|
|
13
|
+
- The two engines use one file format: a file of one engine opens with the other, with its items and its versions.
|
|
14
|
+
- The guarantees stay for each engine:
|
|
15
|
+
- Each write runs in one transaction.
|
|
16
|
+
- The storages of one file in one process share one database.
|
|
17
|
+
- The change log and its versions stay valid after a restart.
|
|
18
|
+
- A storage that only reads does not make the file.
|
|
19
|
+
- The contract of `close()` does not change.
|
|
20
|
+
- The storages of one file in one process share one engine. A storage with the other engine refuses the open database.
|
|
21
|
+
- sql.js refuses a file with a write-ahead log of the engine `node` that holds changes.
|
|
22
|
+
- New: `getEngine()`, `getIds()` (the ids without the values), `isNodeSqliteAvailable()`, and `stats.engine` in `getMetadata()`.
|
|
23
|
+
- `getChangesSince()` reads in one read transaction, and it reads the epoch from the file at each call.
|
|
24
|
+
- bc74091: After `close()`, each operation of `SqliteStorage` and `MongoStorage` rejects with an error that says "closed", and a second `close()` does nothing. Before, `SqliteStorage.flush()` and an empty batch resolved after `close()`, and `MongoStorage` kept working.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [0f75644]
|
|
29
|
+
- Updated dependencies [09e5566]
|
|
30
|
+
- Updated dependencies [ab77f75]
|
|
31
|
+
- Updated dependencies [a47bee6]
|
|
32
|
+
- Updated dependencies [bc74091]
|
|
33
|
+
- @procedures/client@0.5.0
|
|
34
|
+
|
|
35
|
+
## 0.4.0
|
|
36
|
+
|
|
37
|
+
### Minor Changes
|
|
38
|
+
|
|
39
|
+
- cc3f5f6: The SQLite procedures have the namespace `sqlite`, not the general namespace `db`: `sqlite.query`, `sqlite.execute`, `sqlite.tables`, `sqlite.schema`, `sqlite.transaction`, `sqlite.migrate` and `sqlite.backup`. The `logs.*` procedures keep their names.
|
|
40
|
+
|
|
41
|
+
The old names stay for one release as deprecated aliases (`db.query` and the others). Each alias has the same handler, the same schemas and the same effects. Its description starts with `Deprecated: use sqlite.<name>`, and its metadata has `deprecated: true`. The next release removes the aliases.
|
|
42
|
+
|
|
43
|
+
The description of the `dbPath` field does not show the home folder of the computer any more. It says `~/logs/cli/cli.db`.
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- Updated dependencies [d919678]
|
|
48
|
+
- Updated dependencies [057d69a]
|
|
49
|
+
- Updated dependencies [b2a1a1e]
|
|
50
|
+
- Updated dependencies [057d69a]
|
|
51
|
+
- Updated dependencies [057d69a]
|
|
52
|
+
- Updated dependencies [057d69a]
|
|
53
|
+
- Updated dependencies [057d69a]
|
|
54
|
+
- Updated dependencies [057d69a]
|
|
55
|
+
- Updated dependencies [afd6a19]
|
|
56
|
+
- Updated dependencies [057d69a]
|
|
57
|
+
- Updated dependencies [4e751da]
|
|
58
|
+
- @procedures/client@0.4.0
|
|
59
|
+
|
|
3
60
|
## 0.3.0
|
|
4
61
|
|
|
5
62
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -18,9 +18,17 @@ SQLite database operations via client procedures. Execute queries and store stru
|
|
|
18
18
|
- **A collection storage**: `SqliteStorage` keeps a collection in a SQLite table, with a change log
|
|
19
19
|
|
|
20
20
|
The package has 9 procedures. Its register entry (`import "@procedures/sqlite/register"`) registers them:
|
|
21
|
-
- **Database**: `
|
|
21
|
+
- **Database**: `sqlite.query`, `sqlite.execute`, `sqlite.tables`, `sqlite.schema`, `sqlite.transaction`, `sqlite.migrate`, `sqlite.backup`
|
|
22
22
|
- **Logs**: `logs.store`, `logs.query`
|
|
23
23
|
|
|
24
|
+
### The old names `db.*`
|
|
25
|
+
|
|
26
|
+
In version 0.3.0 and before, the database procedures had the namespace `db`. The general name `db` did not tell the database. Since version 0.4.0, the namespace is `sqlite`.
|
|
27
|
+
|
|
28
|
+
The old names stay for one release as deprecated aliases: `db.query`, `db.execute`, `db.tables`, `db.schema`, `db.transaction`, `db.migrate` and `db.backup`. Each alias has the same handler, the same schemas and the same effects as its new procedure. Its description starts with `Deprecated: use sqlite.<name>`. The next release removes the aliases.
|
|
29
|
+
|
|
30
|
+
The MCP server (`bundle-mcp`) does not expose the aliases. Its tools are `sqlite.query`, `sqlite.execute`, `sqlite.tables` and `sqlite.schema`. The `logs.*` procedures keep their names.
|
|
31
|
+
|
|
24
32
|
## Architecture
|
|
25
33
|
|
|
26
34
|
```mermaid
|
|
@@ -34,7 +42,7 @@ graph TB
|
|
|
34
42
|
Register["register.ts<br/>(Auto-registers procedures)"]
|
|
35
43
|
|
|
36
44
|
subgraph "Procedure Categories"
|
|
37
|
-
DB["Database Procedures<br/>
|
|
45
|
+
DB["Database Procedures<br/>sqlite.query, sqlite.execute, sqlite.tables, sqlite.schema,<br/>sqlite.transaction, sqlite.migrate, sqlite.backup"]
|
|
38
46
|
Logs["Log Procedures<br/>logs.store, logs.query"]
|
|
39
47
|
end
|
|
40
48
|
|
|
@@ -148,7 +156,7 @@ You can override this path in each procedure call:
|
|
|
148
156
|
|
|
149
157
|
```typescript
|
|
150
158
|
await client.exec(
|
|
151
|
-
["
|
|
159
|
+
["sqlite", "query"],
|
|
152
160
|
{
|
|
153
161
|
sql: "SELECT * FROM logs",
|
|
154
162
|
dbPath: "./custom/path/database.db"
|
|
@@ -160,13 +168,13 @@ await client.exec(
|
|
|
160
168
|
|
|
161
169
|
### Database Procedures
|
|
162
170
|
|
|
163
|
-
#### `
|
|
171
|
+
#### `sqlite.query`
|
|
164
172
|
|
|
165
173
|
Execute a SQL SELECT query and return rows.
|
|
166
174
|
|
|
167
175
|
```typescript
|
|
168
176
|
const result = await client.exec(
|
|
169
|
-
["
|
|
177
|
+
["sqlite", "query"],
|
|
170
178
|
{
|
|
171
179
|
sql: "SELECT * FROM logs WHERE level = ? ORDER BY timestamp DESC",
|
|
172
180
|
params: ["error"]
|
|
@@ -197,7 +205,7 @@ interface DbQueryOutput<T = Record<string, unknown>> {
|
|
|
197
205
|
**Example:**
|
|
198
206
|
```typescript
|
|
199
207
|
const result = await client.exec(
|
|
200
|
-
["
|
|
208
|
+
["sqlite", "query"],
|
|
201
209
|
{
|
|
202
210
|
sql: `
|
|
203
211
|
SELECT level, COUNT(*) as count
|
|
@@ -212,13 +220,13 @@ const result = await client.exec(
|
|
|
212
220
|
// result.rows = [{ level: "info", count: 45 }, { level: "error", count: 2 }]
|
|
213
221
|
```
|
|
214
222
|
|
|
215
|
-
#### `
|
|
223
|
+
#### `sqlite.execute`
|
|
216
224
|
|
|
217
225
|
Execute a SQL statement (INSERT, UPDATE, DELETE) and return the number of affected rows.
|
|
218
226
|
|
|
219
227
|
```typescript
|
|
220
228
|
const result = await client.exec(
|
|
221
|
-
["
|
|
229
|
+
["sqlite", "execute"],
|
|
222
230
|
{
|
|
223
231
|
sql: "DELETE FROM logs WHERE timestamp < ?",
|
|
224
232
|
params: ["2024-01-01"]
|
|
@@ -248,7 +256,7 @@ interface DbExecuteOutput {
|
|
|
248
256
|
```typescript
|
|
249
257
|
// INSERT
|
|
250
258
|
await client.exec(
|
|
251
|
-
["
|
|
259
|
+
["sqlite", "execute"],
|
|
252
260
|
{
|
|
253
261
|
sql: "INSERT INTO users (name, email) VALUES (?, ?)",
|
|
254
262
|
params: ["Alice", "alice@example.com"]
|
|
@@ -257,7 +265,7 @@ await client.exec(
|
|
|
257
265
|
|
|
258
266
|
// UPDATE
|
|
259
267
|
await client.exec(
|
|
260
|
-
["
|
|
268
|
+
["sqlite", "execute"],
|
|
261
269
|
{
|
|
262
270
|
sql: "UPDATE users SET active = ? WHERE id = ?",
|
|
263
271
|
params: [true, 123]
|
|
@@ -266,7 +274,7 @@ await client.exec(
|
|
|
266
274
|
|
|
267
275
|
// DELETE
|
|
268
276
|
await client.exec(
|
|
269
|
-
["
|
|
277
|
+
["sqlite", "execute"],
|
|
270
278
|
{
|
|
271
279
|
sql: "DELETE FROM users WHERE active = ?",
|
|
272
280
|
params: [false]
|
|
@@ -274,29 +282,29 @@ await client.exec(
|
|
|
274
282
|
);
|
|
275
283
|
```
|
|
276
284
|
|
|
277
|
-
#### `
|
|
285
|
+
#### `sqlite.tables`
|
|
278
286
|
|
|
279
287
|
The procedure lists the tables and the views of a database, without the internal `sqlite_` tables. Each item has the `name`, the `type` and the `sql` that made it. With `views: false`, it gives only the tables. The procedure only reads. A missing file is an error (`ENOENT`), and the procedure does not make it.
|
|
280
288
|
|
|
281
289
|
```typescript
|
|
282
|
-
const { tables } = await client.exec(["
|
|
290
|
+
const { tables } = await client.exec(["sqlite", "tables"], { dbPath: "./app.db" });
|
|
283
291
|
```
|
|
284
292
|
|
|
285
|
-
#### `
|
|
293
|
+
#### `sqlite.schema`
|
|
286
294
|
|
|
287
295
|
The procedure gives the `columns`, the `indexes` and the `foreignKeys` of a table, or the columns of a view. The name of the table goes to SQLite as a bound value, not as SQL text. The procedure only reads.
|
|
288
296
|
|
|
289
297
|
```typescript
|
|
290
|
-
const schema = await client.exec(["
|
|
298
|
+
const schema = await client.exec(["sqlite", "schema"], { dbPath: "./app.db", table: "users" });
|
|
291
299
|
// schema.columns: [{ name: "id", type: "INTEGER", notNull: false, defaultValue: null, primaryKey: 1 }, ...]
|
|
292
300
|
```
|
|
293
301
|
|
|
294
|
-
#### `
|
|
302
|
+
#### `sqlite.transaction`
|
|
295
303
|
|
|
296
304
|
The procedure puts a list of statements in one transaction. When one statement fails, it rolls back the transaction, and the file stays as it was. The `params` of a statement bind to one statement. Without `params`, one `sql` can hold several statements. A statement that gives rows (`SELECT`, or `RETURNING`) gives its `rows` in its result. The procedure refuses `BEGIN`, `COMMIT`, `ROLLBACK`, `SAVEPOINT` and `RELEASE`, because it controls the transaction itself.
|
|
297
305
|
|
|
298
306
|
```typescript
|
|
299
|
-
const { results, changes } = await client.exec(["
|
|
307
|
+
const { results, changes } = await client.exec(["sqlite", "transaction"], {
|
|
300
308
|
dbPath: "./app.db",
|
|
301
309
|
statements: [
|
|
302
310
|
{ sql: "UPDATE accounts SET balance = balance - ? WHERE id = ?", params: [10, 1] },
|
|
@@ -306,7 +314,7 @@ const { results, changes } = await client.exec(["db", "transaction"], {
|
|
|
306
314
|
});
|
|
307
315
|
```
|
|
308
316
|
|
|
309
|
-
#### `
|
|
317
|
+
#### `sqlite.migrate`
|
|
310
318
|
|
|
311
319
|
The procedure applies the numbered migrations that the database does not have, in the order of their numbers. The migrations come from a `folder` or from a list (`migrations`). In a folder, each migration is a file `<number>_<name>.sql`, for example `001_create_users.sql`. The procedure ignores the files that do not end in `.sql`.
|
|
312
320
|
|
|
@@ -317,20 +325,20 @@ The procedure applies the numbered migrations that the database does not have, i
|
|
|
317
325
|
- A migration must not hold `BEGIN`, `COMMIT` or `ROLLBACK`.
|
|
318
326
|
|
|
319
327
|
```typescript
|
|
320
|
-
const { applied, skipped, version } = await client.exec(["
|
|
328
|
+
const { applied, skipped, version } = await client.exec(["sqlite", "migrate"], {
|
|
321
329
|
dbPath: "./app.db",
|
|
322
330
|
folder: "./migrations",
|
|
323
331
|
});
|
|
324
332
|
```
|
|
325
333
|
|
|
326
|
-
#### `
|
|
334
|
+
#### `sqlite.backup`
|
|
327
335
|
|
|
328
336
|
The procedure copies a database to a new file (`to`). It does not replace a file, and it does not write the source. With `vacuum: true`, it compacts the copy.
|
|
329
337
|
|
|
330
338
|
This package uses SQLite in WebAssembly (sql.js). The database is in the memory of the module. `VACUUM INTO` and the backup API of SQLite write into the file system of the module, not to the disk. Thus the procedure writes the image of the database (its `export()`). It takes the image in the file queue of the database, where no other call of the process changes it. It writes the image to a file next to the target, checks it with `PRAGMA integrity_check`, and then renames it.
|
|
331
339
|
|
|
332
340
|
```typescript
|
|
333
|
-
const { path, bytes } = await client.exec(["
|
|
341
|
+
const { path, bytes } = await client.exec(["sqlite", "backup"], { dbPath: "./app.db", to: "./backups/app-2026-10-16.db" });
|
|
334
342
|
```
|
|
335
343
|
|
|
336
344
|
### Log Procedures
|
|
@@ -496,11 +504,38 @@ const users = new SqliteStorage<User>({ path: "./data/app.db", table: "users" })
|
|
|
496
504
|
await users.set("u1", { name: "Ada" });
|
|
497
505
|
await users.setBatch([["u2", { name: "Bob" }], ["u3", { name: "Cy" }]]);
|
|
498
506
|
const changes = await users.getChangesSince(); // { version, full: true, changed, deleted: [] }
|
|
507
|
+
await users.getEngine(); // "node" on Node.js 22.13 or later
|
|
499
508
|
await users.close();
|
|
500
509
|
```
|
|
501
510
|
|
|
502
511
|
The entry `@procedures/sqlite/storage` has the storage alone. The package root exports it too.
|
|
503
512
|
|
|
513
|
+
### The engines
|
|
514
|
+
|
|
515
|
+
The storage has two SQLite engines. The option `engine` selects one.
|
|
516
|
+
|
|
517
|
+
| `engine` | The engine | The file write |
|
|
518
|
+
|---|---|---|
|
|
519
|
+
| `node` | `node:sqlite` of Node.js, without a native build | SQLite writes only the changed pages. The file is in WAL mode. |
|
|
520
|
+
| `sqljs` | sql.js (SQLite in WebAssembly), through `@mark1russell7/docker-sqlite` | The storage keeps the database in memory, and it writes the whole file after a write. |
|
|
521
|
+
| `auto` (the default) | `node` when Node.js has `node:sqlite`, and else `sqljs` | |
|
|
522
|
+
|
|
523
|
+
- Node.js 22.13 and later have `node:sqlite` without a flag. Node.js 22.5 to 22.12 have it only with the flag `--experimental-sqlite`. Node.js 22.0 to 22.4 do not have it.
|
|
524
|
+
- Node.js 22 shows the warning `ExperimentalWarning: SQLite is an experimental feature` at the first open of the engine `node`. Node.js 24 and 26 do not show it.
|
|
525
|
+
- Node.js 26.11 renamed the class `DatabaseSync` to `Database`. The storage uses `Database` when the module has it, and else `DatabaseSync`.
|
|
526
|
+
- With `auto`, the storage uses `sqljs` when the import of `node:sqlite` fails. With `node`, the open fails then, and each operation gives the error.
|
|
527
|
+
- The two engines use the same file format. A file that one engine writes opens with the other engine, with its items and its versions.
|
|
528
|
+
- `getEngine()` gives the engine of the storage. `getMetadata()` gives it in `stats.engine`. `isNodeSqliteAvailable()` tells if this Node.js has `node:sqlite`.
|
|
529
|
+
|
|
530
|
+
The nightly benchmarks (`bench/storage.bench.ts`) measure the two engines in a file. These are the mean times of one CI run with Node.js 26 on Linux:
|
|
531
|
+
|
|
532
|
+
| The benchmark | `node` | `sqljs` |
|
|
533
|
+
|---|---|---|
|
|
534
|
+
| 1,000 calls of `set()`, one after the other, in a table of 1,000 items | 141 ms | 1,246 ms |
|
|
535
|
+
| One `getAll()` of a table of 10,000 items | 20 ms | 28 ms |
|
|
536
|
+
|
|
537
|
+
The engine `sqljs` writes the whole file at each write, so its write time grows with the size of the file.
|
|
538
|
+
|
|
504
539
|
### The table and the values
|
|
505
540
|
|
|
506
541
|
- The option `path` is a database file, or `:memory:` for a database that lives as long as the storage.
|
|
@@ -508,6 +543,7 @@ The entry `@procedures/sqlite/storage` has the storage alone. The package root e
|
|
|
508
543
|
- The table has the columns `id` and `value`. The value is the JSON text of the item.
|
|
509
544
|
- A value without JSON text, for example `undefined` or a `BigInt`, is a `TypeError`. The storage writes nothing then.
|
|
510
545
|
- A read gives a new copy of the item.
|
|
546
|
+
- `getIds()` gives the ids in the order of the items, and it does not read the values. A full sync of `HybridStorage` uses it.
|
|
511
547
|
- The order of the items is the order of the first write of each id, as in a `Map`.
|
|
512
548
|
|
|
513
549
|
### The change log and the versions
|
|
@@ -516,17 +552,40 @@ The entry `@procedures/sqlite/storage` has the storage alone. The package root e
|
|
|
516
552
|
- A version is `<epoch>:<seq>`, as in `InMemoryStorage`. The table `_storage_versions` keeps the epoch, so a version stays valid after a restart.
|
|
517
553
|
- The table `_storage_log` keeps the last change of each id. The option `maxTombstones` (10000 by default) limits the deletes that it keeps.
|
|
518
554
|
- A version that is older than the dropped deletes gives all items (`full: true`).
|
|
555
|
+
- A delta follows the order of the last change of each id, as in each other storage. A full result follows the order of the items.
|
|
519
556
|
- Write the table only through the storage. A change from other SQL is not in the change log.
|
|
520
557
|
|
|
521
558
|
### The transactions and the file
|
|
522
559
|
|
|
523
560
|
- Each write runs in a transaction with the change log. A batch that fails changes nothing.
|
|
524
|
-
-
|
|
525
|
-
-
|
|
526
|
-
-
|
|
527
|
-
- A storage that only reads does not make the file.
|
|
561
|
+
- A write ends after its change is in the file.
|
|
562
|
+
- A storage that only reads does not make the file. The storage makes the file, and its folder, at the first write that changes an item.
|
|
563
|
+
- `close()` writes the file and releases the database. After it, each operation rejects, also `flush()` and an empty batch. A second `close()` does nothing.
|
|
528
564
|
- The storages of one file in one process share one database. Thus the storages of two tables in one file keep their items.
|
|
529
|
-
-
|
|
565
|
+
- The first storage of a file sets the engine of the shared database. A later storage with the engine `auto` uses it. A later storage with the other engine refuses it.
|
|
566
|
+
|
|
567
|
+
The engine `node`:
|
|
568
|
+
|
|
569
|
+
- The commit of a transaction writes the change to the file. `flush()` has nothing to write.
|
|
570
|
+
- The file is in WAL mode, with `synchronous = NORMAL`. After a crash, the file stays consistent. A power loss can lose the last commits.
|
|
571
|
+
- A write that cannot make the file fails, and it changes nothing.
|
|
572
|
+
- A read-only file opens. The reads work, and each write fails.
|
|
573
|
+
|
|
574
|
+
The engine `sqljs`:
|
|
575
|
+
|
|
576
|
+
- The storage keeps the database in memory, and it writes the whole file after each write. When several writes wait, one file write holds their changes.
|
|
577
|
+
- A failed file write fails the write. The change stays in memory, and `flush()` or the next write writes it.
|
|
578
|
+
- The storage refuses a file with a write-ahead log (`<file>-wal`) of the engine `node`. The log can hold changes that are not in the file. Open the file with the engine `node` one time, or close the process that has it open.
|
|
579
|
+
|
|
580
|
+
### Two processes on one file
|
|
581
|
+
|
|
582
|
+
- With the engine `node` in each process, two processes can use one file at the same time.
|
|
583
|
+
- SQLite locks the file for each write. A write waits for the lock of another process for `busyTimeout` (5 seconds by default). After it, the write fails with `database is locked`.
|
|
584
|
+
- In WAL mode, a read does not wait for a write of another process.
|
|
585
|
+
- Each write reads the version counter in its transaction, so the versions of the two processes agree. `getChangesSince()` reads in one read transaction.
|
|
586
|
+
- A storage that opened before the file existed opens the file when another process makes it.
|
|
587
|
+
- The engine `sqljs` does not coordinate with another process. Its whole-file write replaces the writes of the other process. Do not use it on a file that another process writes.
|
|
588
|
+
- The procedures `sqlite.query` and `sqlite.execute` use sql.js, and they do not share the database of the storages. Do not write the file from them while a storage has it open.
|
|
530
589
|
|
|
531
590
|
### The durable local copy of `HybridStorage`
|
|
532
591
|
|
|
@@ -556,7 +615,7 @@ const client = new Client(new LocalTransport({ registry: PROCEDURE_REGISTRY }));
|
|
|
556
615
|
|
|
557
616
|
// Create a table
|
|
558
617
|
await client.exec(
|
|
559
|
-
["
|
|
618
|
+
["sqlite", "execute"],
|
|
560
619
|
{
|
|
561
620
|
sql: `
|
|
562
621
|
CREATE TABLE IF NOT EXISTS users (
|
|
@@ -573,7 +632,7 @@ await client.exec(
|
|
|
573
632
|
|
|
574
633
|
// Insert data
|
|
575
634
|
await client.exec(
|
|
576
|
-
["
|
|
635
|
+
["sqlite", "execute"],
|
|
577
636
|
{
|
|
578
637
|
sql: "INSERT INTO users (name, email) VALUES (?, ?)",
|
|
579
638
|
params: ["Alice Johnson", "alice@example.com"],
|
|
@@ -583,7 +642,7 @@ await client.exec(
|
|
|
583
642
|
|
|
584
643
|
// Query data
|
|
585
644
|
const result = await client.exec(
|
|
586
|
-
["
|
|
645
|
+
["sqlite", "query"],
|
|
587
646
|
{
|
|
588
647
|
sql: "SELECT * FROM users WHERE active = ? ORDER BY created_at DESC",
|
|
589
648
|
params: [1],
|
|
@@ -747,7 +806,7 @@ interface User {
|
|
|
747
806
|
}
|
|
748
807
|
|
|
749
808
|
const result = await client.call<DbQueryOutput<User>>(
|
|
750
|
-
["
|
|
809
|
+
["sqlite", "query"],
|
|
751
810
|
{
|
|
752
811
|
sql: "SELECT * FROM users WHERE active = ?",
|
|
753
812
|
params: [true],
|
|
@@ -827,11 +886,13 @@ export type {
|
|
|
827
886
|
client-sqlite/
|
|
828
887
|
├── src/
|
|
829
888
|
│ ├── types.ts # Type definitions
|
|
830
|
-
│ ├── module.ts # The procedures as data, and the
|
|
889
|
+
│ ├── module.ts # The procedures as data, and the sqlite.query, sqlite.execute and logs.* handlers
|
|
831
890
|
│ ├── register.ts # The ./register entry: registers the module
|
|
832
891
|
│ ├── storage.ts # SqliteStorage, the collection storage (the ./storage entry)
|
|
833
|
-
│ ├──
|
|
892
|
+
│ ├── engines.ts # The engines of SqliteStorage: node:sqlite and sql.js
|
|
893
|
+
│ ├── procedures/db/ # sqlite.tables, sqlite.schema, sqlite.transaction, sqlite.migrate, sqlite.backup
|
|
834
894
|
│ └── index.ts # Main entry point
|
|
895
|
+
├── bench/ # The benchmarks of the engines (the nightly workflow runs them)
|
|
835
896
|
├── dist/ # Compiled output
|
|
836
897
|
├── package.json
|
|
837
898
|
├── tsconfig.json
|