@quereus/isolation 0.2.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 ADDED
@@ -0,0 +1,216 @@
1
+ # @quereus/isolation
2
+
3
+ Generic transaction isolation layer for Quereus virtual table modules.
4
+
5
+ ## Overview
6
+
7
+ The `@quereus/isolation` package provides MVCC-style transaction isolation semantics for any Quereus virtual table module. It wraps existing modules to add:
8
+
9
+ - **Read-your-own-writes** — See uncommitted changes within your transaction
10
+ - **Snapshot isolation** — Consistent reads throughout the transaction
11
+ - **Savepoint support** — Nested transaction control
12
+ - **ACID semantics** — Full transaction guarantees
13
+
14
+ This allows module authors to focus on storage concerns while getting isolation "for free."
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ yarn add @quereus/isolation @quereus/quereus
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { Database, MemoryTableModule } from '@quereus/quereus';
26
+ import { IsolationModule } from '@quereus/isolation';
27
+
28
+ const db = new Database();
29
+
30
+ // Create any underlying module (memory, store, custom, etc.)
31
+ const memoryModule = new MemoryTableModule();
32
+
33
+ // Wrap it with the isolation layer
34
+ const isolatedModule = new IsolationModule({
35
+ underlying: memoryModule,
36
+ });
37
+
38
+ db.registerModule('isolated', isolatedModule);
39
+
40
+ // Use it like any other module, but with full isolation
41
+ await db.exec(`CREATE TABLE users (
42
+ id INTEGER PRIMARY KEY,
43
+ name TEXT
44
+ ) USING isolated`);
45
+
46
+ await db.exec('BEGIN');
47
+ await db.exec(`INSERT INTO users VALUES (1, 'Alice')`);
48
+
49
+ // Reads see uncommitted changes
50
+ const user = await db.get('SELECT * FROM users WHERE id = 1');
51
+ console.log(user.name); // 'Alice'
52
+
53
+ await db.exec('COMMIT'); // Or ROLLBACK
54
+ ```
55
+
56
+ ## Architecture
57
+
58
+ The isolation layer operates at the **row level**, merging query results from two modules:
59
+
60
+ 1. **Overlay module** — Stores uncommitted changes (inserts, updates, deletes as tombstones)
61
+ 2. **Underlying module** — Stores committed data
62
+
63
+ ```
64
+ ┌─────────────────────────────────────────────────────────┐
65
+ │ IsolationModule │
66
+ │ │
67
+ │ ┌────────────────────────────────────────────────────┐ │
68
+ │ │ Overlay Module (e.g., memory vtab) │ │
69
+ │ │ - Stores pending inserts, updates, tombstones │ │
70
+ │ │ - Per-connection isolation │ │
71
+ │ └────────────────────────────────────────────────────┘ │
72
+ │ │ │
73
+ │ │ row-level merge │
74
+ │ ▼ │
75
+ │ ┌────────────────────────────────────────────────────┐ │
76
+ │ │ Underlying Module (any) │ │
77
+ │ │ - LevelDB / IndexedDB store │ │
78
+ │ │ - Custom module without isolation │ │
79
+ │ └────────────────────────────────────────────────────┘ │
80
+ └─────────────────────────────────────────────────────────┘
81
+ ```
82
+
83
+ ### Key Features
84
+
85
+ **Per-connection overlay** — Each database instance gets its own overlay storage, ensuring proper isolation between connections.
86
+
87
+ **Lazy overlay creation** — No memory overhead until the first write in a transaction.
88
+
89
+ **Configurable overlay module** — Use memory for fast transactions, or persistent storage for large transactions:
90
+
91
+ ```typescript
92
+ import { IsolationModule } from '@quereus/isolation';
93
+ import { MemoryTableModule } from '@quereus/quereus';
94
+ import { StoreModule } from '@quereus/store';
95
+
96
+ // Fast, ephemeral overlay (default)
97
+ const isolatedModule = new IsolationModule({
98
+ underlying: myStoreModule,
99
+ overlay: new MemoryTableModule(),
100
+ });
101
+
102
+ // Or use persistent overlay for large transactions
103
+ const isolatedModule = new IsolationModule({
104
+ underlying: myStoreModule,
105
+ overlay: new StoreModule(tempStoreProvider),
106
+ });
107
+ ```
108
+
109
+ ## API
110
+
111
+ ### `IsolationModule`
112
+
113
+ ```typescript
114
+ class IsolationModule implements VirtualTableModule {
115
+ constructor(config: IsolationModuleConfig);
116
+ getCapabilities(): ModuleCapabilities;
117
+ }
118
+ ```
119
+
120
+ #### Configuration
121
+
122
+ ```typescript
123
+ interface IsolationModuleConfig {
124
+ /** Module to wrap with isolation semantics */
125
+ underlying: VirtualTableModule<any, any>;
126
+
127
+ /** Optional overlay module (defaults to MemoryTableModule) */
128
+ overlay?: VirtualTableModule<any, any>;
129
+
130
+ /** Optional tombstone column name (defaults to '_tombstone') */
131
+ tombstoneColumn?: string;
132
+ }
133
+ ```
134
+
135
+ ### Merge Utilities
136
+
137
+ The package also exports low-level utilities for merging sorted streams:
138
+
139
+ ```typescript
140
+ import { mergeStreams, createMergeEntry, createTombstone } from '@quereus/isolation';
141
+
142
+ // Merge two sorted streams (overlay and underlying)
143
+ const merged = mergeStreams(overlayStream, underlyingStream, {
144
+ comparePK: (a, b) => /* compare primary keys */,
145
+ extractPK: (row) => /* extract PK from row */,
146
+ });
147
+ ```
148
+
149
+ See the [design document](https://github.com/gotchoices/quereus/blob/main/packages/quereus/docs/design-isolation-layer.md) for detailed architecture and implementation notes.
150
+
151
+ ## Use Cases
152
+
153
+ ### Store Module Isolation
154
+
155
+ The `@quereus/store` package provides a convenience function:
156
+
157
+ ```typescript
158
+ import { createIsolatedStoreModule } from '@quereus/store';
159
+ import { createLevelDBProvider } from '@quereus/plugin-leveldb';
160
+
161
+ const provider = createLevelDBProvider({ basePath: './data' });
162
+ const module = createIsolatedStoreModule({ provider });
163
+
164
+ db.registerModule('store', module);
165
+ ```
166
+
167
+ ### Custom Module Isolation
168
+
169
+ Wrap any custom module:
170
+
171
+ ```typescript
172
+ import { IsolationModule } from '@quereus/isolation';
173
+ import { MyCustomModule } from './my-module';
174
+
175
+ const isolatedModule = new IsolationModule({
176
+ underlying: new MyCustomModule(),
177
+ });
178
+ ```
179
+
180
+ ## Checking Capabilities
181
+
182
+ ```typescript
183
+ const caps = isolatedModule.getCapabilities();
184
+ console.log(caps.isolation); // true
185
+ console.log(caps.savepoints); // true
186
+ console.log(caps.persistent); // (from underlying module)
187
+ ```
188
+
189
+ ## Performance
190
+
191
+ The isolation layer adds minimal overhead:
192
+
193
+ - **Fast path** — No overlay merging if no writes have occurred
194
+ - **Point lookups** — O(log n) overlay check + underlying lookup
195
+ - **Range scans** — Streaming merge of sorted results
196
+
197
+ For performance-critical applications, consider:
198
+ - Using memory overlay for small transactions
199
+ - The memory vtab uses integrated isolation (no separate layer)
200
+
201
+ ## Testing
202
+
203
+ ```bash
204
+ yarn test
205
+ ```
206
+
207
+ ## License
208
+
209
+ MIT
210
+
211
+ ## Related Packages
212
+
213
+ - [@quereus/quereus](https://www.npmjs.com/package/@quereus/quereus) — Core SQL engine
214
+ - [@quereus/store](https://www.npmjs.com/package/@quereus/store) — Abstract key-value storage
215
+ - [@quereus/plugin-leveldb](https://www.npmjs.com/package/@quereus/plugin-leveldb) — LevelDB storage
216
+ - [@quereus/plugin-indexeddb](https://www.npmjs.com/package/@quereus/plugin-indexeddb) — IndexedDB storage
@@ -0,0 +1,8 @@
1
+ export type { MergeEntry, MergeConfig, PKComparator, PKExtractor, SortKeyComparator, SortKeyExtractor } from './merge-types.js';
2
+ export { mergeStreams, createMergeEntry, createTombstone } from './merge-iterator.js';
3
+ export type { IsolationModuleConfig } from './isolation-types.js';
4
+ export type { UnderlyingTableState } from './isolation-module.js';
5
+ export { IsolationModule } from './isolation-module.js';
6
+ export { IsolatedTable } from './isolated-table.js';
7
+ export { IsolatedConnection } from './isolated-connection.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAChI,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtF,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { mergeStreams, createMergeEntry, createTombstone } from './merge-iterator.js';
2
+ // Isolation layer classes
3
+ export { IsolationModule } from './isolation-module.js';
4
+ export { IsolatedTable } from './isolated-table.js';
5
+ export { IsolatedConnection } from './isolated-connection.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAMtF,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { VirtualTableConnection } from '@quereus/quereus';
2
+ /**
3
+ * Callback interface for IsolatedTable to receive transaction events.
4
+ */
5
+ export interface IsolatedTableCallback {
6
+ onConnectionCommit(): Promise<void>;
7
+ onConnectionRollback(): Promise<void>;
8
+ onConnectionSavepoint(index: number): Promise<void>;
9
+ onConnectionReleaseSavepoint(index: number): Promise<void>;
10
+ onConnectionRollbackToSavepoint(index: number): Promise<void>;
11
+ }
12
+ /**
13
+ * Connection wrapper that coordinates transactions across overlay and underlying.
14
+ * When commit/rollback are called by the database, this connection calls back
15
+ * to the IsolatedTable to perform flush/clear operations.
16
+ */
17
+ export declare class IsolatedConnection implements VirtualTableConnection {
18
+ readonly connectionId: string;
19
+ readonly tableName: string;
20
+ private readonly underlyingConnection;
21
+ private readonly overlayConnection;
22
+ private readonly tableCallback;
23
+ constructor(tableName: string, underlyingConnection: VirtualTableConnection | undefined, overlayConnection: VirtualTableConnection | undefined, tableCallback?: IsolatedTableCallback);
24
+ begin(): Promise<void>;
25
+ commit(): Promise<void>;
26
+ rollback(): Promise<void>;
27
+ createSavepoint(index: number): Promise<void>;
28
+ releaseSavepoint(index: number): Promise<void>;
29
+ rollbackToSavepoint(index: number): Promise<void>;
30
+ disconnect(): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=isolated-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolated-connection.d.ts","sourceRoot":"","sources":["../../src/isolated-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAK/D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,sBAAsB;IAChE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqC;IAC1E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqC;IACvE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;gBAGjE,SAAS,EAAE,MAAM,EACjB,oBAAoB,EAAE,sBAAsB,GAAG,SAAS,EACxD,iBAAiB,EAAE,sBAAsB,GAAG,SAAS,EACrD,aAAa,CAAC,EAAE,qBAAqB;IAShC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAUvB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAazB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAIjC"}
@@ -0,0 +1,71 @@
1
+ let connectionIdCounter = 0;
2
+ /**
3
+ * Connection wrapper that coordinates transactions across overlay and underlying.
4
+ * When commit/rollback are called by the database, this connection calls back
5
+ * to the IsolatedTable to perform flush/clear operations.
6
+ */
7
+ export class IsolatedConnection {
8
+ connectionId;
9
+ tableName;
10
+ underlyingConnection;
11
+ overlayConnection;
12
+ tableCallback;
13
+ constructor(tableName, underlyingConnection, overlayConnection, tableCallback) {
14
+ this.connectionId = `isolated-${++connectionIdCounter}`;
15
+ this.tableName = tableName;
16
+ this.underlyingConnection = underlyingConnection;
17
+ this.overlayConnection = overlayConnection;
18
+ this.tableCallback = tableCallback;
19
+ }
20
+ async begin() {
21
+ await this.underlyingConnection?.begin();
22
+ await this.overlayConnection?.begin();
23
+ }
24
+ async commit() {
25
+ // Call table's commit logic (flush overlay to underlying)
26
+ if (this.tableCallback) {
27
+ await this.tableCallback.onConnectionCommit();
28
+ }
29
+ // Then commit the underlying connections
30
+ await this.overlayConnection?.commit();
31
+ await this.underlyingConnection?.commit();
32
+ }
33
+ async rollback() {
34
+ // Call table's rollback logic (clear overlay)
35
+ if (this.tableCallback) {
36
+ await this.tableCallback.onConnectionRollback();
37
+ }
38
+ // Only rollback the overlay - the underlying table should NOT be rolled back
39
+ // because it only contains committed data (flushed during commit)
40
+ await this.overlayConnection?.rollback();
41
+ // Note: We intentionally do NOT rollback underlyingConnection here.
42
+ // The underlying table only receives writes during commit flush,
43
+ // and those writes should be committed, not rolled back.
44
+ }
45
+ async createSavepoint(index) {
46
+ if (this.tableCallback) {
47
+ await this.tableCallback.onConnectionSavepoint(index);
48
+ }
49
+ await this.underlyingConnection?.createSavepoint(index);
50
+ await this.overlayConnection?.createSavepoint(index);
51
+ }
52
+ async releaseSavepoint(index) {
53
+ if (this.tableCallback) {
54
+ await this.tableCallback.onConnectionReleaseSavepoint(index);
55
+ }
56
+ await this.underlyingConnection?.releaseSavepoint(index);
57
+ await this.overlayConnection?.releaseSavepoint(index);
58
+ }
59
+ async rollbackToSavepoint(index) {
60
+ if (this.tableCallback) {
61
+ await this.tableCallback.onConnectionRollbackToSavepoint(index);
62
+ }
63
+ await this.overlayConnection?.rollbackToSavepoint(index);
64
+ await this.underlyingConnection?.rollbackToSavepoint(index);
65
+ }
66
+ async disconnect() {
67
+ await this.overlayConnection?.disconnect();
68
+ await this.underlyingConnection?.disconnect();
69
+ }
70
+ }
71
+ //# sourceMappingURL=isolated-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolated-connection.js","sourceRoot":"","sources":["../../src/isolated-connection.ts"],"names":[],"mappings":"AAGA,IAAI,mBAAmB,GAAG,CAAC,CAAC;AAa5B;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACrB,YAAY,CAAS;IACrB,SAAS,CAAS;IAEV,oBAAoB,CAAqC;IACzD,iBAAiB,CAAqC;IACtD,aAAa,CAAoC;IAElE,YACC,SAAiB,EACjB,oBAAwD,EACxD,iBAAqD,EACrD,aAAqC;QAErC,IAAI,CAAC,YAAY,GAAG,YAAY,EAAE,mBAAmB,EAAE,CAAC;QACxD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QACjD,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,KAAK;QACV,MAAM,IAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,CAAC;QACzC,MAAM,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,MAAM;QACX,0DAA0D;QAC1D,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,CAAC;QAC/C,CAAC;QACD,yCAAyC;QACzC,MAAM,IAAI,CAAC,iBAAiB,EAAE,MAAM,EAAE,CAAC;QACvC,MAAM,IAAI,CAAC,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,QAAQ;QACb,8CAA8C;QAC9C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,aAAa,CAAC,oBAAoB,EAAE,CAAC;QACjD,CAAC;QACD,6EAA6E;QAC7E,kEAAkE;QAClE,MAAM,IAAI,CAAC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;QACzC,oEAAoE;QACpE,iEAAiE;QACjE,yDAAyD;IAC1D,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAa;QAClC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,IAAI,CAAC,iBAAiB,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,KAAa;QACnC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,aAAa,CAAC,4BAA4B,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAa;QACtC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,aAAa,CAAC,+BAA+B,CAAC,KAAK,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,UAAU;QACf,MAAM,IAAI,CAAC,iBAAiB,EAAE,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,oBAAoB,EAAE,UAAU,EAAE,CAAC;IAC/C,CAAC;CACD"}
@@ -0,0 +1,219 @@
1
+ import type { Database, MaybePromise, Row, TableIndexSchema as IndexSchema, FilterInfo, SchemaChangeInfo, UpdateArgs, VirtualTableConnection } from '@quereus/quereus';
2
+ import { VirtualTable } from '@quereus/quereus';
3
+ import type { IsolationModule } from './isolation-module.js';
4
+ import { type IsolatedTableCallback } from './isolated-connection.js';
5
+ /**
6
+ * A table wrapper that provides transaction isolation via an overlay.
7
+ *
8
+ * Each IsolatedTable instance accesses a connection-scoped overlay that is:
9
+ * - Created lazily on first write
10
+ * - Shared across all IsolatedTable instances in the same transaction
11
+ * - Stored in the IsolationModule's connection overlay map
12
+ *
13
+ * This provides true per-connection isolation - each connection's uncommitted
14
+ * changes are invisible to other connections, but visible to all queries
15
+ * within the same connection.
16
+ *
17
+ * Reads merge overlay changes with underlying data.
18
+ * Writes go to overlay only until commit.
19
+ */
20
+ export declare class IsolatedTable extends VirtualTable implements IsolatedTableCallback {
21
+ private readonly isolationModule;
22
+ private readonly underlyingTable;
23
+ private registeredConnection;
24
+ constructor(db: Database, module: IsolationModule, underlyingTable: VirtualTable);
25
+ /**
26
+ * Gets the tombstone column name from the module.
27
+ */
28
+ private get tombstoneColumn();
29
+ /**
30
+ * Gets the connection-scoped overlay state, or undefined if no overlay exists yet.
31
+ */
32
+ private getOverlayState;
33
+ /**
34
+ * Gets the overlay table, or undefined if no overlay exists yet.
35
+ */
36
+ private get overlayTable();
37
+ /**
38
+ * Gets whether this connection has uncommitted changes.
39
+ */
40
+ private get hasChanges();
41
+ /**
42
+ * Sets the hasChanges flag in the connection-scoped overlay state.
43
+ */
44
+ private setHasChanges;
45
+ /**
46
+ * Lazily creates the overlay table on first write.
47
+ *
48
+ * The overlay is stored in connection-scoped storage, so it persists
49
+ * across multiple IsolatedTable instances within the same transaction.
50
+ *
51
+ * The schema is obtained from the underlying table at this point,
52
+ * supporting scenarios where schema is discovered lazily from storage.
53
+ */
54
+ private ensureOverlay;
55
+ /**
56
+ * Ensures a connection is registered with the database for transaction coordination.
57
+ * This is called before any read or write operation.
58
+ */
59
+ private ensureConnection;
60
+ /**
61
+ * Creates a new isolated connection for transaction support.
62
+ */
63
+ createConnection(): MaybePromise<VirtualTableConnection>;
64
+ private createConnectionAsync;
65
+ /**
66
+ * Query the table, merging overlay with underlying.
67
+ *
68
+ * When overlay is empty or doesn't exist, delegates directly to underlying for efficiency.
69
+ * When overlay has changes, merges both streams using the appropriate key order.
70
+ *
71
+ * For primary key scans: merge by PK order
72
+ * For secondary index scans: merge by (indexKey, PK) order
73
+ */
74
+ query(filterInfo: FilterInfo): AsyncIterable<Row>;
75
+ /**
76
+ * Wrapper that ensures connection before merging.
77
+ */
78
+ private mergedQueryWithConnection;
79
+ /**
80
+ * Performs merged query combining overlay and underlying data.
81
+ *
82
+ * Both overlay and underlying are queried with the same FilterInfo (for the overlay,
83
+ * adjusted for its schema with tombstone column). Both streams return rows in the
84
+ * same order (by PK for primary scans, by index key for secondary index scans).
85
+ *
86
+ * The merge iterator combines them using the appropriate sort key comparator.
87
+ */
88
+ private mergedQuery;
89
+ /**
90
+ * Parses FilterInfo to determine which index is being used.
91
+ * Returns null for full table scan or primary key scan, index name for secondary indexes.
92
+ */
93
+ private parseIndexFromFilterInfo;
94
+ /**
95
+ * Gets the column indices for a secondary index.
96
+ */
97
+ private getIndexColumnIndices;
98
+ /**
99
+ * Adapts FilterInfo for the overlay table schema (which has an extra tombstone column).
100
+ * The constraints and index references remain the same since the overlay has matching indexes.
101
+ */
102
+ private adaptFilterInfoForOverlay;
103
+ /**
104
+ * Queries the overlay table and converts rows to MergeEntry format.
105
+ *
106
+ * Uses the same FilterInfo as the underlying query so both streams are in the same order.
107
+ * For secondary index scans, the sort key includes both the index key and primary key.
108
+ */
109
+ private queryOverlayAsMergeEntries;
110
+ /**
111
+ * Builds the sort key for a row based on the index being scanned.
112
+ *
113
+ * For primary key scans: sort key is the PK
114
+ * For secondary index scans: sort key is [indexKeyParts..., pkParts...]
115
+ */
116
+ private buildSortKey;
117
+ /**
118
+ * Builds the merge configuration using this table's key functions.
119
+ *
120
+ * For primary key scans: compare by PK
121
+ * For secondary index scans: compare by (indexKey, PK) using underlying's comparator
122
+ *
123
+ * @param indexInfo Which index is being scanned. Defaults to primary key scan.
124
+ */
125
+ private buildMergeConfig;
126
+ /**
127
+ * Gets the primary key comparator, preferring the underlying table's comparator.
128
+ */
129
+ private getComparePK;
130
+ /**
131
+ * Builds a sort key comparator for secondary index scans.
132
+ *
133
+ * Compares by index key columns first, then by PK columns.
134
+ */
135
+ private buildCompareSortKey;
136
+ /**
137
+ * Gets the index of the tombstone column in overlay rows.
138
+ */
139
+ private getTombstoneColumnIndex;
140
+ /**
141
+ * Gets the primary key column indices from the underlying table schema.
142
+ */
143
+ getPrimaryKeyIndices(): number[];
144
+ /**
145
+ * Performs INSERT, UPDATE, or DELETE on the overlay.
146
+ * Changes are not visible to underlying until commit.
147
+ *
148
+ * The overlay is created lazily on first write, using schema from the underlying table.
149
+ */
150
+ update(args: UpdateArgs): Promise<Row | undefined>;
151
+ /**
152
+ * Checks if a row with the given primary key exists in the overlay.
153
+ */
154
+ private rowExistsInOverlay;
155
+ /**
156
+ * Gets a row from the overlay by primary key.
157
+ */
158
+ private getOverlayRow;
159
+ /**
160
+ * Creates a FilterInfo for a full table scan (no constraints).
161
+ */
162
+ private createFullScanFilterInfo;
163
+ begin(): Promise<void>;
164
+ sync(): Promise<void>;
165
+ commit(): Promise<void>;
166
+ /**
167
+ * Flushes all overlay changes to the underlying table.
168
+ * Called during commit to persist changes.
169
+ *
170
+ * This method manages the underlying table's transaction lifecycle independently
171
+ * to ensure that flushed data is committed and won't be rolled back by subsequent
172
+ * transaction rollbacks.
173
+ */
174
+ private flushOverlayToUnderlying;
175
+ /**
176
+ * Checks if a row with the given primary key exists in the underlying table.
177
+ */
178
+ private rowExistsInUnderlying;
179
+ rollback(): Promise<void>;
180
+ /**
181
+ * Clears the connection-scoped overlay and resets state.
182
+ */
183
+ private clearOverlay;
184
+ savepoint(index: number): Promise<void>;
185
+ release(index: number): Promise<void>;
186
+ rollbackTo(index: number): Promise<void>;
187
+ disconnect(): Promise<void>;
188
+ rename(newName: string): Promise<void>;
189
+ alterSchema(changeInfo: SchemaChangeInfo): Promise<void>;
190
+ createIndex(indexInfo: IndexSchema): Promise<void>;
191
+ dropIndex(indexName: string): Promise<void>;
192
+ /**
193
+ * Returns whether there are pending uncommitted changes.
194
+ */
195
+ hasPendingChanges(): boolean;
196
+ /**
197
+ * Called by IsolatedConnection when the database commits.
198
+ * Flushes overlay to underlying and clears overlay.
199
+ */
200
+ onConnectionCommit(): Promise<void>;
201
+ /**
202
+ * Called by IsolatedConnection when the database rolls back.
203
+ * Clears overlay without flushing.
204
+ */
205
+ onConnectionRollback(): Promise<void>;
206
+ /**
207
+ * Called by IsolatedConnection when a savepoint is created.
208
+ */
209
+ onConnectionSavepoint(index: number): Promise<void>;
210
+ /**
211
+ * Called by IsolatedConnection when a savepoint is released.
212
+ */
213
+ onConnectionReleaseSavepoint(index: number): Promise<void>;
214
+ /**
215
+ * Called by IsolatedConnection when rolling back to a savepoint.
216
+ */
217
+ onConnectionRollbackToSavepoint(index: number): Promise<void>;
218
+ }
219
+ //# sourceMappingURL=isolated-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isolated-table.d.ts","sourceRoot":"","sources":["../../src/isolated-table.ts"],"names":[],"mappings":"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"}