dbgate-sqlite-dumper 0.1.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/LICENSE +674 -0
- package/README.md +230 -0
- package/dist/better-sqlite3.cjs +178 -0
- package/dist/better-sqlite3.cjs.map +1 -0
- package/dist/better-sqlite3.d.cts +55 -0
- package/dist/better-sqlite3.d.ts +55 -0
- package/dist/better-sqlite3.js +140 -0
- package/dist/better-sqlite3.js.map +1 -0
- package/dist/index.cjs +3902 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1893 -0
- package/dist/index.d.ts +1893 -0
- package/dist/index.js +3775 -0
- package/dist/index.js.map +1 -0
- package/dist/types-CTyJTzB6.d.cts +151 -0
- package/dist/types-CTyJTzB6.d.ts +151 -0
- package/docs/architecture.md +157 -0
- package/docs/better-sqlite3-adapter.md +83 -0
- package/docs/dump-api.md +253 -0
- package/docs/known-limitations.md +88 -0
- package/docs/native-compatibility.md +124 -0
- package/docs/restore-api.md +234 -0
- package/docs/round-trip-testing.md +84 -0
- package/docs/supported-data-types.md +74 -0
- package/docs/supported-objects.md +85 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# dbgate-sqlite-dumper
|
|
2
|
+
|
|
3
|
+
Standalone, client-agnostic SQLite dump and restore library for Node.js.
|
|
4
|
+
|
|
5
|
+
Produces the same plain-SQL dump the `sqlite3` shell's `.dump` command writes, and
|
|
6
|
+
restores it back — entirely through a database handle. **No `sqlite3` shell, no
|
|
7
|
+
external process is ever invoked.** Framework-independent: it does not depend on
|
|
8
|
+
DbGate internals and works outside DbGate.
|
|
9
|
+
|
|
10
|
+
- Node.js >= 20, ESM and CJS builds, full TypeScript types
|
|
11
|
+
- `better-sqlite3` is an **optional** peer dependency, reachable only through the
|
|
12
|
+
separate `dbgate-sqlite-dumper/better-sqlite3` entry point — the core never
|
|
13
|
+
imports a driver
|
|
14
|
+
- Streaming both ways: a multi-gigabyte database dumps, and a multi-gigabyte `.sql`
|
|
15
|
+
file restores, in constant memory
|
|
16
|
+
|
|
17
|
+
## Two-way native compatibility
|
|
18
|
+
|
|
19
|
+
Both directions are **proven by automated tests against the real `sqlite3` shell**,
|
|
20
|
+
not assumed:
|
|
21
|
+
|
|
22
|
+
- **Dumps produced by this library restore with the native shell.**
|
|
23
|
+
```sh
|
|
24
|
+
sqlite3 copy.db < dump.sql
|
|
25
|
+
```
|
|
26
|
+
- **Dumps produced by the native `.dump` restore with this library.**
|
|
27
|
+
```ts
|
|
28
|
+
await restoreSqlDump({ connection, source: createReadStream('native-dump.sql') });
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
There is no custom format, no archive wrapper, and no metadata sidecar. A `.sql` file
|
|
32
|
+
this package writes is **byte-identical** to `sqlite3 database.db .dump` — and to its
|
|
33
|
+
`--data-only`, `--preserve-rowids`, `--newlines` and `--nosys` variants — with one
|
|
34
|
+
exception that is a property of SQLite itself: the digits past the 17th in a `REAL`
|
|
35
|
+
literal come from SQLite's own `printf("%!.20g")`, which changed between releases, so a
|
|
36
|
+
driver bundling a different SQLite than the shell may spell the same double differently
|
|
37
|
+
(`0.100000000000000005` vs `0.1000000000000000056`). Both parse to the identical value;
|
|
38
|
+
the tests compare such lines as doubles and every other byte exactly. See
|
|
39
|
+
[docs/native-compatibility.md](docs/native-compatibility.md).
|
|
40
|
+
|
|
41
|
+
Every path in the matrix ends by reading back the restored database and deep-comparing
|
|
42
|
+
its schema _and_ every table's rows — text compared as bytes — against the source.
|
|
43
|
+
|
|
44
|
+
| Path | Tested |
|
|
45
|
+
| ------------------------------------------ | ------ |
|
|
46
|
+
| this library → native `sqlite3` restore | ✅ |
|
|
47
|
+
| native `.dump` → this library's restore | ✅ |
|
|
48
|
+
| this library → this library | ✅ |
|
|
49
|
+
| native `.dump` → native restore (baseline) | ✅ |
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npm install dbgate-sqlite-dumper
|
|
55
|
+
# optional, for the bundled better-sqlite3 adapter:
|
|
56
|
+
npm install better-sqlite3
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick start
|
|
60
|
+
|
|
61
|
+
### Dump
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { createWriteStream } from 'node:fs';
|
|
65
|
+
import { dumpSqlite } from 'dbgate-sqlite-dumper';
|
|
66
|
+
import { connectBetterSqlite3 } from 'dbgate-sqlite-dumper/better-sqlite3';
|
|
67
|
+
|
|
68
|
+
const { connection, close } = await connectBetterSqlite3('shop.db', { readonly: true });
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const result = await dumpSqlite(
|
|
72
|
+
connection,
|
|
73
|
+
{ mode: 'full' },
|
|
74
|
+
createWriteStream('shop.sql'),
|
|
75
|
+
event => console.log(event.phase, event.objectName ?? '', event.bytesWritten ?? ''),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
console.log(`${result.rowsExported} rows in ${result.statementsWritten} statements`);
|
|
79
|
+
for (const warning of result.warnings) {
|
|
80
|
+
console.warn(`[${warning.severity}] ${warning.code}: ${warning.message}`);
|
|
81
|
+
}
|
|
82
|
+
} finally {
|
|
83
|
+
await close();
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The result is restorable by `sqlite3 shop_copy.db < shop.sql`.
|
|
88
|
+
|
|
89
|
+
### Restore
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createReadStream } from 'node:fs';
|
|
93
|
+
import { restoreSqlDump } from 'dbgate-sqlite-dumper';
|
|
94
|
+
import { connectBetterSqlite3 } from 'dbgate-sqlite-dumper/better-sqlite3';
|
|
95
|
+
|
|
96
|
+
const { connection, close } = await connectBetterSqlite3('shop_copy.db');
|
|
97
|
+
|
|
98
|
+
const result = await restoreSqlDump({
|
|
99
|
+
connection,
|
|
100
|
+
source: createReadStream('shop.sql'),
|
|
101
|
+
progress: event => console.log(event.phase, event.currentObject, event.rowsRestored),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log(`${result.statementsExecuted} statements, ${result.rowsRestored} rows`);
|
|
105
|
+
for (const error of result.errors) {
|
|
106
|
+
console.error(
|
|
107
|
+
`statement ${error.statementIndex} (line ${error.location.startLine}): ${error.message}`,
|
|
108
|
+
);
|
|
109
|
+
console.error(` ${error.sqlPreview}`); // truncated, key-redacted
|
|
110
|
+
console.error(` code=${error.sqliteError?.code}`);
|
|
111
|
+
}
|
|
112
|
+
await close();
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`source` accepts a `string`, a `Buffer`, a `Readable`, or any `AsyncIterable` of text or
|
|
116
|
+
`Buffer` chunks. Input is parsed incrementally, so restoring a multi-gigabyte dump does not
|
|
117
|
+
read it into memory.
|
|
118
|
+
|
|
119
|
+
### Using a database you already have open
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import Database from 'better-sqlite3';
|
|
123
|
+
import { fromBetterSqlite3 } from 'dbgate-sqlite-dumper/better-sqlite3';
|
|
124
|
+
|
|
125
|
+
const connection = fromBetterSqlite3(new Database('shop.db'));
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A database you supply is **borrowed and never closed**. See
|
|
129
|
+
[docs/better-sqlite3-adapter.md](docs/better-sqlite3-adapter.md); any other driver works by
|
|
130
|
+
implementing the small `SqliteConnection` interface.
|
|
131
|
+
|
|
132
|
+
## Public API
|
|
133
|
+
|
|
134
|
+
| Function | Purpose |
|
|
135
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
136
|
+
| `dumpSqlite(connection, options, output, onProgress?, signal?)` | Full pipeline: snapshot → introspect → plan → render → stream rows |
|
|
137
|
+
| `restoreSqlDump({ connection, source, options?, progress?, signal? })` | Streaming parser → statements → database |
|
|
138
|
+
| `introspectSqlite(connection, options?, signal?)` | Normalized `SqliteDatabase` + version/capabilities/diagnostics |
|
|
139
|
+
| `inspectDumpArchive(database, options?)` | Pure planning → ordered, verified `ArchiveEntry[]` |
|
|
140
|
+
| `renderPlainSql(request)` | Pure model → plain SQL text (never touches the database) |
|
|
141
|
+
| `exportTableDataAsInserts(request)` | Stream one table's rows as `INSERT` statements |
|
|
142
|
+
| `preflightRestore(request)` | Target version, modules, conflicts, and what the dump needs |
|
|
143
|
+
| `isSqliteDump(sample)` | Recognizes native _and_ this package's dumps |
|
|
144
|
+
| `parseSqlStatements(sql)` / `streamSqlStatements(source)` | The shell-compatible statement splitter, usable on its own |
|
|
145
|
+
| `isCompleteStatement(sql)` | A port of `sqlite3_complete()` |
|
|
146
|
+
| `beginSqliteDumpSession(connection, options?)` | The read snapshot, on its own |
|
|
147
|
+
| `checkTargetCompatibility(database, target)` | Which features a target cannot accept |
|
|
148
|
+
| `fromBetterSqlite3(database)` | Adapter (from `dbgate-sqlite-dumper/better-sqlite3`) |
|
|
149
|
+
| `connectBetterSqlite3(filename, options?)` | Convenience opener (from `dbgate-sqlite-dumper/better-sqlite3`) |
|
|
150
|
+
|
|
151
|
+
Each stage is independently usable: `inspectDumpArchive` and `renderPlainSql` are pure
|
|
152
|
+
functions of the model and need no connection at all.
|
|
153
|
+
|
|
154
|
+
## Why a real parser, not `split(';')`
|
|
155
|
+
|
|
156
|
+
Splitting a SQLite script on semicolons breaks on the _first_ trigger in any dump:
|
|
157
|
+
|
|
158
|
+
```sql
|
|
159
|
+
CREATE TRIGGER audit_books AFTER INSERT ON books BEGIN
|
|
160
|
+
INSERT INTO audit VALUES ('book ' || new.title || '; END');
|
|
161
|
+
UPDATE authors SET books = books + 1 WHERE id = new.author_id;
|
|
162
|
+
END;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
This package splits a script exactly where the `sqlite3` shell does, by porting the two
|
|
166
|
+
pieces of SQLite that decide it: `sqlite3_complete()` — the state machine that keeps a
|
|
167
|
+
trigger body's inner statements together — and the shell's own line rules:
|
|
168
|
+
|
|
169
|
+
- **`GO` and `/` lines end a statement**, as in the shell (SQL Server and Oracle habits).
|
|
170
|
+
- **Dot-commands are the shell's, not SQL.** Presentational ones (`.mode`, `.headers`,
|
|
171
|
+
`.print`) are skipped with a warning; ones that change the database (`.read`,
|
|
172
|
+
`.import`, `.open`) are refused with a precise error rather than silently ignored.
|
|
173
|
+
- **A `\r` before a newline is dropped**, even inside a string literal — the shell's line
|
|
174
|
+
reader does that, so a CRLF file restores identically.
|
|
175
|
+
- **Bytes that are not valid UTF-8 survive.** The native `.dump` writes such text raw; a
|
|
176
|
+
JavaScript driver cannot pass it through a string, so the literal is rewritten to the
|
|
177
|
+
exact-byte `CAST(X'...' AS TEXT)`.
|
|
178
|
+
|
|
179
|
+
Boundary correctness is not assumed: the parser's output is asserted identical at **every**
|
|
180
|
+
chunk size and **every** single split point, and a suite of hand-written scripts is run
|
|
181
|
+
through both the shell and this package, which must leave identical databases.
|
|
182
|
+
|
|
183
|
+
## Documentation
|
|
184
|
+
|
|
185
|
+
| Document | Contents |
|
|
186
|
+
| ---------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
187
|
+
| [docs/native-compatibility.md](docs/native-compatibility.md) | The two-way promise, what is reproduced and why, native quirks |
|
|
188
|
+
| [docs/dump-api.md](docs/dump-api.md) | `dumpSqlite` options, modes, consistency, progress, batching |
|
|
189
|
+
| [docs/restore-api.md](docs/restore-api.md) | `restoreSqlDump`, the parser, session cleanup, errors, preflight |
|
|
190
|
+
| [docs/better-sqlite3-adapter.md](docs/better-sqlite3-adapter.md) | Ownership, defensive mode, the `SqliteConnection` contract |
|
|
191
|
+
| [docs/supported-objects.md](docs/supported-objects.md) | Object matrix: dumped / restored / round-trip tested |
|
|
192
|
+
| [docs/supported-data-types.md](docs/supported-data-types.md) | Per-storage-class fidelity and how each value is written |
|
|
193
|
+
| [docs/known-limitations.md](docs/known-limitations.md) | What this package does not do, and why |
|
|
194
|
+
| [docs/round-trip-testing.md](docs/round-trip-testing.md) | Running the interop suite; the fixture |
|
|
195
|
+
| [docs/architecture.md](docs/architecture.md) | Layer-by-layer design and the reasoning behind it |
|
|
196
|
+
|
|
197
|
+
## Fidelity highlights
|
|
198
|
+
|
|
199
|
+
- **64-bit integers are exact.** Every value is rendered to text by SQLite itself, so
|
|
200
|
+
`9223372036854775807` never passes through a JavaScript number.
|
|
201
|
+
- **`REAL` digits are SQLite's own**, from the same `printf` the shell uses.
|
|
202
|
+
- **Virtual tables come back byte-identical**, FTS5 indexes and R-trees included: like the
|
|
203
|
+
native `.dump`, the schema row is written directly and the shadow tables' contents are
|
|
204
|
+
copied, instead of rebuilding the index.
|
|
205
|
+
- **Text is bytes.** Invalid UTF-8 and embedded `NUL`s survive a round trip; the native
|
|
206
|
+
shell silently truncates text at the first `NUL`, this package does not.
|
|
207
|
+
- **Connections are left as they were found.** A native dump turns `foreign_keys` off and
|
|
208
|
+
never turns it back on; a restore that stops early leaves a transaction open. Both are
|
|
209
|
+
put back before the handle is returned.
|
|
210
|
+
- **Consistent under concurrent writes.** A dump reads every table inside one snapshot.
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
npm install
|
|
216
|
+
npm run typecheck
|
|
217
|
+
npm run lint
|
|
218
|
+
npm test # unit tests; better-sqlite3 in-process, nothing else needed
|
|
219
|
+
|
|
220
|
+
npm run test:integration # needs the sqlite3 shell on PATH (apt install sqlite3)
|
|
221
|
+
npm run test:package # builds, then smoke-tests dist/ as ESM and CJS
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Integration tests skip themselves with a clear message when the `sqlite3` shell is not
|
|
225
|
+
installed; set `SQLITE_TEST_REQUIRED=1` (as CI does) to make that a hard error.
|
|
226
|
+
`SQLITE3_BIN` selects a specific shell binary.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
GPL-3.0-only. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/better-sqlite3.ts
|
|
31
|
+
var better_sqlite3_exports = {};
|
|
32
|
+
__export(better_sqlite3_exports, {
|
|
33
|
+
connectBetterSqlite3: () => connectBetterSqlite3,
|
|
34
|
+
fromBetterSqlite3: () => fromBetterSqlite3
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(better_sqlite3_exports);
|
|
37
|
+
|
|
38
|
+
// src/utils/errors.ts
|
|
39
|
+
var SqliteDumperError = class extends Error {
|
|
40
|
+
code;
|
|
41
|
+
constructor(code, message, options) {
|
|
42
|
+
super(message, options);
|
|
43
|
+
this.name = "SqliteDumperError";
|
|
44
|
+
this.code = code;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var OperationCancelledError = class extends SqliteDumperError {
|
|
48
|
+
constructor(message = "The operation was cancelled") {
|
|
49
|
+
super("operation-cancelled", message);
|
|
50
|
+
this.name = "OperationCancelledError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function throwIfAborted(signal) {
|
|
54
|
+
if (signal?.aborted) {
|
|
55
|
+
throw new OperationCancelledError();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/better-sqlite3.ts
|
|
60
|
+
function toDriverParameters(parameters) {
|
|
61
|
+
return (parameters ?? []).map(
|
|
62
|
+
(value) => value instanceof Uint8Array && !Buffer.isBuffer(value) ? Buffer.from(value.buffer, value.byteOffset, value.byteLength) : value
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function prepare(database, sql) {
|
|
66
|
+
const statement = database.prepare(sql);
|
|
67
|
+
statement.safeIntegers(true);
|
|
68
|
+
return statement;
|
|
69
|
+
}
|
|
70
|
+
function isMultipleStatementsError(error) {
|
|
71
|
+
return error instanceof RangeError && /more than one statement/i.test(error.message);
|
|
72
|
+
}
|
|
73
|
+
function fromBetterSqlite3(database) {
|
|
74
|
+
const connection = {
|
|
75
|
+
async query(query, signal) {
|
|
76
|
+
throwIfAborted(signal);
|
|
77
|
+
const statement = prepare(database, query.sql);
|
|
78
|
+
const parameters = toDriverParameters(query.parameters);
|
|
79
|
+
if (statement.reader) {
|
|
80
|
+
const rows = statement.all(...parameters);
|
|
81
|
+
return { rows, columns: statement.columns().map((column) => column.name) };
|
|
82
|
+
}
|
|
83
|
+
statement.run(...parameters);
|
|
84
|
+
return { rows: [] };
|
|
85
|
+
},
|
|
86
|
+
stream(query, options) {
|
|
87
|
+
const signal = options?.signal;
|
|
88
|
+
return {
|
|
89
|
+
async *[Symbol.asyncIterator]() {
|
|
90
|
+
throwIfAborted(signal);
|
|
91
|
+
const statement = prepare(database, query.sql);
|
|
92
|
+
if (!statement.reader) {
|
|
93
|
+
statement.run(...toDriverParameters(query.parameters));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const iterator = statement.iterate(
|
|
97
|
+
...toDriverParameters(query.parameters)
|
|
98
|
+
);
|
|
99
|
+
try {
|
|
100
|
+
for (const row of iterator) {
|
|
101
|
+
if (signal?.aborted) {
|
|
102
|
+
throw new OperationCancelledError();
|
|
103
|
+
}
|
|
104
|
+
yield row;
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
iterator.return?.();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
async execute(sql, signal) {
|
|
113
|
+
throwIfAborted(signal);
|
|
114
|
+
let statement;
|
|
115
|
+
try {
|
|
116
|
+
statement = prepare(database, sql);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (!isMultipleStatementsError(error)) {
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
const before = totalChanges(database);
|
|
122
|
+
database.exec(sql);
|
|
123
|
+
return { changes: totalChanges(database) - before };
|
|
124
|
+
}
|
|
125
|
+
if (statement.reader) {
|
|
126
|
+
for (const _row of statement.iterate()) {
|
|
127
|
+
}
|
|
128
|
+
return { changes: 0 };
|
|
129
|
+
}
|
|
130
|
+
const info = statement.run();
|
|
131
|
+
return { changes: Number(info.changes) };
|
|
132
|
+
},
|
|
133
|
+
describeError(error) {
|
|
134
|
+
if (!(error instanceof Error)) {
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
const code = error.code;
|
|
138
|
+
return {
|
|
139
|
+
...typeof code === "string" ? { code } : {},
|
|
140
|
+
message: error.message
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
isInTransaction() {
|
|
144
|
+
return database.inTransaction;
|
|
145
|
+
},
|
|
146
|
+
async setDefensive(enabled) {
|
|
147
|
+
database.unsafeMode(!enabled);
|
|
148
|
+
},
|
|
149
|
+
async cancel() {
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return connection;
|
|
153
|
+
}
|
|
154
|
+
function totalChanges(database) {
|
|
155
|
+
return Number(database.prepare("SELECT total_changes()").pluck().get());
|
|
156
|
+
}
|
|
157
|
+
async function connectBetterSqlite3(filename, options) {
|
|
158
|
+
const module2 = await import("better-sqlite3");
|
|
159
|
+
const DatabaseConstructor = module2.default ?? module2;
|
|
160
|
+
const database = new DatabaseConstructor(filename, options);
|
|
161
|
+
let closed = false;
|
|
162
|
+
return {
|
|
163
|
+
connection: fromBetterSqlite3(database),
|
|
164
|
+
database,
|
|
165
|
+
close: async () => {
|
|
166
|
+
if (!closed) {
|
|
167
|
+
closed = true;
|
|
168
|
+
database.close();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
174
|
+
0 && (module.exports = {
|
|
175
|
+
connectBetterSqlite3,
|
|
176
|
+
fromBetterSqlite3
|
|
177
|
+
});
|
|
178
|
+
//# sourceMappingURL=better-sqlite3.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/better-sqlite3.ts","../src/utils/errors.ts"],"sourcesContent":["/**\r\n * Optional adapter for the `better-sqlite3` package.\r\n *\r\n * Wraps a caller-owned `better-sqlite3` `Database` as a\r\n * {@link SqliteConnection}. This module is never imported by the core\r\n * package; `better-sqlite3` is an optional peer dependency and is only\r\n * resolved when a consumer imports `dbgate-sqlite-dumper/better-sqlite3`\r\n * themselves.\r\n *\r\n * `better-sqlite3` is the driver DbGate's own SQLite plugin uses, which is\r\n * why it is the bundled adapter. Any other driver works through the same\r\n * {@link SqliteConnection} interface.\r\n */\r\nimport type BetterSqlite3 from 'better-sqlite3';\r\nimport type {\r\n SqliteConnection,\r\n SqliteErrorInfo,\r\n SqliteExecResult,\r\n SqliteParameterValue,\r\n SqliteQuery,\r\n SqliteQueryResult,\r\n SqliteRow,\r\n SqliteStreamOptions,\r\n} from './connection/types.js';\r\nimport { OperationCancelledError, throwIfAborted } from './utils/errors.js';\r\n\r\ntype Database = BetterSqlite3.Database;\r\ntype Statement = BetterSqlite3.Statement<unknown[], unknown>;\r\n\r\nfunction toDriverParameters(parameters: readonly SqliteParameterValue[] | undefined): unknown[] {\r\n return (parameters ?? []).map(value =>\r\n value instanceof Uint8Array && !Buffer.isBuffer(value)\r\n ? Buffer.from(value.buffer, value.byteOffset, value.byteLength)\r\n : value,\r\n );\r\n}\r\n\r\n/**\r\n * Prepares a statement with 64-bit integers returned as `bigint`, so no\r\n * catalog value is ever rounded. (Row data does not depend on this: the core\r\n * reads it through SQL that renders numbers as text — see `valueQuery.ts`.)\r\n */\r\nfunction prepare(database: Database, sql: string): Statement {\r\n const statement = database.prepare(sql) as Statement;\r\n statement.safeIntegers(true);\r\n return statement;\r\n}\r\n\r\nfunction isMultipleStatementsError(error: unknown): boolean {\r\n return error instanceof RangeError && /more than one statement/i.test(error.message);\r\n}\r\n\r\n/**\r\n * Wraps an open `better-sqlite3` database. The database is **borrowed**:\r\n * this package never closes it.\r\n *\r\n * `better-sqlite3` is synchronous, which shapes three behaviours:\r\n *\r\n * - `stream()` steps the statement one row at a time with `iterate()`,\r\n * yielding between rows, so a table of any size streams in constant\r\n * memory. While a stream is open the database cannot run another\r\n * statement — this package never asks it to.\r\n * - `cancel()` cannot interrupt a statement mid-step. Cancellation takes\r\n * effect between rows and between statements, which for a dump or a\r\n * restore (each statement is short) is prompt.\r\n * - `setDefensive()` is implemented with `unsafeMode()`, which is how\r\n * `better-sqlite3` exposes `SQLITE_DBCONFIG_DEFENSIVE`. (Unsafe mode also\r\n * relaxes the driver's own guard against writing while iterating; this\r\n * package never does that.)\r\n */\r\nexport function fromBetterSqlite3(database: Database): SqliteConnection {\r\n const connection: SqliteConnection = {\r\n async query<Row extends SqliteRow = SqliteRow>(\r\n query: SqliteQuery,\r\n signal?: AbortSignal,\r\n ): Promise<SqliteQueryResult<Row>> {\r\n throwIfAborted(signal);\r\n const statement = prepare(database, query.sql);\r\n const parameters = toDriverParameters(query.parameters);\r\n if (statement.reader) {\r\n const rows = statement.all(...parameters) as Row[];\r\n return { rows, columns: statement.columns().map(column => column.name) };\r\n }\r\n statement.run(...parameters);\r\n return { rows: [] };\r\n },\r\n\r\n stream<Row extends SqliteRow = SqliteRow>(\r\n query: SqliteQuery,\r\n options?: SqliteStreamOptions,\r\n ): AsyncIterable<Row> {\r\n const signal = options?.signal;\r\n return {\r\n async *[Symbol.asyncIterator](): AsyncGenerator<Row> {\r\n throwIfAborted(signal);\r\n const statement = prepare(database, query.sql);\r\n if (!statement.reader) {\r\n statement.run(...toDriverParameters(query.parameters));\r\n return;\r\n }\r\n const iterator = statement.iterate(\r\n ...toDriverParameters(query.parameters),\r\n ) as IterableIterator<Row>;\r\n try {\r\n for (const row of iterator) {\r\n if (signal?.aborted) {\r\n throw new OperationCancelledError();\r\n }\r\n yield row;\r\n }\r\n } finally {\r\n // Finalizes the statement even when the consumer stops early,\r\n // which is what frees the database for the next statement.\r\n iterator.return?.();\r\n }\r\n },\r\n };\r\n },\r\n\r\n async execute(sql: string, signal?: AbortSignal): Promise<SqliteExecResult> {\r\n throwIfAborted(signal);\r\n let statement: Statement;\r\n try {\r\n statement = prepare(database, sql);\r\n } catch (error) {\r\n if (!isMultipleStatementsError(error)) {\r\n throw error;\r\n }\r\n // Text the shell would run as several statements at once. Rare in a\r\n // restore (the parser splits statements), but valid input.\r\n const before = totalChanges(database);\r\n database.exec(sql);\r\n return { changes: totalChanges(database) - before };\r\n }\r\n if (statement.reader) {\r\n // A script's own SELECT or PRAGMA query: step it to completion, as\r\n // the shell would, without keeping the rows.\r\n for (const _row of statement.iterate()) {\r\n // discard\r\n }\r\n return { changes: 0 };\r\n }\r\n const info = statement.run();\r\n return { changes: Number(info.changes) };\r\n },\r\n\r\n describeError(error: unknown): SqliteErrorInfo | undefined {\r\n if (!(error instanceof Error)) {\r\n return undefined;\r\n }\r\n const code = (error as { code?: unknown }).code;\r\n return {\r\n ...(typeof code === 'string' ? { code } : {}),\r\n message: error.message,\r\n };\r\n },\r\n\r\n isInTransaction(): boolean {\r\n return database.inTransaction;\r\n },\r\n\r\n async setDefensive(enabled: boolean): Promise<void> {\r\n database.unsafeMode(!enabled);\r\n },\r\n\r\n async cancel(): Promise<void> {\r\n // better-sqlite3 runs each statement synchronously to completion;\r\n // there is nothing in flight to interrupt.\r\n },\r\n };\r\n return connection;\r\n}\r\n\r\nfunction totalChanges(database: Database): number {\r\n return Number(database.prepare('SELECT total_changes()').pluck().get());\r\n}\r\n\r\nexport interface ConnectedBetterSqlite3 {\r\n readonly connection: SqliteConnection;\r\n /** The underlying `better-sqlite3` database, for anything the adapter does not cover. */\r\n readonly database: Database;\r\n /** Closes the database. Idempotent. */\r\n close(): Promise<void>;\r\n}\r\n\r\n/**\r\n * Opens a database file with `better-sqlite3` and wraps it. Unlike\r\n * {@link fromBetterSqlite3}, the returned `close()` owns the handle.\r\n *\r\n * `better-sqlite3` is loaded lazily, so importing this module does not\r\n * require the optional peer dependency to be installed until this function\r\n * is actually called.\r\n */\r\nexport async function connectBetterSqlite3(\r\n filename: string,\r\n options?: BetterSqlite3.Options,\r\n): Promise<ConnectedBetterSqlite3> {\r\n const module = await import('better-sqlite3');\r\n const DatabaseConstructor = (module.default ?? module) as unknown as new (\r\n filename: string,\r\n options?: BetterSqlite3.Options,\r\n ) => Database;\r\n const database = new DatabaseConstructor(filename, options);\r\n let closed = false;\r\n return {\r\n connection: fromBetterSqlite3(database),\r\n database,\r\n close: async () => {\r\n if (!closed) {\r\n closed = true;\r\n database.close();\r\n }\r\n },\r\n };\r\n}\r\n","/** Base class for every error this package throws intentionally. */\r\nexport class SqliteDumperError extends Error {\r\n readonly code: string;\r\n\r\n constructor(code: string, message: string, options?: { cause?: unknown }) {\r\n super(message, options);\r\n this.name = 'SqliteDumperError';\r\n this.code = code;\r\n }\r\n}\r\n\r\n/** Thrown when an operation stops because its `AbortSignal` was triggered. */\r\nexport class OperationCancelledError extends SqliteDumperError {\r\n constructor(message = 'The operation was cancelled') {\r\n super('operation-cancelled', message);\r\n this.name = 'OperationCancelledError';\r\n }\r\n}\r\n\r\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\r\n if (signal?.aborted) {\r\n throw new OperationCancelledError();\r\n }\r\n}\r\n\r\n/**\r\n * True for both cancellation shapes this package can observe: its own\r\n * {@link OperationCancelledError} and the `DOMException` an `AbortSignal`\r\n * (or a Node stream aborted through one) raises.\r\n */\r\nexport function isAbortError(error: unknown): boolean {\r\n return (\r\n (error instanceof DOMException && error.name === 'AbortError') ||\r\n (error instanceof Error && error.name === 'OperationCancelledError')\r\n );\r\n}\r\n"],"mappings":"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","names":["module"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
import { S as SqliteConnection } from './types-CTyJTzB6.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Optional adapter for the `better-sqlite3` package.
|
|
6
|
+
*
|
|
7
|
+
* Wraps a caller-owned `better-sqlite3` `Database` as a
|
|
8
|
+
* {@link SqliteConnection}. This module is never imported by the core
|
|
9
|
+
* package; `better-sqlite3` is an optional peer dependency and is only
|
|
10
|
+
* resolved when a consumer imports `dbgate-sqlite-dumper/better-sqlite3`
|
|
11
|
+
* themselves.
|
|
12
|
+
*
|
|
13
|
+
* `better-sqlite3` is the driver DbGate's own SQLite plugin uses, which is
|
|
14
|
+
* why it is the bundled adapter. Any other driver works through the same
|
|
15
|
+
* {@link SqliteConnection} interface.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type Database = BetterSqlite3.Database;
|
|
19
|
+
/**
|
|
20
|
+
* Wraps an open `better-sqlite3` database. The database is **borrowed**:
|
|
21
|
+
* this package never closes it.
|
|
22
|
+
*
|
|
23
|
+
* `better-sqlite3` is synchronous, which shapes three behaviours:
|
|
24
|
+
*
|
|
25
|
+
* - `stream()` steps the statement one row at a time with `iterate()`,
|
|
26
|
+
* yielding between rows, so a table of any size streams in constant
|
|
27
|
+
* memory. While a stream is open the database cannot run another
|
|
28
|
+
* statement — this package never asks it to.
|
|
29
|
+
* - `cancel()` cannot interrupt a statement mid-step. Cancellation takes
|
|
30
|
+
* effect between rows and between statements, which for a dump or a
|
|
31
|
+
* restore (each statement is short) is prompt.
|
|
32
|
+
* - `setDefensive()` is implemented with `unsafeMode()`, which is how
|
|
33
|
+
* `better-sqlite3` exposes `SQLITE_DBCONFIG_DEFENSIVE`. (Unsafe mode also
|
|
34
|
+
* relaxes the driver's own guard against writing while iterating; this
|
|
35
|
+
* package never does that.)
|
|
36
|
+
*/
|
|
37
|
+
declare function fromBetterSqlite3(database: Database): SqliteConnection;
|
|
38
|
+
interface ConnectedBetterSqlite3 {
|
|
39
|
+
readonly connection: SqliteConnection;
|
|
40
|
+
/** The underlying `better-sqlite3` database, for anything the adapter does not cover. */
|
|
41
|
+
readonly database: Database;
|
|
42
|
+
/** Closes the database. Idempotent. */
|
|
43
|
+
close(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Opens a database file with `better-sqlite3` and wraps it. Unlike
|
|
47
|
+
* {@link fromBetterSqlite3}, the returned `close()` owns the handle.
|
|
48
|
+
*
|
|
49
|
+
* `better-sqlite3` is loaded lazily, so importing this module does not
|
|
50
|
+
* require the optional peer dependency to be installed until this function
|
|
51
|
+
* is actually called.
|
|
52
|
+
*/
|
|
53
|
+
declare function connectBetterSqlite3(filename: string, options?: BetterSqlite3.Options): Promise<ConnectedBetterSqlite3>;
|
|
54
|
+
|
|
55
|
+
export { type ConnectedBetterSqlite3, connectBetterSqlite3, fromBetterSqlite3 };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import BetterSqlite3 from 'better-sqlite3';
|
|
2
|
+
import { S as SqliteConnection } from './types-CTyJTzB6.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Optional adapter for the `better-sqlite3` package.
|
|
6
|
+
*
|
|
7
|
+
* Wraps a caller-owned `better-sqlite3` `Database` as a
|
|
8
|
+
* {@link SqliteConnection}. This module is never imported by the core
|
|
9
|
+
* package; `better-sqlite3` is an optional peer dependency and is only
|
|
10
|
+
* resolved when a consumer imports `dbgate-sqlite-dumper/better-sqlite3`
|
|
11
|
+
* themselves.
|
|
12
|
+
*
|
|
13
|
+
* `better-sqlite3` is the driver DbGate's own SQLite plugin uses, which is
|
|
14
|
+
* why it is the bundled adapter. Any other driver works through the same
|
|
15
|
+
* {@link SqliteConnection} interface.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
type Database = BetterSqlite3.Database;
|
|
19
|
+
/**
|
|
20
|
+
* Wraps an open `better-sqlite3` database. The database is **borrowed**:
|
|
21
|
+
* this package never closes it.
|
|
22
|
+
*
|
|
23
|
+
* `better-sqlite3` is synchronous, which shapes three behaviours:
|
|
24
|
+
*
|
|
25
|
+
* - `stream()` steps the statement one row at a time with `iterate()`,
|
|
26
|
+
* yielding between rows, so a table of any size streams in constant
|
|
27
|
+
* memory. While a stream is open the database cannot run another
|
|
28
|
+
* statement — this package never asks it to.
|
|
29
|
+
* - `cancel()` cannot interrupt a statement mid-step. Cancellation takes
|
|
30
|
+
* effect between rows and between statements, which for a dump or a
|
|
31
|
+
* restore (each statement is short) is prompt.
|
|
32
|
+
* - `setDefensive()` is implemented with `unsafeMode()`, which is how
|
|
33
|
+
* `better-sqlite3` exposes `SQLITE_DBCONFIG_DEFENSIVE`. (Unsafe mode also
|
|
34
|
+
* relaxes the driver's own guard against writing while iterating; this
|
|
35
|
+
* package never does that.)
|
|
36
|
+
*/
|
|
37
|
+
declare function fromBetterSqlite3(database: Database): SqliteConnection;
|
|
38
|
+
interface ConnectedBetterSqlite3 {
|
|
39
|
+
readonly connection: SqliteConnection;
|
|
40
|
+
/** The underlying `better-sqlite3` database, for anything the adapter does not cover. */
|
|
41
|
+
readonly database: Database;
|
|
42
|
+
/** Closes the database. Idempotent. */
|
|
43
|
+
close(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Opens a database file with `better-sqlite3` and wraps it. Unlike
|
|
47
|
+
* {@link fromBetterSqlite3}, the returned `close()` owns the handle.
|
|
48
|
+
*
|
|
49
|
+
* `better-sqlite3` is loaded lazily, so importing this module does not
|
|
50
|
+
* require the optional peer dependency to be installed until this function
|
|
51
|
+
* is actually called.
|
|
52
|
+
*/
|
|
53
|
+
declare function connectBetterSqlite3(filename: string, options?: BetterSqlite3.Options): Promise<ConnectedBetterSqlite3>;
|
|
54
|
+
|
|
55
|
+
export { type ConnectedBetterSqlite3, connectBetterSqlite3, fromBetterSqlite3 };
|