@quereus/store 4.5.0 → 4.7.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/README.md +34 -6
- package/dist/src/common/encoding.d.ts +4 -3
- package/dist/src/common/encoding.d.ts.map +1 -1
- package/dist/src/common/encoding.js +17 -13
- package/dist/src/common/encoding.js.map +1 -1
- package/dist/src/common/events.d.ts +31 -25
- package/dist/src/common/events.d.ts.map +1 -1
- package/dist/src/common/events.js +38 -39
- package/dist/src/common/events.js.map +1 -1
- package/dist/src/common/implicit-unique-index.d.ts +80 -0
- package/dist/src/common/implicit-unique-index.d.ts.map +1 -0
- package/dist/src/common/implicit-unique-index.js +148 -0
- package/dist/src/common/implicit-unique-index.js.map +1 -0
- package/dist/src/common/index.d.ts +7 -3
- package/dist/src/common/index.d.ts.map +1 -1
- package/dist/src/common/index.js +5 -2
- package/dist/src/common/index.js.map +1 -1
- package/dist/src/common/json-key.d.ts +30 -2
- package/dist/src/common/json-key.d.ts.map +1 -1
- package/dist/src/common/json-key.js +49 -2
- package/dist/src/common/json-key.js.map +1 -1
- package/dist/src/common/key-builder.d.ts +40 -20
- package/dist/src/common/key-builder.d.ts.map +1 -1
- package/dist/src/common/key-builder.js +31 -23
- package/dist/src/common/key-builder.js.map +1 -1
- package/dist/src/common/kv-store.d.ts +10 -0
- package/dist/src/common/kv-store.d.ts.map +1 -1
- package/dist/src/common/pk-key-resolution.d.ts +348 -0
- package/dist/src/common/pk-key-resolution.d.ts.map +1 -0
- package/dist/src/common/pk-key-resolution.js +481 -0
- package/dist/src/common/pk-key-resolution.js.map +1 -0
- package/dist/src/common/store-module-access-plan.d.ts +32 -0
- package/dist/src/common/store-module-access-plan.d.ts.map +1 -0
- package/dist/src/common/store-module-access-plan.js +428 -0
- package/dist/src/common/store-module-access-plan.js.map +1 -0
- package/dist/src/common/store-module-alter-column.d.ts +23 -0
- package/dist/src/common/store-module-alter-column.d.ts.map +1 -0
- package/dist/src/common/store-module-alter-column.js +389 -0
- package/dist/src/common/store-module-alter-column.js.map +1 -0
- package/dist/src/common/store-module-alter.d.ts +55 -0
- package/dist/src/common/store-module-alter.d.ts.map +1 -0
- package/dist/src/common/store-module-alter.js +504 -0
- package/dist/src/common/store-module-alter.js.map +1 -0
- package/dist/src/common/store-module-base.d.ts +268 -0
- package/dist/src/common/store-module-base.d.ts.map +1 -0
- package/dist/src/common/store-module-base.js +362 -0
- package/dist/src/common/store-module-base.js.map +1 -0
- package/dist/src/common/store-module-catalog.d.ts +269 -0
- package/dist/src/common/store-module-catalog.d.ts.map +1 -0
- package/dist/src/common/store-module-catalog.js +462 -0
- package/dist/src/common/store-module-catalog.js.map +1 -0
- package/dist/src/common/store-module-index-build.d.ts +88 -0
- package/dist/src/common/store-module-index-build.d.ts.map +1 -0
- package/dist/src/common/store-module-index-build.js +280 -0
- package/dist/src/common/store-module-index-build.js.map +1 -0
- package/dist/src/common/store-module-index.d.ts +116 -0
- package/dist/src/common/store-module-index.d.ts.map +1 -0
- package/dist/src/common/store-module-index.js +373 -0
- package/dist/src/common/store-module-index.js.map +1 -0
- package/dist/src/common/store-module-rename.d.ts +49 -0
- package/dist/src/common/store-module-rename.d.ts.map +1 -0
- package/dist/src/common/store-module-rename.js +268 -0
- package/dist/src/common/store-module-rename.js.map +1 -0
- package/dist/src/common/store-module-schema-rewrite.d.ts +57 -0
- package/dist/src/common/store-module-schema-rewrite.d.ts.map +1 -0
- package/dist/src/common/store-module-schema-rewrite.js +140 -0
- package/dist/src/common/store-module-schema-rewrite.js.map +1 -0
- package/dist/src/common/store-module-schema-sync.d.ts +232 -0
- package/dist/src/common/store-module-schema-sync.d.ts.map +1 -0
- package/dist/src/common/store-module-schema-sync.js +529 -0
- package/dist/src/common/store-module-schema-sync.js.map +1 -0
- package/dist/src/common/store-module.d.ts +43 -909
- package/dist/src/common/store-module.d.ts.map +1 -1
- package/dist/src/common/store-module.js +184 -3276
- package/dist/src/common/store-module.js.map +1 -1
- package/dist/src/common/store-table-base.d.ts +539 -0
- package/dist/src/common/store-table-base.d.ts.map +1 -0
- package/dist/src/common/store-table-base.js +900 -0
- package/dist/src/common/store-table-base.js.map +1 -0
- package/dist/src/common/store-table-constraints.d.ts +237 -0
- package/dist/src/common/store-table-constraints.d.ts.map +1 -0
- package/dist/src/common/store-table-constraints.js +624 -0
- package/dist/src/common/store-table-constraints.js.map +1 -0
- package/dist/src/common/store-table-scan.d.ts +452 -0
- package/dist/src/common/store-table-scan.d.ts.map +1 -0
- package/dist/src/common/store-table-scan.js +901 -0
- package/dist/src/common/store-table-scan.js.map +1 -0
- package/dist/src/common/store-table.d.ts +71 -1144
- package/dist/src/common/store-table.d.ts.map +1 -1
- package/dist/src/common/store-table.js +168 -2385
- package/dist/src/common/store-table.js.map +1 -1
- package/dist/src/testing/index.d.ts +12 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +12 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/kv-assert.d.ts +19 -0
- package/dist/src/testing/kv-assert.d.ts.map +1 -0
- package/dist/src/testing/kv-assert.js +25 -0
- package/dist/src/testing/kv-assert.js.map +1 -0
- package/dist/src/testing/kv-conformance.d.ts.map +1 -1
- package/dist/src/testing/kv-conformance.js +1 -15
- package/dist/src/testing/kv-conformance.js.map +1 -1
- package/dist/src/testing/kv-provider-conformance.d.ts +54 -0
- package/dist/src/testing/kv-provider-conformance.d.ts.map +1 -0
- package/dist/src/testing/kv-provider-conformance.js +183 -0
- package/dist/src/testing/kv-provider-conformance.js.map +1 -0
- package/package.json +8 -8
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ALTER TABLE ... RENAME TO: the two-phase table rename. The first phase relocates the
|
|
3
|
+
* physical data / index / stats stores and writes the new catalog entry while leaving
|
|
4
|
+
* the old entry in place; the second, called by the engine once every dependent object
|
|
5
|
+
* has been rewritten, drains those dependent catalog writes and only then drops it.
|
|
6
|
+
*
|
|
7
|
+
* Seventh layer of the store-module chain:
|
|
8
|
+
* StoreModuleBase -> StoreModuleCatalog -> StoreModuleSchemaSync -> StoreModuleIndex
|
|
9
|
+
* -> StoreModuleAlterColumn -> StoreModuleAlter -> StoreModuleRename -> StoreModule
|
|
10
|
+
*/
|
|
11
|
+
import { QuereusError, StatusCode, renameTableInCheckConstraints, renameTableInColumnExpressions, renameTableInIndexPredicates, } from '@quereus/quereus';
|
|
12
|
+
import { StoreConnection } from './store-connection.js';
|
|
13
|
+
import { buildDataStoreName, buildIndexStoreName, buildStatsKey } from './key-builder.js';
|
|
14
|
+
import { StoreModuleAlter } from './store-module-alter.js';
|
|
15
|
+
import { collectOccupiedStoreNames } from './store-module-base.js';
|
|
16
|
+
import { retargetSelfForeignKeys } from './store-module-schema-rewrite.js';
|
|
17
|
+
export class StoreModuleRename extends StoreModuleAlter {
|
|
18
|
+
/**
|
|
19
|
+
* Rename a store-backed table.
|
|
20
|
+
*
|
|
21
|
+
* Drops every in-memory reference to the old name (so the coordinator, open
|
|
22
|
+
* handles, and cached StoreTable instance don't linger with stale paths),
|
|
23
|
+
* delegates physical storage relocation to the provider, then rewrites the
|
|
24
|
+
* persistent catalog DDL under the new key. After this returns, the next
|
|
25
|
+
* access to `newName` will reconnect via `connect()` and open fresh stores
|
|
26
|
+
* against the moved directories.
|
|
27
|
+
*/
|
|
28
|
+
async renameTable(db, schemaName, oldName, newName, ddl) {
|
|
29
|
+
const oldKey = `${schemaName}.${oldName}`.toLowerCase();
|
|
30
|
+
const newKey = `${schemaName}.${newName}`.toLowerCase();
|
|
31
|
+
if (this.tables.has(newKey)) {
|
|
32
|
+
throw new QuereusError(`Store table '${newName}' already exists in schema '${schemaName}'`, StatusCode.ERROR);
|
|
33
|
+
}
|
|
34
|
+
// Capture the current schema BEFORE the guard (and before we drop in-memory
|
|
35
|
+
// references): the guard needs the index list to compute every relocated
|
|
36
|
+
// store name, and the new catalog DDL must reflect the real column set.
|
|
37
|
+
const existing = this.tables.get(oldKey);
|
|
38
|
+
const currentSchema = existing?.getSchema() ?? db.schemaManager.getTable(schemaName, oldName);
|
|
39
|
+
// Authoritative index list (exact store names): the provider relocates
|
|
40
|
+
// exactly these index stores instead of prefix-scanning `{oldName}_idx_`,
|
|
41
|
+
// which would also catch a sibling table named `{oldName}_idx_<x>`.
|
|
42
|
+
// MATERIALIZED, so the hidden `_uc_*` store realizing a plain UNIQUE moves with
|
|
43
|
+
// the table — otherwise the renamed table seeks a fresh EMPTY `_uc_*` and silently
|
|
44
|
+
// accepts a duplicate of a pre-rename row. `currentSchema` itself stays
|
|
45
|
+
// non-materialized: the catalog DDL rewritten below must carry no `_uc_*`.
|
|
46
|
+
const indexNames = this.materializedIndexNames(existing, currentSchema);
|
|
47
|
+
// Reject when ANY physical name the rename introduces — the new data store
|
|
48
|
+
// AND each relocated index store `{schema}.{newName}_idx_{x}` — already
|
|
49
|
+
// names an existing store. E.g. rename some table to `q_idx_archive` while
|
|
50
|
+
// table `q` has index `archive` (both → `{schema}.q_idx_archive`); or rename
|
|
51
|
+
// `t`→`u` while `t` has index `x` and a sibling table is literally named
|
|
52
|
+
// `u_idx_x`, which would relocate `t`'s index onto the sibling's data store.
|
|
53
|
+
// The renamed table's own current stores stay in the occupied set (see
|
|
54
|
+
// collectOccupiedStoreNames): an introduced name can only equal an own store
|
|
55
|
+
// in a footprint-swap rename providers cannot relocate safely. All checks
|
|
56
|
+
// run before the FIRST side effect (the coordinator commit, disconnect, and
|
|
57
|
+
// cache evictions below, then the physical relocation) so a colliding
|
|
58
|
+
// rename is a clean no-op.
|
|
59
|
+
const occupied = collectOccupiedStoreNames(this.tables.values(), db, schemaName, this);
|
|
60
|
+
this.assertStoreNameFree(db, schemaName, buildDataStoreName(schemaName, newName), `data store of table '${schemaName}.${newName}' (rename target)`, occupied);
|
|
61
|
+
for (const indexName of indexNames) {
|
|
62
|
+
this.assertStoreNameFree(db, schemaName, buildIndexStoreName(schemaName, newName, indexName), `index store of index '${indexName}' on table '${schemaName}.${newName}' (rename target)`, occupied);
|
|
63
|
+
}
|
|
64
|
+
// Flush buffered writes before the old store's handle is closed: once the
|
|
65
|
+
// on-disk directory moves, prior buffered ops address stores that no longer
|
|
66
|
+
// exist under those names. See `StoreModuleBase.ddlCommitPendingOps`.
|
|
67
|
+
await this.ddlCommitPendingOps();
|
|
68
|
+
// Hard-dispose the evicted handle: flush any lazy stats it was buffering AND
|
|
69
|
+
// deregister its coordinator stats-callback pair (the renamed instance is
|
|
70
|
+
// gone after this — the next connect()/getOrReconnectTable mints a fresh one
|
|
71
|
+
// that re-registers against the shared coordinator). Dispose failures must
|
|
72
|
+
// not block the physical rename.
|
|
73
|
+
if (existing) {
|
|
74
|
+
try {
|
|
75
|
+
await existing.dispose();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* ignore — physical rename must proceed */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
this.tables.delete(oldKey);
|
|
82
|
+
this.stores.delete(oldKey);
|
|
83
|
+
// The coordinator is module-wide (flushed above); it is not per-table, so
|
|
84
|
+
// it is not evicted here.
|
|
85
|
+
// Evict the disposed instance's registered engine connections — but ONLY the
|
|
86
|
+
// ones this module created. Unlike drop — where the engine's schema manager
|
|
87
|
+
// calls `removeConnectionsForTable` for us — the generic rename path
|
|
88
|
+
// (`alter-table.ts` renameTableImpl) does NOT, so the store must evict here or
|
|
89
|
+
// a StoreConnection leaks one per rename. Safe for `StoreConnection`s (both the
|
|
90
|
+
// StoreTable-owned DML connection and the StoreBackingHost-owned one): they hold
|
|
91
|
+
// no state of their own, delegating to the module-wide coordinator that the
|
|
92
|
+
// DDL-commit above already flushed, and their owning StoreTable is now disposed.
|
|
93
|
+
//
|
|
94
|
+
// A blanket name-keyed sweep is NOT safe: a wrapping module (the isolation layer)
|
|
95
|
+
// registers its own connection under the same qualified name, and that connection
|
|
96
|
+
// is the only thing that drives its staged overlay to storage at COMMIT. Evicting
|
|
97
|
+
// it silently drops the transaction's writes.
|
|
98
|
+
//
|
|
99
|
+
// NOTE: a rename onto a never-before-used name leaves the wrapper's connection
|
|
100
|
+
// registered under the stale old name (it is not retargeted here). Benign — the
|
|
101
|
+
// commit flush resolves overlays db-wide by their re-keyed names — but a workload
|
|
102
|
+
// that renames one table through many distinct names in a single process
|
|
103
|
+
// accumulates one such connection per name. If that ever matters, retarget the
|
|
104
|
+
// connection's `tableName` across the rename instead of leaving it stale.
|
|
105
|
+
const oldQualified = `${schemaName}.${oldName}`.toLowerCase();
|
|
106
|
+
for (const conn of db.getAllConnections()) {
|
|
107
|
+
if (conn instanceof StoreConnection && conn.tableName.toLowerCase() === oldQualified) {
|
|
108
|
+
db.removeConnection(conn.connectionId);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Move physical storage (data directory + index directories).
|
|
112
|
+
//
|
|
113
|
+
// NOTE: the relocation runs BEFORE the catalog rewrite below, and nothing undoes it.
|
|
114
|
+
// Any failure after this point — an IO error, or the DDL-text guard in
|
|
115
|
+
// `encodeCatalogDDL` firing on an unpaired surrogate in a column name or a `default`
|
|
116
|
+
// literal — strands the rows under the NEW physical name while the catalog still
|
|
117
|
+
// names the old table, so the table reads as empty with only the raised error as a
|
|
118
|
+
// clue. Harmless for today's validation cases: the store-name guard in
|
|
119
|
+
// `buildDataStoreName` (called above, before any side effect) refuses a bad target
|
|
120
|
+
// name outright, and a table whose DDL text is unpersistable can never have held
|
|
121
|
+
// rows in the first place. If a new post-relocation failure mode appears, the
|
|
122
|
+
// relocation must be undone here or deferred until after the catalog write.
|
|
123
|
+
if (this.provider.renameTableStores) {
|
|
124
|
+
await this.provider.renameTableStores(schemaName, oldName, newName, indexNames);
|
|
125
|
+
}
|
|
126
|
+
// Rewrite persistent catalog under the new name. Write the new DDL first
|
|
127
|
+
// so a crash mid-rename leaves the table discoverable under at least one
|
|
128
|
+
// name rather than neither.
|
|
129
|
+
if (currentSchema) {
|
|
130
|
+
const renamedSchema = {
|
|
131
|
+
...currentSchema,
|
|
132
|
+
name: newName,
|
|
133
|
+
// A self-referencing FK is persisted as `references <oldName>(...)`; after
|
|
134
|
+
// `removeTableDDL` below, that names a table no longer in the catalog. Pure
|
|
135
|
+
// copy — no rollback needed, `renamedSchema` is local to the write.
|
|
136
|
+
foreignKeys: retargetSelfForeignKeys(currentSchema.foreignKeys, schemaName, oldName, newName),
|
|
137
|
+
};
|
|
138
|
+
// A partial index's WHERE clause, a CHECK constraint's expression, and a column's
|
|
139
|
+
// DEFAULT / `generated always as` body can each name the OLD table — a
|
|
140
|
+
// table-qualified self-reference (`where t.b > 0`), or, for a column expression,
|
|
141
|
+
// a subquery reading it (`default ((select count(*) from t))`). All three are
|
|
142
|
+
// rendered into the persisted DDL bundle, and `propagateTableRename` runs only
|
|
143
|
+
// after this hook returns, so — exactly as in the `renameColumn` arm of
|
|
144
|
+
// `alterTable` — persisting now would durably write a stale reference that only
|
|
145
|
+
// the later propagation event corrects.
|
|
146
|
+
// Rewrite first, in place (each `Expression` is shared with the catalog
|
|
147
|
+
// `TableSchema` and, for a unique partial index, with its derived UNIQUE
|
|
148
|
+
// constraint), which also makes that later pass a no-op for these fields. A
|
|
149
|
+
// throw anywhere in the `try` — including partway through a walk — reverses the
|
|
150
|
+
// rewrites; reversing is a no-op wherever nothing names `newName`. The physical
|
|
151
|
+
// stores have already moved by this point, so the reverse restores only the AST,
|
|
152
|
+
// not the on-disk layout.
|
|
153
|
+
//
|
|
154
|
+
// NOTE: the reverse assumes no expression legitimately named `newName` before
|
|
155
|
+
// the rename. The rename-target guard above makes that true for a real table,
|
|
156
|
+
// and `compilePredicate` now rejects a foreign `table` qualifier at create time,
|
|
157
|
+
// so a live partial-index predicate can only carry a self-qualifier (`where
|
|
158
|
+
// <thisTable>.b > 0`) or a bare reference — never `where <newName>.b > 0` for a
|
|
159
|
+
// different table. The mis-reversal path is therefore unreachable for a live
|
|
160
|
+
// predicate.
|
|
161
|
+
//
|
|
162
|
+
// NOTE: that argument does NOT extend to the column-expression arm, and the
|
|
163
|
+
// residual is left open deliberately. A DEFAULT naming a table that does not
|
|
164
|
+
// exist is accepted at create time (the reference is only resolved when a row is
|
|
165
|
+
// written), so `create table u (…, v integer default ((select 1 from u2)))`
|
|
166
|
+
// followed by `alter table u rename to u2` has a forward pass that matches
|
|
167
|
+
// nothing and a reverse pass that would clobber that `u2` back to `u`. Reaching
|
|
168
|
+
// it needs `saveTableDDL` to throw, and closing it needs a per-walk changed-set
|
|
169
|
+
// threaded through every arm — not worth it for a failed-persist-only path.
|
|
170
|
+
const rewriteTable = (from, to) => {
|
|
171
|
+
renameTableInIndexPredicates(currentSchema.indexes, from, to, schemaName);
|
|
172
|
+
renameTableInCheckConstraints(currentSchema.checkConstraints, from, to, schemaName);
|
|
173
|
+
renameTableInColumnExpressions(currentSchema.columns, from, to, schemaName);
|
|
174
|
+
};
|
|
175
|
+
try {
|
|
176
|
+
rewriteTable(oldName, newName);
|
|
177
|
+
await this.saveTableDDL(renamedSchema);
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
rewriteTable(newName, oldName);
|
|
181
|
+
throw e;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// NOTE: the OLD name's catalog entry is deliberately NOT removed here. Deleting it
|
|
185
|
+
// synchronously would drop `oldName` from the catalog BEFORE the engine's post-hook
|
|
186
|
+
// `propagateTableRename` has rewritten — let alone persisted — the OTHER tables /
|
|
187
|
+
// views / MVs that still name `oldName` (a cross-schema FK, a CHECK expression, a
|
|
188
|
+
// dependent view/MV body). A crash in that gap would strand a durable catalog set
|
|
189
|
+
// naming a vanished table, which reopens as a healthy-looking database whose
|
|
190
|
+
// dependents cannot be written to. The removal is deferred to `finalizeRename`,
|
|
191
|
+
// which the engine calls AFTER propagation has made the dependents durable.
|
|
192
|
+
// Migrate the stats entry (unified __stats__ store, keyed by schema.table).
|
|
193
|
+
// The entry is RE-KEYED, not physically moved with the directory: a unified
|
|
194
|
+
// store keys every table's stats under `schema.table` in one store, so the
|
|
195
|
+
// value must be copied from the old key to the new key and the old key
|
|
196
|
+
// dropped. `dispose()` above already flushed any buffered delta to disk under
|
|
197
|
+
// the old key, so the read here sees the current row-count estimate; deleting
|
|
198
|
+
// without copying (the prior behavior) blinded the planner — a freshly-renamed
|
|
199
|
+
// table reported getEstimatedRowCount() === 0 until stats were re-gathered.
|
|
200
|
+
//
|
|
201
|
+
// NOTE: this re-key reaches the old value only when getStatsStore(newName)
|
|
202
|
+
// returns a store that CONTAINS the old key — true for the shipped providers,
|
|
203
|
+
// which all share one unified __stats__ store, and for any provider whose
|
|
204
|
+
// renameTableStores physically relocates a per-table stats store. A provider
|
|
205
|
+
// that kept per-table stats stores and did NOT relocate them in
|
|
206
|
+
// renameTableStores would orphan the old-keyed value out of reach here (the
|
|
207
|
+
// prior delete-only code lost it just the same — no regression). No shipped
|
|
208
|
+
// provider does this; revisit if one ever keeps per-table, non-relocated stats.
|
|
209
|
+
try {
|
|
210
|
+
const statsStore = await this.provider.getStatsStore(schemaName, newName);
|
|
211
|
+
const oldStatsKey = buildStatsKey(schemaName, oldName);
|
|
212
|
+
const statsValue = await statsStore.get(oldStatsKey);
|
|
213
|
+
if (statsValue) {
|
|
214
|
+
// A table with no stats yet (never flushed) returns undefined here —
|
|
215
|
+
// skip the copy so no spurious zero-count entry lands under the new key.
|
|
216
|
+
await statsStore.put(buildStatsKey(schemaName, newName), statsValue);
|
|
217
|
+
}
|
|
218
|
+
await statsStore.delete(oldStatsKey);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
/* stats are advisory — a stats hiccup must never block the rename */
|
|
222
|
+
}
|
|
223
|
+
// Same emit-iff-`ddl` rule as `StoreModuleAlter.alterTable`: `ddl` set means this
|
|
224
|
+
// call IS the RENAME TO statement's action; absent means an engine-internal step
|
|
225
|
+
// that must announce nothing. No in-tree caller omits it today — the shadow-table
|
|
226
|
+
// rebuild's trailing rename is itself a RENAME TO statement and is silenced by
|
|
227
|
+
// `withPublicEventsSuppressed`, not by this gate.
|
|
228
|
+
// `oldObjectName` says what the table renamed FROM — `objectName` names only the
|
|
229
|
+
// new table, and a receiver could not otherwise tell which of its tables moved.
|
|
230
|
+
if (ddl !== undefined) {
|
|
231
|
+
this.eventEmitter?.emitSchemaChange({
|
|
232
|
+
type: 'alter',
|
|
233
|
+
objectType: 'table',
|
|
234
|
+
schemaName,
|
|
235
|
+
objectName: newName,
|
|
236
|
+
oldObjectName: oldName,
|
|
237
|
+
ddl,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Second phase of a two-phase RENAME TABLE (see {@link renameTable}). The engine calls
|
|
243
|
+
* this at the END of ALTER TABLE ... RENAME TO — AFTER its `propagateTableRename` has
|
|
244
|
+
* rewritten every dependent object that named `oldName` and enqueued their corrective
|
|
245
|
+
* catalog writes onto `StoreModuleBase.persistQueue`. `renameTable` deliberately left `oldName`'s
|
|
246
|
+
* catalog entry in place; here we drain those dependent writes to durability and only
|
|
247
|
+
* THEN drop the old entry. During the window both entries coexist on disk, so every
|
|
248
|
+
* dependent resolves against one of them and every intermediate catalog set rehydrates
|
|
249
|
+
* into a working database.
|
|
250
|
+
*
|
|
251
|
+
* The old-entry delete rides `persistQueue` behind the dependents' already-enqueued
|
|
252
|
+
* writes (FIFO), so it can only run after them, and the drain below awaits the whole
|
|
253
|
+
* chain. Errors are swallowed+logged (the `StoreModuleBase.enqueuePersist` contract): a failed
|
|
254
|
+
* delete leaves the old entry present — a visible, droppable orphan, strictly safer
|
|
255
|
+
* than a dependent stranded against a vanished table.
|
|
256
|
+
*
|
|
257
|
+
* NOTE: full cross-table atomicity — bundling this old-entry delete together with every
|
|
258
|
+
* dependent rewrite into one `provider.beginAtomicBatch` commit — would eliminate even
|
|
259
|
+
* the transient two-entry window (and the physical-move orphan `renameTableStores`
|
|
260
|
+
* leaves), but only on atomic providers, and the dependent set is known only to the
|
|
261
|
+
* engine post-propagate. That is the atomic-provider hardening path; out of scope here.
|
|
262
|
+
*/
|
|
263
|
+
async finalizeRename(_db, schemaName, oldName, _newName) {
|
|
264
|
+
this.enqueuePersist(() => this.removeTableDDL(schemaName, oldName));
|
|
265
|
+
await this.whenCatalogPersisted();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=store-module-rename.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-module-rename.js","sourceRoot":"","sources":["../../../src/common/store-module-rename.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure schema-to-schema rewrites the module applies around DDL: reconciling an
|
|
3
|
+
* implicit-default text primary-key collation to the store's key collation at CREATE
|
|
4
|
+
* time, and retargeting a table's SELF-referencing foreign keys when the table or one
|
|
5
|
+
* of its columns is renamed.
|
|
6
|
+
*
|
|
7
|
+
* Free functions rather than a layer of the store-module chain — each takes a schema
|
|
8
|
+
* and returns a schema, reading no module state.
|
|
9
|
+
*/
|
|
10
|
+
import type { ForeignKeyConstraintSchema, TableSchema } from '@quereus/quereus';
|
|
11
|
+
/**
|
|
12
|
+
* Reconcile each text PRIMARY KEY column's declared collation at CREATE time.
|
|
13
|
+
*
|
|
14
|
+
* The store now encodes PRIMARY KEY uniqueness/ordering PHYSICALLY with a
|
|
15
|
+
* PER-COLUMN key collation (`StoreTable.pkKeyCollations`, drawn from each PK
|
|
16
|
+
* column's declared `collation`), so ANY declared PK collation is honored
|
|
17
|
+
* natively — an explicit `collate binary` text PK is keyed under BINARY, a
|
|
18
|
+
* `collate nocase` under NOCASE, etc. The only thing this reconcile still does is
|
|
19
|
+
* supply the store's table-level DEFAULT (`keyCollation` = K = `config.collation`,
|
|
20
|
+
* default NOCASE) to a text PK column that declares NO explicit collation, so an
|
|
21
|
+
* implicit-default text PK keeps the store's historical NOCASE-keyed behavior
|
|
22
|
+
* (rather than the engine's BINARY column default). An EXPLICIT collation — even
|
|
23
|
+
* one diverging from K — is left exactly as declared and re-keyed natively.
|
|
24
|
+
*
|
|
25
|
+
* Collation governs key bytes only for text, so only PK members whose logical type
|
|
26
|
+
* is textual are touched (an `integer primary key` keeps its BINARY; temporal
|
|
27
|
+
* text-physical types carry no collation). Non-PK columns are never touched.
|
|
28
|
+
*
|
|
29
|
+
* This is the CREATE path only — the load path (`connect` / rehydrate) does not
|
|
30
|
+
* reconcile; the persisted DDL is the source of truth on reopen, and the column's
|
|
31
|
+
* (BINARY-elided) `COLLATE` clause round-trips the per-column key collation.
|
|
32
|
+
*
|
|
33
|
+
* Returns the schema unchanged when no implicit text PK column needs the default
|
|
34
|
+
* applied; otherwise a new schema with a rebuilt `columns` array + `columnIndexMap`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function reconcilePkCollations(schema: TableSchema, keyCollation: string): TableSchema;
|
|
37
|
+
/**
|
|
38
|
+
* Build a column remap array: newColumnIndex -> oldColumnIndex | -1.
|
|
39
|
+
* Maps each column in the new layout to its position in the old layout.
|
|
40
|
+
* -1 means the column is new (fill with default).
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildColumnRemap(oldColumnNames: string[], newColumnNames: string[]): number[];
|
|
43
|
+
/**
|
|
44
|
+
* Retarget a self-referencing foreign key's `referencedTable` across a rename of
|
|
45
|
+
* the owning table. A pure copy — unlike the CHECK / partial-index rewrites this
|
|
46
|
+
* touches a name field, not a shared AST, so the caller needs no rollback.
|
|
47
|
+
* Returns the input array when nothing self-references.
|
|
48
|
+
*/
|
|
49
|
+
export declare function retargetSelfForeignKeys(fks: ReadonlyArray<ForeignKeyConstraintSchema> | undefined, schemaName: string, oldName: string, newName: string): ReadonlyArray<ForeignKeyConstraintSchema> | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Rename a column inside a self-referencing foreign key's `referencedColumnNames`
|
|
52
|
+
* (the names the FK resolves to parent-column indices at enforcement time). Pure
|
|
53
|
+
* copy, as in {@link retargetSelfForeignKeys}. The child-side `columns` are
|
|
54
|
+
* indices, unaffected by a rename.
|
|
55
|
+
*/
|
|
56
|
+
export declare function renameColumnInSelfForeignKeys(fks: ReadonlyArray<ForeignKeyConstraintSchema> | undefined, schemaName: string, tableName: string, oldCol: string, newCol: string): ReadonlyArray<ForeignKeyConstraintSchema> | undefined;
|
|
57
|
+
//# sourceMappingURL=store-module-schema-rewrite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-module-schema-rewrite.d.ts","sourceRoot":"","sources":["../../../src/common/store-module-schema-rewrite.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,WAAW,EACnB,YAAY,EAAE,MAAM,GAClB,WAAW,CAkCb;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAS7F;AAYD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACtC,GAAG,EAAE,aAAa,CAAC,0BAA0B,CAAC,GAAG,SAAS,EAC1D,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACb,aAAa,CAAC,0BAA0B,CAAC,GAAG,SAAS,CAWvD;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC5C,GAAG,EAAE,aAAa,CAAC,0BAA0B,CAAC,GAAG,SAAS,EAC1D,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACZ,aAAa,CAAC,0BAA0B,CAAC,GAAG,SAAS,CAiBvD"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure schema-to-schema rewrites the module applies around DDL: reconciling an
|
|
3
|
+
* implicit-default text primary-key collation to the store's key collation at CREATE
|
|
4
|
+
* time, and retargeting a table's SELF-referencing foreign keys when the table or one
|
|
5
|
+
* of its columns is renamed.
|
|
6
|
+
*
|
|
7
|
+
* Free functions rather than a layer of the store-module chain — each takes a schema
|
|
8
|
+
* and returns a schema, reading no module state.
|
|
9
|
+
*/
|
|
10
|
+
import { buildColumnIndexMap } from '@quereus/quereus';
|
|
11
|
+
/**
|
|
12
|
+
* Reconcile each text PRIMARY KEY column's declared collation at CREATE time.
|
|
13
|
+
*
|
|
14
|
+
* The store now encodes PRIMARY KEY uniqueness/ordering PHYSICALLY with a
|
|
15
|
+
* PER-COLUMN key collation (`StoreTable.pkKeyCollations`, drawn from each PK
|
|
16
|
+
* column's declared `collation`), so ANY declared PK collation is honored
|
|
17
|
+
* natively — an explicit `collate binary` text PK is keyed under BINARY, a
|
|
18
|
+
* `collate nocase` under NOCASE, etc. The only thing this reconcile still does is
|
|
19
|
+
* supply the store's table-level DEFAULT (`keyCollation` = K = `config.collation`,
|
|
20
|
+
* default NOCASE) to a text PK column that declares NO explicit collation, so an
|
|
21
|
+
* implicit-default text PK keeps the store's historical NOCASE-keyed behavior
|
|
22
|
+
* (rather than the engine's BINARY column default). An EXPLICIT collation — even
|
|
23
|
+
* one diverging from K — is left exactly as declared and re-keyed natively.
|
|
24
|
+
*
|
|
25
|
+
* Collation governs key bytes only for text, so only PK members whose logical type
|
|
26
|
+
* is textual are touched (an `integer primary key` keeps its BINARY; temporal
|
|
27
|
+
* text-physical types carry no collation). Non-PK columns are never touched.
|
|
28
|
+
*
|
|
29
|
+
* This is the CREATE path only — the load path (`connect` / rehydrate) does not
|
|
30
|
+
* reconcile; the persisted DDL is the source of truth on reopen, and the column's
|
|
31
|
+
* (BINARY-elided) `COLLATE` clause round-trips the per-column key collation.
|
|
32
|
+
*
|
|
33
|
+
* Returns the schema unchanged when no implicit text PK column needs the default
|
|
34
|
+
* applied; otherwise a new schema with a rebuilt `columns` array + `columnIndexMap`.
|
|
35
|
+
*/
|
|
36
|
+
export function reconcilePkCollations(schema, keyCollation) {
|
|
37
|
+
const pkIndices = new Set(schema.primaryKeyDefinition.map(def => def.index));
|
|
38
|
+
if (pkIndices.size === 0)
|
|
39
|
+
return schema;
|
|
40
|
+
let changed = false;
|
|
41
|
+
const newColumns = schema.columns.map((col, idx) => {
|
|
42
|
+
if (!pkIndices.has(idx))
|
|
43
|
+
return col;
|
|
44
|
+
// Collation governs key bytes only for text; non-text PK columns (integer,
|
|
45
|
+
// real, blob, …) are encoded type-natively and keep their declared collation.
|
|
46
|
+
// Deliberately `isTextual`, NOT `collationAware`: `any` now keys (and
|
|
47
|
+
// compares) under its declared collation, but an undecorated `any` PK's
|
|
48
|
+
// declared collation is always BINARY — `resolveDefaultCollation` never
|
|
49
|
+
// applies a non-BINARY default to ANY, which declares no supported-collation
|
|
50
|
+
// list — and rewriting it to K here would silently flip that shape's
|
|
51
|
+
// comparison semantics store-side only, diverging from the memory backend
|
|
52
|
+
// for no compatibility gain (the historical K-default covers text PKs, not
|
|
53
|
+
// `any`). Only `text` takes the rewrite.
|
|
54
|
+
if (!col.logicalType.isTextual)
|
|
55
|
+
return col;
|
|
56
|
+
// An EXPLICIT collation is honored as-declared — the per-column key encoding
|
|
57
|
+
// keys the column under it (Option B physical re-key parity with memory).
|
|
58
|
+
if (col.collationExplicit)
|
|
59
|
+
return col;
|
|
60
|
+
const declared = (col.collation || 'BINARY').toUpperCase();
|
|
61
|
+
if (declared === keyCollation)
|
|
62
|
+
return col; // implicit default already == K
|
|
63
|
+
// Implicit default diverges from K (e.g. the engine's BINARY column default
|
|
64
|
+
// under the store's NOCASE K): apply the store's table-level default so an
|
|
65
|
+
// undecorated text PK keeps the store's historical NOCASE-keyed semantics.
|
|
66
|
+
changed = true;
|
|
67
|
+
return { ...col, collation: keyCollation };
|
|
68
|
+
});
|
|
69
|
+
if (!changed)
|
|
70
|
+
return schema;
|
|
71
|
+
const columns = Object.freeze(newColumns);
|
|
72
|
+
return { ...schema, columns, columnIndexMap: buildColumnIndexMap(columns) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build a column remap array: newColumnIndex -> oldColumnIndex | -1.
|
|
76
|
+
* Maps each column in the new layout to its position in the old layout.
|
|
77
|
+
* -1 means the column is new (fill with default).
|
|
78
|
+
*/
|
|
79
|
+
export function buildColumnRemap(oldColumnNames, newColumnNames) {
|
|
80
|
+
const oldIndexByName = new Map();
|
|
81
|
+
for (let i = 0; i < oldColumnNames.length; i++) {
|
|
82
|
+
oldIndexByName.set(oldColumnNames[i].toLowerCase(), i);
|
|
83
|
+
}
|
|
84
|
+
return newColumnNames.map(name => {
|
|
85
|
+
const oldIdx = oldIndexByName.get(name.toLowerCase());
|
|
86
|
+
return oldIdx !== undefined ? oldIdx : -1;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/** Whether `fk` points back at the table that owns it. */
|
|
90
|
+
function isSelfForeignKey(fk, schemaLower, tableLower) {
|
|
91
|
+
return (fk.referencedSchema ?? schemaLower).toLowerCase() === schemaLower
|
|
92
|
+
&& fk.referencedTable.toLowerCase() === tableLower;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Retarget a self-referencing foreign key's `referencedTable` across a rename of
|
|
96
|
+
* the owning table. A pure copy — unlike the CHECK / partial-index rewrites this
|
|
97
|
+
* touches a name field, not a shared AST, so the caller needs no rollback.
|
|
98
|
+
* Returns the input array when nothing self-references.
|
|
99
|
+
*/
|
|
100
|
+
export function retargetSelfForeignKeys(fks, schemaName, oldName, newName) {
|
|
101
|
+
if (!fks)
|
|
102
|
+
return fks;
|
|
103
|
+
const schemaLower = schemaName.toLowerCase();
|
|
104
|
+
const oldLower = oldName.toLowerCase();
|
|
105
|
+
let changed = false;
|
|
106
|
+
const next = fks.map(fk => {
|
|
107
|
+
if (!isSelfForeignKey(fk, schemaLower, oldLower))
|
|
108
|
+
return fk;
|
|
109
|
+
changed = true;
|
|
110
|
+
return { ...fk, referencedTable: newName };
|
|
111
|
+
});
|
|
112
|
+
return changed ? Object.freeze(next) : fks;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Rename a column inside a self-referencing foreign key's `referencedColumnNames`
|
|
116
|
+
* (the names the FK resolves to parent-column indices at enforcement time). Pure
|
|
117
|
+
* copy, as in {@link retargetSelfForeignKeys}. The child-side `columns` are
|
|
118
|
+
* indices, unaffected by a rename.
|
|
119
|
+
*/
|
|
120
|
+
export function renameColumnInSelfForeignKeys(fks, schemaName, tableName, oldCol, newCol) {
|
|
121
|
+
if (!fks)
|
|
122
|
+
return fks;
|
|
123
|
+
const schemaLower = schemaName.toLowerCase();
|
|
124
|
+
const tableLower = tableName.toLowerCase();
|
|
125
|
+
const oldColLower = oldCol.toLowerCase();
|
|
126
|
+
let changed = false;
|
|
127
|
+
const next = fks.map(fk => {
|
|
128
|
+
if (!isSelfForeignKey(fk, schemaLower, tableLower))
|
|
129
|
+
return fk;
|
|
130
|
+
if (!fk.referencedColumnNames?.some(n => n.toLowerCase() === oldColLower))
|
|
131
|
+
return fk;
|
|
132
|
+
changed = true;
|
|
133
|
+
return {
|
|
134
|
+
...fk,
|
|
135
|
+
referencedColumnNames: Object.freeze(fk.referencedColumnNames.map(n => n.toLowerCase() === oldColLower ? newCol : n)),
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
return changed ? Object.freeze(next) : fks;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=store-module-schema-rewrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-module-schema-rewrite.js","sourceRoot":"","sources":["../../../src/common/store-module-schema-rewrite.ts"],"names":[],"mappings":"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"}
|