dbgate-sqlite-dumper 0.1.0 → 0.1.1

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.
@@ -1,88 +1,100 @@
1
- # Known limitations
2
-
3
- What this package does not do, and why. The items in the first section are also returned by
4
- `unsupportedFeatureDiagnostics()`, so a UI can list them without hardcoding the text.
5
-
6
- ## Not dumped
7
-
8
- ### File-level settings
9
-
10
- `page_size`, `auto_vacuum`, `journal_mode`, `encoding` and the like describe how the
11
- database _file_ is laid out, not what it contains, and most can only be chosen before the
12
- first table exists. The native `.dump` does not carry them either. Set them on the target
13
- before restoring if they matter; the dump restores correctly under any of them.
14
-
15
- `user_version` and `application_id` are the exception: they are carried with
16
- `render.includeDatabaseSettings`, and reported as a warning when they are non-zero and not
17
- carried.
18
-
19
- ### The temp schema and attached databases
20
-
21
- A dump covers one schema — `main` by default. Temporary objects belong to one connection;
22
- each attached database is a separate file. Dump an attached database on its own with
23
- `schemaName`.
24
-
25
- ### Application-defined functions, collations and modules
26
-
27
- User-defined SQL functions, collating sequences and virtual-table modules are registered by
28
- the application (or loaded as extensions), not stored in the database. Schema that uses
29
- them is dumped verbatim, and the restoring application must register them first.
30
- `checkTargetCompatibility` reports custom collations used by indexes, and — when the
31
- target's module list is known — missing virtual-table modules.
32
-
33
- ### Encryption
34
-
35
- An encrypted database (SQLCipher, SEE) dumps as plain SQL once it is opened with its key.
36
- The dump is not encrypted, and no key is ever written into it or into any diagnostic.
37
-
38
- ## Behaviours to know about
39
-
40
- ### `REAL` digits depend on the SQLite release
41
-
42
- See [native-compatibility.md](native-compatibility.md#the-one-exception-real-digit-tails).
43
- The value never changes; the spelling past the 17th digit can.
44
-
45
- ### `sqlite_stat4` across builds
46
-
47
- `sqlite_stat4` exists only on SQLite built with `SQLITE_ENABLE_STAT4` (such as
48
- `better-sqlite3`'s). A dump that carries its rows cannot be restored on a build without
49
- it — by the native `.dump` either — because the `ANALYZE sqlite_schema;` that prepares the
50
- table does not create it there. `preflightRestore` reports `statistics-table-unsupported`;
51
- dump with `objectKinds.includeSystemTables: false`, or drop the table first.
52
-
53
- Conversely, restoring through a STAT4 build creates an empty `sqlite_stat4` the source did
54
- not have. It is harmless, and `ANALYZE` repopulates it.
55
-
56
- ### Data-only dumps
57
-
58
- A data-only dump reproduces `.dump --data-only`, including two quirks that matter when
59
- loading the rows into an existing schema (virtual-table rows carried twice; system tables
60
- not prepared). [dump-api.md](dump-api.md#data-only-dumps) gives the option set that avoids
61
- both.
62
-
63
- ### Hidden rowids are renumbered by default
64
-
65
- As natively; see [supported-objects.md](supported-objects.md#hidden-rowids).
66
-
67
- ### Virtual-table dumps need defensive mode off to restore
68
-
69
- Inherent to how the native `.dump` recreates virtual tables. The restore lifts
70
- `SQLITE_DBCONFIG_DEFENSIVE` itself through adapters that can (the bundled one can); with an
71
- adapter that cannot, the restore succeeds only on a handle not in defensive mode.
72
-
73
- ### Cancellation granularity
74
-
75
- `better-sqlite3` runs each statement synchronously, so cancellation takes effect between
76
- rows and between statements, never inside one.
77
-
78
- ### Memory
79
-
80
- A dump holds one statement and a 64 KiB output buffer; a restore holds one statement's
81
- text. A single enormous value — a 500 MB blob — is held whole, once, because one row cannot
82
- be split.
83
-
84
- ### Scripts the parser refuses
85
-
86
- Dot-commands that change the database (`.read`, `.import`, `.open`, …), identifiers
87
- containing bytes that are not valid UTF-8, and input ending inside a string or quoted
88
- identifier. See [restore-api.md](restore-api.md#errors).
1
+ # Known limitations
2
+
3
+ What this package does not do, and why. The items in the first section are also returned by
4
+ `unsupportedFeatureDiagnostics()`, so a UI can list them without hardcoding the text.
5
+
6
+ ## Not dumped
7
+
8
+ ### File-level settings
9
+
10
+ `page_size`, `auto_vacuum`, `journal_mode`, `encoding` and the like describe how the
11
+ database _file_ is laid out, not what it contains, and most can only be chosen before the
12
+ first table exists. The native `.dump` does not carry them either. Set them on the target
13
+ before restoring if they matter; the dump restores correctly under any of them.
14
+
15
+ `user_version` and `application_id` are the exception: they are carried with
16
+ `render.includeDatabaseSettings`, and reported as a warning when they are non-zero and not
17
+ carried.
18
+
19
+ ### The temp schema and attached databases
20
+
21
+ A dump covers one schema — `main` by default. Temporary objects belong to one connection;
22
+ each attached database is a separate file. Dump an attached database on its own with
23
+ `schemaName`.
24
+
25
+ ### Application-defined functions, collations and modules
26
+
27
+ User-defined SQL functions, collating sequences and virtual-table modules are registered by
28
+ the application (or loaded as extensions), not stored in the database. Schema that uses
29
+ them is dumped verbatim, and the restoring application must register them first.
30
+ `checkTargetCompatibility` reports custom collations used by indexes, and — when the
31
+ target's module list is known — missing virtual-table modules.
32
+
33
+ ### Encryption
34
+
35
+ An encrypted database (SQLCipher, SEE) dumps as plain SQL once it is opened with its key.
36
+ The dump is not encrypted, and no key is ever written into it or into any diagnostic.
37
+
38
+ ### Restoring into Cloudflare D1
39
+
40
+ The D1 adapter dumps only. D1 refuses the transaction and `writable_schema` statements a
41
+ dump script contains; see [d1-adapter.md](d1-adapter.md) for loading a dump with
42
+ `wrangler`.
43
+
44
+ ## Behaviours to know about
45
+
46
+ ### A D1 dump is not a snapshot
47
+
48
+ D1 has no client transactions, so a dump of a database that is written to meanwhile may be
49
+ partly inconsistent. It is reported as `snapshot-unavailable`; see
50
+ [d1-adapter.md](d1-adapter.md#consistency).
51
+
52
+ ### `REAL` digits depend on the SQLite release
53
+
54
+ See [native-compatibility.md](native-compatibility.md#the-one-exception-real-digit-tails).
55
+ The value never changes; the spelling past the 17th digit can.
56
+
57
+ ### `sqlite_stat4` across builds
58
+
59
+ `sqlite_stat4` exists only on SQLite built with `SQLITE_ENABLE_STAT4` (such as
60
+ `better-sqlite3`'s). A dump that carries its rows cannot be restored on a build without
61
+ it — by the native `.dump` either — because the `ANALYZE sqlite_schema;` that prepares the
62
+ table does not create it there. `preflightRestore` reports `statistics-table-unsupported`;
63
+ dump with `objectKinds.includeSystemTables: false`, or drop the table first.
64
+
65
+ Conversely, restoring through a STAT4 build creates an empty `sqlite_stat4` the source did
66
+ not have. It is harmless, and `ANALYZE` repopulates it.
67
+
68
+ ### Data-only dumps
69
+
70
+ A data-only dump reproduces `.dump --data-only`, including two quirks that matter when
71
+ loading the rows into an existing schema (virtual-table rows carried twice; system tables
72
+ not prepared). [dump-api.md](dump-api.md#data-only-dumps) gives the option set that avoids
73
+ both.
74
+
75
+ ### Hidden rowids are renumbered by default
76
+
77
+ As natively; see [supported-objects.md](supported-objects.md#hidden-rowids).
78
+
79
+ ### Virtual-table dumps need defensive mode off to restore
80
+
81
+ Inherent to how the native `.dump` recreates virtual tables. The restore lifts
82
+ `SQLITE_DBCONFIG_DEFENSIVE` itself through adapters that can (the bundled one can); with an
83
+ adapter that cannot, the restore succeeds only on a handle not in defensive mode.
84
+
85
+ ### Cancellation granularity
86
+
87
+ `better-sqlite3` runs each statement synchronously, so cancellation takes effect between
88
+ rows and between statements, never inside one.
89
+
90
+ ### Memory
91
+
92
+ A dump holds one statement and a 64 KiB output buffer; a restore holds one statement's
93
+ text. A single enormous value — a 500 MB blob — is held whole, once, because one row cannot
94
+ be split.
95
+
96
+ ### Scripts the parser refuses
97
+
98
+ Dot-commands that change the database (`.read`, `.import`, `.open`, …), identifiers
99
+ containing bytes that are not valid UTF-8, and input ending inside a string or quoted
100
+ identifier. See [restore-api.md](restore-api.md#errors).
@@ -1,124 +1,124 @@
1
- # Native compatibility
2
-
3
- The promise, in both directions:
4
-
5
- 1. **A dump written by this package restores with the native shell**:
6
- `sqlite3 target.db < dump.sql`.
7
- 2. **A dump written by the native `.dump` restores with this package**:
8
- `restoreSqlDump({ connection, source })`.
9
-
10
- Both are tested on every run against the real `sqlite3` shell (3.45 on Ubuntu 24.04 in CI),
11
- ending with a deep comparison of the restored database's schema and every row against the
12
- source. See [round-trip-testing.md](round-trip-testing.md).
13
-
14
- ## Byte identity
15
-
16
- With default options, `dumpSqlite` writes the same bytes as `sqlite3 db .dump`, and each
17
- native switch has an equivalent that is tested the same way:
18
-
19
- | Native | This package |
20
- | ------------------------- | ------------------------------------------------- |
21
- | `.dump` | `{}` |
22
- | `.dump --data-only` | `{ mode: 'data-only' }` |
23
- | `.dump --preserve-rowids` | `{ dataExport: { preserveRowids: true } }` |
24
- | `.dump --newlines` | `{ dataExport: { rawNewlines: true } }` |
25
- | `.dump --nosys` | `{ objectKinds: { includeSystemTables: false } }` |
26
-
27
- ### The one exception: `REAL` digit tails
28
-
29
- The shell writes a non-integral `REAL` with `sqlite3_snprintf("%!.20g")`, and this package
30
- has SQLite evaluate `printf('%!.20g', value)` for it — the same formatter. But the digits
31
- that formatter produces past the 17th significant digit changed between SQLite releases
32
- (3.45 writes `0.100000000000000005`, 3.53 writes `0.1000000000000000056`). A driver that
33
- bundles its own SQLite — as `better-sqlite3` does — therefore writes the digits of _its_
34
- release.
35
-
36
- Those digits carry no information: 17 significant digits already identify a double
37
- exactly, and both spellings parse back to the identical value. The interop tests compare
38
- every line byte for byte, and compare a differing line again with each `REAL` literal
39
- parsed as a double; nothing else is allowed to differ. When the driver and the shell embed
40
- the same SQLite release, the output is byte-identical without exception.
41
-
42
- ## What is reproduced, and why
43
-
44
- Every rule below is ported from `shell.c`, because each one changes bytes in the output.
45
-
46
- - **The frame.** `PRAGMA foreign_keys=OFF;` and `BEGIN TRANSACTION;` first, `COMMIT;`
47
- last. A data-only dump has neither, as natively.
48
- - **Creation order.** Tables in `sqlite_schema` rowid order (`sqlite_sequence` moved last),
49
- each followed by its rows; then indexes, triggers and views in rowid order. Creation order
50
- is a valid dependency order for everything SQLite checks at `CREATE` time, and
51
- `foreign_keys=OFF` covers foreign keys, circular ones included.
52
- - **Verbatim DDL.** SQLite stores every object's `CREATE` statement exactly as written; that
53
- text is what is emitted, never a reconstruction.
54
- - **`printSchemaLine()`.** A table whose name is quoted with `'` or `"` becomes
55
- `CREATE TABLE IF NOT EXISTS`; DDL ending in a `--` comment or an unterminated `/*` gets a
56
- newline or `*/` before its `;`, chosen with `sqlite3_complete()` exactly as the shell does.
57
- - **Index, trigger and view terminators.** The statement's `;` goes on a line of its own if
58
- its text contains `--` anywhere.
59
- - **Identifier quoting.** Table and column names are quoted in `INSERT` only when not a
60
- plain identifier or when a keyword (`quoteChar()`, with SQLite's 147 keywords).
61
- - **Values.** Integers as decimal; `REAL` as `%lld.0` when integral and in range, else
62
- `%!.20g`, with infinities as `9.0e+999`; blobs as lower-case `X'...'`; text quoted with
63
- `'` doubled, and — by default — newlines and carriage returns rewritten as
64
- `replace('...\n...','\n',char(10))`, with a placeholder guaranteed not to occur in the
65
- text (`unused_string()`).
66
- - **Virtual tables.** Written as `INSERT INTO sqlite_schema(...)` under
67
- `PRAGMA writable_schema=ON`, not as `CREATE VIRTUAL TABLE`, so the module's constructor
68
- does not run and the shadow tables — which the dump then fills with the source's exact
69
- contents — are not rebuilt. The whole dump is preceded by
70
- `/* WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */`.
71
- - **System tables.** `DELETE FROM sqlite_sequence;` before the `AUTOINCREMENT` counters,
72
- `ANALYZE sqlite_schema;` before each statistics table's rows.
73
-
74
- ## Native quirks reproduced deliberately
75
-
76
- These are faithful to the shell rather than "fixed", because a dump that differs from the
77
- native one for no stated reason is harder to trust than one with a documented quirk. Each is
78
- reported as a warning when it applies.
79
-
80
- - **`.dump --data-only` selects virtual tables' rows twice** — through the table itself,
81
- and again as the shadow tables' rows — because the shell checks for data-only before it
82
- checks for a virtual table. Restoring such a dump into a database where the virtual table
83
- exists conflicts with its own shadow rows (`data-only-virtual-table`).
84
- - **`.dump --data-only` inserts into system tables without preparing them**: the
85
- `sqlite_sequence` rows are not preceded by a `DELETE`, and the statistics rows by no
86
- `ANALYZE` (`data-only-system-table`).
87
- - **`-0.0` is written as `0.0`**, because the shell's integral test is true for it.
88
-
89
- [dump-api.md](dump-api.md#data-only-dumps) gives the option set that avoids the first two
90
- when loading rows into an existing schema.
91
-
92
- ## Deliberate deviations
93
-
94
- Only where the native behaviour loses data, and never for data the shell can represent:
95
-
96
- - **`NUL` characters in text are kept.** The shell handles values as C strings and drops
97
- everything after the first `NUL`; this package writes `'a'||char(0)||'b'`, which the
98
- shell restores correctly. Text without `NUL` — all text in practice — is unaffected.
99
- - **A schema-only dump** has no native `.dump` equivalent. It creates virtual tables with
100
- their own `CREATE VIRTUAL TABLE` (as `.schema` shows them) and leaves their shadow tables
101
- to the module, since no shadow rows follow.
102
- - **Partial dumps** (a `selection`) keep only the selected tables' `sqlite_sequence` and
103
- statistics rows, and reset only their counters
104
- (`DELETE FROM sqlite_sequence WHERE lower(name) IN (...)`). The native `.dump PATTERN`
105
- omits the counters of the selected tables entirely.
106
-
107
- ## Restoring native dumps
108
-
109
- The restore side reproduces how the shell _reads_ a script, which is what makes any
110
- `sqlite3` script restorable, not just `.dump` output:
111
-
112
- - statement boundaries from `sqlite3_complete()`, so trigger bodies stay whole;
113
- - `GO` and `/` lines as terminators, `#` lines at column 0 as comments, dot-commands at
114
- column 0 as shell commands;
115
- - a `\r` immediately before `\n` dropped everywhere, as the shell's line reader does;
116
- - a leading UTF-8 byte-order mark ignored;
117
- - a final statement without `;` still executed.
118
-
119
- And it accounts for running inside an application instead of a process that exits:
120
-
121
- - the defensive mode a virtual-table dump requires is lifted for the restore and put back;
122
- - the schema is reloaded afterwards, so restored virtual tables work on the same handle;
123
- - `foreign_keys` is put back to its previous value (a native dump never re-enables it);
124
- - a transaction left open by a failing or truncated script is rolled back.
1
+ # Native compatibility
2
+
3
+ The promise, in both directions:
4
+
5
+ 1. **A dump written by this package restores with the native shell**:
6
+ `sqlite3 target.db < dump.sql`.
7
+ 2. **A dump written by the native `.dump` restores with this package**:
8
+ `restoreSqlDump({ connection, source })`.
9
+
10
+ Both are tested on every run against the real `sqlite3` shell (3.45 on Ubuntu 24.04 in CI),
11
+ ending with a deep comparison of the restored database's schema and every row against the
12
+ source. See [round-trip-testing.md](round-trip-testing.md).
13
+
14
+ ## Byte identity
15
+
16
+ With default options, `dumpSqlite` writes the same bytes as `sqlite3 db .dump`, and each
17
+ native switch has an equivalent that is tested the same way:
18
+
19
+ | Native | This package |
20
+ | ------------------------- | ------------------------------------------------- |
21
+ | `.dump` | `{}` |
22
+ | `.dump --data-only` | `{ mode: 'data-only' }` |
23
+ | `.dump --preserve-rowids` | `{ dataExport: { preserveRowids: true } }` |
24
+ | `.dump --newlines` | `{ dataExport: { rawNewlines: true } }` |
25
+ | `.dump --nosys` | `{ objectKinds: { includeSystemTables: false } }` |
26
+
27
+ ### The one exception: `REAL` digit tails
28
+
29
+ The shell writes a non-integral `REAL` with `sqlite3_snprintf("%!.20g")`, and this package
30
+ has SQLite evaluate `printf('%!.20g', value)` for it — the same formatter. But the digits
31
+ that formatter produces past the 17th significant digit changed between SQLite releases
32
+ (3.45 writes `0.100000000000000005`, 3.53 writes `0.1000000000000000056`). A driver that
33
+ bundles its own SQLite — as `better-sqlite3` does — therefore writes the digits of _its_
34
+ release.
35
+
36
+ Those digits carry no information: 17 significant digits already identify a double
37
+ exactly, and both spellings parse back to the identical value. The interop tests compare
38
+ every line byte for byte, and compare a differing line again with each `REAL` literal
39
+ parsed as a double; nothing else is allowed to differ. When the driver and the shell embed
40
+ the same SQLite release, the output is byte-identical without exception.
41
+
42
+ ## What is reproduced, and why
43
+
44
+ Every rule below is ported from `shell.c`, because each one changes bytes in the output.
45
+
46
+ - **The frame.** `PRAGMA foreign_keys=OFF;` and `BEGIN TRANSACTION;` first, `COMMIT;`
47
+ last. A data-only dump has neither, as natively.
48
+ - **Creation order.** Tables in `sqlite_schema` rowid order (`sqlite_sequence` moved last),
49
+ each followed by its rows; then indexes, triggers and views in rowid order. Creation order
50
+ is a valid dependency order for everything SQLite checks at `CREATE` time, and
51
+ `foreign_keys=OFF` covers foreign keys, circular ones included.
52
+ - **Verbatim DDL.** SQLite stores every object's `CREATE` statement exactly as written; that
53
+ text is what is emitted, never a reconstruction.
54
+ - **`printSchemaLine()`.** A table whose name is quoted with `'` or `"` becomes
55
+ `CREATE TABLE IF NOT EXISTS`; DDL ending in a `--` comment or an unterminated `/*` gets a
56
+ newline or `*/` before its `;`, chosen with `sqlite3_complete()` exactly as the shell does.
57
+ - **Index, trigger and view terminators.** The statement's `;` goes on a line of its own if
58
+ its text contains `--` anywhere.
59
+ - **Identifier quoting.** Table and column names are quoted in `INSERT` only when not a
60
+ plain identifier or when a keyword (`quoteChar()`, with SQLite's 147 keywords).
61
+ - **Values.** Integers as decimal; `REAL` as `%lld.0` when integral and in range, else
62
+ `%!.20g`, with infinities as `9.0e+999`; blobs as lower-case `X'...'`; text quoted with
63
+ `'` doubled, and — by default — newlines and carriage returns rewritten as
64
+ `replace('...\n...','\n',char(10))`, with a placeholder guaranteed not to occur in the
65
+ text (`unused_string()`).
66
+ - **Virtual tables.** Written as `INSERT INTO sqlite_schema(...)` under
67
+ `PRAGMA writable_schema=ON`, not as `CREATE VIRTUAL TABLE`, so the module's constructor
68
+ does not run and the shadow tables — which the dump then fills with the source's exact
69
+ contents — are not rebuilt. The whole dump is preceded by
70
+ `/* WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */`.
71
+ - **System tables.** `DELETE FROM sqlite_sequence;` before the `AUTOINCREMENT` counters,
72
+ `ANALYZE sqlite_schema;` before each statistics table's rows.
73
+
74
+ ## Native quirks reproduced deliberately
75
+
76
+ These are faithful to the shell rather than "fixed", because a dump that differs from the
77
+ native one for no stated reason is harder to trust than one with a documented quirk. Each is
78
+ reported as a warning when it applies.
79
+
80
+ - **`.dump --data-only` selects virtual tables' rows twice** — through the table itself,
81
+ and again as the shadow tables' rows — because the shell checks for data-only before it
82
+ checks for a virtual table. Restoring such a dump into a database where the virtual table
83
+ exists conflicts with its own shadow rows (`data-only-virtual-table`).
84
+ - **`.dump --data-only` inserts into system tables without preparing them**: the
85
+ `sqlite_sequence` rows are not preceded by a `DELETE`, and the statistics rows by no
86
+ `ANALYZE` (`data-only-system-table`).
87
+ - **`-0.0` is written as `0.0`**, because the shell's integral test is true for it.
88
+
89
+ [dump-api.md](dump-api.md#data-only-dumps) gives the option set that avoids the first two
90
+ when loading rows into an existing schema.
91
+
92
+ ## Deliberate deviations
93
+
94
+ Only where the native behaviour loses data, and never for data the shell can represent:
95
+
96
+ - **`NUL` characters in text are kept.** The shell handles values as C strings and drops
97
+ everything after the first `NUL`; this package writes `'a'||char(0)||'b'`, which the
98
+ shell restores correctly. Text without `NUL` — all text in practice — is unaffected.
99
+ - **A schema-only dump** has no native `.dump` equivalent. It creates virtual tables with
100
+ their own `CREATE VIRTUAL TABLE` (as `.schema` shows them) and leaves their shadow tables
101
+ to the module, since no shadow rows follow.
102
+ - **Partial dumps** (a `selection`) keep only the selected tables' `sqlite_sequence` and
103
+ statistics rows, and reset only their counters
104
+ (`DELETE FROM sqlite_sequence WHERE lower(name) IN (...)`). The native `.dump PATTERN`
105
+ omits the counters of the selected tables entirely.
106
+
107
+ ## Restoring native dumps
108
+
109
+ The restore side reproduces how the shell _reads_ a script, which is what makes any
110
+ `sqlite3` script restorable, not just `.dump` output:
111
+
112
+ - statement boundaries from `sqlite3_complete()`, so trigger bodies stay whole;
113
+ - `GO` and `/` lines as terminators, `#` lines at column 0 as comments, dot-commands at
114
+ column 0 as shell commands;
115
+ - a `\r` immediately before `\n` dropped everywhere, as the shell's line reader does;
116
+ - a leading UTF-8 byte-order mark ignored;
117
+ - a final statement without `;` still executed.
118
+
119
+ And it accounts for running inside an application instead of a process that exits:
120
+
121
+ - the defensive mode a virtual-table dump requires is lifted for the restore and put back;
122
+ - the schema is reloaded afterwards, so restored virtual tables work on the same handle;
123
+ - `foreign_keys` is put back to its previous value (a native dump never re-enables it);
124
+ - a transaction left open by a failing or truncated script is rolled back.