@quereus/quereus 0.6.2 → 0.6.4
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 +1 -1
- package/dist/src/index.d.ts +4 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +1 -1
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/schema/schema-hasher.d.ts +3 -3
- package/dist/src/schema/schema-hasher.d.ts.map +1 -1
- package/dist/src/schema/schema-hasher.js +9 -27
- package/dist/src/schema/schema-hasher.js.map +1 -1
- package/dist/src/util/hash.d.ts +19 -0
- package/dist/src/util/hash.d.ts.map +1 -0
- package/dist/src/util/hash.js +76 -0
- package/dist/src/util/hash.js.map +1 -0
- package/dist/src/util/plugin-helper.d.ts +45 -0
- package/dist/src/util/plugin-helper.d.ts.map +1 -0
- package/dist/src/util/plugin-helper.js +85 -0
- package/dist/src/util/plugin-helper.js.map +1 -0
- package/package.json +4 -2
- package/src/index.ts +10 -16
- package/src/planner/building/delete.ts +214 -214
- package/src/planner/building/insert.ts +428 -428
- package/src/planner/building/update.ts +319 -319
- package/src/runtime/emit/schema-declarative.ts +1 -1
- package/src/schema/schema-hasher.ts +9 -27
- package/src/util/ast-stringify.ts +864 -864
- package/src/util/hash.ts +90 -0
- package/src/util/plugin-helper.ts +110 -0
- package/src/vtab/memory/table.ts +256 -256
- package/src/vtab/table.ts +162 -162
- package/dist/src/config/loader.d.ts +0 -41
- package/dist/src/config/loader.d.ts.map +0 -1
- package/dist/src/config/loader.js +0 -102
- package/dist/src/config/loader.js.map +0 -1
- package/dist/src/planner/nodes/physical-access-nodes.d.ts +0 -83
- package/dist/src/planner/nodes/physical-access-nodes.d.ts.map +0 -1
- package/dist/src/planner/nodes/physical-access-nodes.js +0 -226
- package/dist/src/planner/nodes/physical-access-nodes.js.map +0 -1
- package/dist/src/planner/nodes/scan.d.ts +0 -27
- package/dist/src/planner/nodes/scan.d.ts.map +0 -1
- package/dist/src/planner/nodes/scan.js +0 -78
- package/dist/src/planner/nodes/scan.js.map +0 -1
- package/dist/src/planner/nodes/update-executor-node.d.ts +0 -24
- package/dist/src/planner/nodes/update-executor-node.d.ts.map +0 -1
- package/dist/src/planner/nodes/update-executor-node.js +0 -57
- package/dist/src/planner/nodes/update-executor-node.js.map +0 -1
- package/dist/src/planner/physical-utils.d.ts +0 -36
- package/dist/src/planner/physical-utils.d.ts.map +0 -1
- package/dist/src/planner/physical-utils.js +0 -122
- package/dist/src/planner/physical-utils.js.map +0 -1
- package/dist/src/planner/rules/physical/rule-filter-optimization.d.ts +0 -11
- package/dist/src/planner/rules/physical/rule-filter-optimization.d.ts.map +0 -1
- package/dist/src/planner/rules/physical/rule-filter-optimization.js +0 -49
- package/dist/src/planner/rules/physical/rule-filter-optimization.js.map +0 -1
- package/dist/src/planner/rules/physical/rule-mark-physical.d.ts +0 -11
- package/dist/src/planner/rules/physical/rule-mark-physical.d.ts.map +0 -1
- package/dist/src/planner/rules/physical/rule-mark-physical.js +0 -29
- package/dist/src/planner/rules/physical/rule-mark-physical.js.map +0 -1
- package/dist/src/planner/rules/physical/rule-project-optimization.d.ts +0 -11
- package/dist/src/planner/rules/physical/rule-project-optimization.d.ts.map +0 -1
- package/dist/src/planner/rules/physical/rule-project-optimization.js +0 -44
- package/dist/src/planner/rules/physical/rule-project-optimization.js.map +0 -1
- package/dist/src/planner/rules/physical/rule-sort-optimization.d.ts +0 -11
- package/dist/src/planner/rules/physical/rule-sort-optimization.d.ts.map +0 -1
- package/dist/src/planner/rules/physical/rule-sort-optimization.js +0 -53
- package/dist/src/planner/rules/physical/rule-sort-optimization.js.map +0 -1
- package/dist/src/planner/rules/rewrite/rule-constant-folding.d.ts +0 -11
- package/dist/src/planner/rules/rewrite/rule-constant-folding.d.ts.map +0 -1
- package/dist/src/planner/rules/rewrite/rule-constant-folding.js +0 -59
- package/dist/src/planner/rules/rewrite/rule-constant-folding.js.map +0 -1
- package/dist/src/planner/util/deferred-constraint.d.ts +0 -14
- package/dist/src/planner/util/deferred-constraint.d.ts.map +0 -1
- package/dist/src/planner/util/deferred-constraint.js +0 -85
- package/dist/src/planner/util/deferred-constraint.js.map +0 -1
- package/dist/src/runtime/emit/table-reference.d.ts +0 -5
- package/dist/src/runtime/emit/table-reference.d.ts.map +0 -1
- package/dist/src/runtime/emit/table-reference.js +0 -67
- package/dist/src/runtime/emit/table-reference.js.map +0 -1
- package/dist/src/runtime/emit/update-executor.d.ts +0 -5
- package/dist/src/runtime/emit/update-executor.d.ts.map +0 -1
- package/dist/src/runtime/emit/update-executor.js +0 -54
- package/dist/src/runtime/emit/update-executor.js.map +0 -1
- package/dist/src/util/plugin-loader.d.ts +0 -52
- package/dist/src/util/plugin-loader.d.ts.map +0 -1
- package/dist/src/util/plugin-loader.js +0 -307
- package/dist/src/util/plugin-loader.js.map +0 -1
- package/src/config/loader.ts +0 -140
- package/src/util/plugin-loader.ts +0 -387
package/src/vtab/memory/table.ts
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
import { VirtualTable } from '../table.js';
|
|
2
|
-
import type { AnyVirtualTableModule, SchemaChangeInfo } from '../module.js';
|
|
3
|
-
import type { Database } from '../../core/database.js';
|
|
4
|
-
import type { Row } from '../../common/types.js';
|
|
5
|
-
import { type IndexSchema, type TableSchema } from '../../schema/table.js';
|
|
6
|
-
import { MemoryTableManager } from './layer/manager.js';
|
|
7
|
-
import type { MemoryTableConnection } from './layer/connection.js';
|
|
8
|
-
import { QuereusError } from '../../common/errors.js';
|
|
9
|
-
import { StatusCode } from '../../common/types.js';
|
|
10
|
-
import type { FilterInfo } from '../filter-info.js';
|
|
11
|
-
import { buildScanPlanFromFilterInfo } from './layer/scan-plan.js';
|
|
12
|
-
import type { ColumnDef as ASTColumnDef } from '../../parser/ast.js'; // Assuming this will be updated for renameColumn
|
|
13
|
-
import { createMemoryTableLoggers } from './utils/logging.js';
|
|
14
|
-
import { safeJsonStringify } from '../../util/serialization.js';
|
|
15
|
-
import type { VirtualTableConnection } from '../connection.js';
|
|
16
|
-
import { MemoryVirtualTableConnection } from './connection.js';
|
|
17
|
-
import type { ConflictResolution } from '../../common/constants.js';
|
|
18
|
-
|
|
19
|
-
const logger = createMemoryTableLoggers('table');
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Represents a connection-specific instance of an in-memory table using the layer-based MVCC model.
|
|
23
|
-
* This class acts as a thin wrapper around the shared MemoryTableManager,
|
|
24
|
-
* holding the connection state.
|
|
25
|
-
*/
|
|
26
|
-
export class MemoryTable extends VirtualTable {
|
|
27
|
-
/** @internal The shared manager handling layers, schema, and global state */
|
|
28
|
-
public readonly manager: MemoryTableManager;
|
|
29
|
-
/** @internal Connection state specific to this table instance (lazily initialized) */
|
|
30
|
-
private connection: MemoryTableConnection | null = null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @internal - Use MemoryTableModule.connect or create
|
|
34
|
-
* Creates a connection-specific instance linked to a manager.
|
|
35
|
-
*/
|
|
36
|
-
constructor(
|
|
37
|
-
db: Database,
|
|
38
|
-
module: AnyVirtualTableModule,
|
|
39
|
-
manager: MemoryTableManager // Pass the shared manager instance
|
|
40
|
-
) {
|
|
41
|
-
// Use manager's schema and name for the base class constructor
|
|
42
|
-
super(db, module, manager.schemaName, manager.tableName);
|
|
43
|
-
this.manager = manager;
|
|
44
|
-
// Set the tableSchema directly from the manager's current canonical schema
|
|
45
|
-
// This ensures the VirtualTable base class has the correct schema reference.
|
|
46
|
-
this.tableSchema = manager.tableSchema;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Returns the canonical schema from the manager */
|
|
50
|
-
getSchema(): TableSchema | undefined {
|
|
51
|
-
// Always return the potentially updated schema from the manager
|
|
52
|
-
return this.manager.tableSchema;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Checks read-only status via the manager */
|
|
56
|
-
isReadOnly(): boolean {
|
|
57
|
-
// Access readOnly via a public method on the manager
|
|
58
|
-
return this.manager.isReadOnly;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Ensures the connection to the manager is established */
|
|
62
|
-
private async ensureConnection(): Promise<MemoryTableConnection> {
|
|
63
|
-
if (!this.connection) {
|
|
64
|
-
// Check if there's already an active connection for this table in the database
|
|
65
|
-
const existingConnections = this.db.getConnectionsForTable(this.tableName);
|
|
66
|
-
if (existingConnections.length > 0 && existingConnections[0] instanceof MemoryVirtualTableConnection) {
|
|
67
|
-
const memoryVirtualConnection = existingConnections[0] as MemoryVirtualTableConnection;
|
|
68
|
-
this.connection = memoryVirtualConnection.getMemoryConnection();
|
|
69
|
-
logger.debugLog(`ensureConnection: Reused existing connection ${this.connection.connectionId} for table ${this.tableName}`);
|
|
70
|
-
} else {
|
|
71
|
-
// Establish connection state with the manager upon first use
|
|
72
|
-
this.connection = this.manager.connect();
|
|
73
|
-
|
|
74
|
-
// Create a VirtualTableConnection wrapper and register it with the database
|
|
75
|
-
const vtabConnection = new MemoryVirtualTableConnection(this.tableName, this.connection);
|
|
76
|
-
await this.db.registerConnection(vtabConnection);
|
|
77
|
-
|
|
78
|
-
logger.debugLog(`ensureConnection: Created and registered new connection ${this.connection.connectionId} for table ${this.tableName}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return this.connection;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Sets an existing connection for this table instance (for transaction reuse) */
|
|
85
|
-
setConnection(memoryConnection: MemoryTableConnection): void {
|
|
86
|
-
logger.debugLog(`Setting connection ${memoryConnection.connectionId} for table ${this.tableName}`);
|
|
87
|
-
this.connection = memoryConnection;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Creates a new VirtualTableConnection for transaction support */
|
|
91
|
-
createConnection(): VirtualTableConnection {
|
|
92
|
-
const memoryConnection = this.manager.connect();
|
|
93
|
-
return new MemoryVirtualTableConnection(this.tableName, memoryConnection);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Gets the current connection if this table maintains one internally */
|
|
97
|
-
getConnection(): VirtualTableConnection | undefined {
|
|
98
|
-
if (!this.connection) {
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
return new MemoryVirtualTableConnection(this.tableName, this.connection);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Direct async iteration for query execution
|
|
105
|
-
async* query(filterInfo: FilterInfo): AsyncIterable<Row> {
|
|
106
|
-
const conn = await this.ensureConnection();
|
|
107
|
-
logger.debugLog(`query using connection ${conn.connectionId} (pending: ${conn.pendingTransactionLayer?.getLayerId()}, read: ${conn.readLayer.getLayerId()})`);
|
|
108
|
-
const currentSchema = this.manager.tableSchema;
|
|
109
|
-
if (!currentSchema) {
|
|
110
|
-
logger.error('query', this.tableName, 'Table schema is undefined');
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
const plan = buildScanPlanFromFilterInfo(filterInfo, currentSchema);
|
|
114
|
-
logger.debugLog(`query invoked for ${this.tableName} with plan: ${safeJsonStringify(plan)}`);
|
|
115
|
-
|
|
116
|
-
const startLayer = conn.pendingTransactionLayer ?? conn.readLayer;
|
|
117
|
-
logger.debugLog(`query reading from layer ${startLayer.getLayerId()}`);
|
|
118
|
-
|
|
119
|
-
// Delegate scanning to the manager, which handles layer recursion
|
|
120
|
-
yield* this.manager.scanLayer(startLayer, plan);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Note: getBestAccessPlan is handled by the MemoryTableModule, not the table instance.
|
|
124
|
-
|
|
125
|
-
/** Performs mutation through the connection's transaction layer */
|
|
126
|
-
async update(
|
|
127
|
-
operation: 'insert' | 'update' | 'delete',
|
|
128
|
-
values: Row | undefined,
|
|
129
|
-
oldKeyValues?: Row,
|
|
130
|
-
onConflict?: ConflictResolution
|
|
131
|
-
): Promise<Row | undefined> {
|
|
132
|
-
const conn = await this.ensureConnection();
|
|
133
|
-
// Delegate mutation to the manager.
|
|
134
|
-
// This assumes manager.performMutation will be updated to this signature and logic.
|
|
135
|
-
return this.manager.performMutation(conn, operation, values, oldKeyValues, onConflict);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Begins a transaction for this connection */
|
|
139
|
-
async begin(): Promise<void> {
|
|
140
|
-
(await this.ensureConnection()).begin();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/** Commits this connection's transaction */
|
|
144
|
-
async commit(): Promise<void> {
|
|
145
|
-
// Only commit if a connection has actually been established
|
|
146
|
-
if (this.connection) {
|
|
147
|
-
await this.connection.commit();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Rolls back this connection's transaction */
|
|
152
|
-
async rollback(): Promise<void> {
|
|
153
|
-
// Only rollback if a connection has actually been established
|
|
154
|
-
if (this.connection) {
|
|
155
|
-
this.connection.rollback();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** Sync operation (currently no-op for memory table layers) */
|
|
160
|
-
async sync(): Promise<void> {
|
|
161
|
-
// This might trigger background collapse in the manager in the future
|
|
162
|
-
// await this.manager.tryCollapseLayers(); // Optional: trigger collapse on sync?
|
|
163
|
-
return Promise.resolve();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** Renames the underlying table via the manager */
|
|
167
|
-
async rename(newName: string): Promise<void> {
|
|
168
|
-
logger.operation('Rename', this.tableName, { newName });
|
|
169
|
-
await this.manager.renameTable(newName);
|
|
170
|
-
// Update this instance's schema reference after rename
|
|
171
|
-
this.tableSchema = this.manager.tableSchema;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// --- Savepoint operations ---
|
|
175
|
-
async savepoint(savepointIndex: number): Promise<void> {
|
|
176
|
-
const conn = await this.ensureConnection();
|
|
177
|
-
conn.createSavepoint(savepointIndex);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async release(savepointIndex: number): Promise<void> {
|
|
181
|
-
if (!this.connection) return; // No connection, no savepoints to release
|
|
182
|
-
this.connection.releaseSavepoint(savepointIndex);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async rollbackTo(savepointIndex: number): Promise<void> {
|
|
186
|
-
if (!this.connection) return; // No connection, no savepoints to rollback to
|
|
187
|
-
this.connection.rollbackToSavepoint(savepointIndex);
|
|
188
|
-
}
|
|
189
|
-
// --- End Savepoint operations ---
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
/** Handles schema changes via the manager */
|
|
193
|
-
async alterSchema(changeInfo: SchemaChangeInfo): Promise<void> {
|
|
194
|
-
const originalManagerSchema = this.manager.tableSchema; // For potential error recovery
|
|
195
|
-
try {
|
|
196
|
-
switch (changeInfo.type) {
|
|
197
|
-
case 'addColumn':
|
|
198
|
-
await this.manager.addColumn(changeInfo.columnDef);
|
|
199
|
-
break;
|
|
200
|
-
case 'dropColumn':
|
|
201
|
-
await this.manager.dropColumn(changeInfo.columnName);
|
|
202
|
-
break;
|
|
203
|
-
case 'renameColumn':
|
|
204
|
-
if (!('newColumnDefAst' in changeInfo)) {
|
|
205
|
-
throw new QuereusError('SchemaChangeInfo for renameColumn missing newColumnDefAst', StatusCode.INTERNAL);
|
|
206
|
-
}
|
|
207
|
-
await this.manager.renameColumn(changeInfo.oldName, changeInfo.newColumnDefAst as ASTColumnDef);
|
|
208
|
-
break;
|
|
209
|
-
default: {
|
|
210
|
-
const exhaustiveCheck: never = changeInfo;
|
|
211
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
|
-
throw new QuereusError(`Unhandled schema change: ${(exhaustiveCheck as any)?.type}`, StatusCode.INTERNAL);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
this.tableSchema = this.manager.tableSchema; // Refresh local schema ref
|
|
216
|
-
} catch (e) {
|
|
217
|
-
logger.error('Schema Change', this.tableName, e);
|
|
218
|
-
// Manager DDL methods should handle reverting their own BaseLayer schema updates on error.
|
|
219
|
-
// Refresh local schema ref to ensure it's consistent with manager after potential error/revert.
|
|
220
|
-
this.tableSchema = originalManagerSchema;
|
|
221
|
-
// It might be safer for manager DDL to not alter its own this.tableSchema until baseLayer op succeeds.
|
|
222
|
-
// And if baseLayer op fails, manager DDL reverts baseLayer.tableSchema.
|
|
223
|
-
// Then here, we always sync from manager: this.tableSchema = this.manager.tableSchema;
|
|
224
|
-
throw e;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** Disconnects this connection instance from the manager */
|
|
229
|
-
async disconnect(): Promise<void> {
|
|
230
|
-
if (this.connection) {
|
|
231
|
-
// Manager handles cleanup and potential layer collapse trigger
|
|
232
|
-
await this.manager.disconnect(this.connection.connectionId);
|
|
233
|
-
this.connection = null; // Clear connection reference on this instance
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// --- Index DDL methods delegate to the manager ---
|
|
238
|
-
async createIndex(indexSchema: IndexSchema): Promise<void> {
|
|
239
|
-
logger.operation('Create Index', this.tableName, { indexName: indexSchema.name });
|
|
240
|
-
await this.manager.createIndex(indexSchema);
|
|
241
|
-
this.tableSchema = this.manager.tableSchema; // Refresh local schema ref
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async dropIndex(indexName: string): Promise<void> {
|
|
245
|
-
logger.operation('Drop Index', this.tableName, { indexName });
|
|
246
|
-
await this.manager.dropIndex(indexName);
|
|
247
|
-
// Update schema reference
|
|
248
|
-
this.tableSchema = this.manager.tableSchema;
|
|
249
|
-
}
|
|
250
|
-
// --- End Index DDL methods ---
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Helper function (moved from MemoryTableCursor and adapted)
|
|
254
|
-
// function buildScanPlanInternal(filterInfo: FilterInfo, tableSchema: TableSchema): ScanPlan { ... MOVED ... }
|
|
255
|
-
|
|
256
|
-
|
|
1
|
+
import { VirtualTable } from '../table.js';
|
|
2
|
+
import type { AnyVirtualTableModule, SchemaChangeInfo } from '../module.js';
|
|
3
|
+
import type { Database } from '../../core/database.js';
|
|
4
|
+
import type { Row } from '../../common/types.js';
|
|
5
|
+
import { type IndexSchema, type TableSchema } from '../../schema/table.js';
|
|
6
|
+
import { MemoryTableManager } from './layer/manager.js';
|
|
7
|
+
import type { MemoryTableConnection } from './layer/connection.js';
|
|
8
|
+
import { QuereusError } from '../../common/errors.js';
|
|
9
|
+
import { StatusCode } from '../../common/types.js';
|
|
10
|
+
import type { FilterInfo } from '../filter-info.js';
|
|
11
|
+
import { buildScanPlanFromFilterInfo } from './layer/scan-plan.js';
|
|
12
|
+
import type { ColumnDef as ASTColumnDef } from '../../parser/ast.js'; // Assuming this will be updated for renameColumn
|
|
13
|
+
import { createMemoryTableLoggers } from './utils/logging.js';
|
|
14
|
+
import { safeJsonStringify } from '../../util/serialization.js';
|
|
15
|
+
import type { VirtualTableConnection } from '../connection.js';
|
|
16
|
+
import { MemoryVirtualTableConnection } from './connection.js';
|
|
17
|
+
import type { ConflictResolution } from '../../common/constants.js';
|
|
18
|
+
|
|
19
|
+
const logger = createMemoryTableLoggers('table');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a connection-specific instance of an in-memory table using the layer-based MVCC model.
|
|
23
|
+
* This class acts as a thin wrapper around the shared MemoryTableManager,
|
|
24
|
+
* holding the connection state.
|
|
25
|
+
*/
|
|
26
|
+
export class MemoryTable extends VirtualTable {
|
|
27
|
+
/** @internal The shared manager handling layers, schema, and global state */
|
|
28
|
+
public readonly manager: MemoryTableManager;
|
|
29
|
+
/** @internal Connection state specific to this table instance (lazily initialized) */
|
|
30
|
+
private connection: MemoryTableConnection | null = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @internal - Use MemoryTableModule.connect or create
|
|
34
|
+
* Creates a connection-specific instance linked to a manager.
|
|
35
|
+
*/
|
|
36
|
+
constructor(
|
|
37
|
+
db: Database,
|
|
38
|
+
module: AnyVirtualTableModule,
|
|
39
|
+
manager: MemoryTableManager // Pass the shared manager instance
|
|
40
|
+
) {
|
|
41
|
+
// Use manager's schema and name for the base class constructor
|
|
42
|
+
super(db, module, manager.schemaName, manager.tableName);
|
|
43
|
+
this.manager = manager;
|
|
44
|
+
// Set the tableSchema directly from the manager's current canonical schema
|
|
45
|
+
// This ensures the VirtualTable base class has the correct schema reference.
|
|
46
|
+
this.tableSchema = manager.tableSchema;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Returns the canonical schema from the manager */
|
|
50
|
+
getSchema(): TableSchema | undefined {
|
|
51
|
+
// Always return the potentially updated schema from the manager
|
|
52
|
+
return this.manager.tableSchema;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Checks read-only status via the manager */
|
|
56
|
+
isReadOnly(): boolean {
|
|
57
|
+
// Access readOnly via a public method on the manager
|
|
58
|
+
return this.manager.isReadOnly;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Ensures the connection to the manager is established */
|
|
62
|
+
private async ensureConnection(): Promise<MemoryTableConnection> {
|
|
63
|
+
if (!this.connection) {
|
|
64
|
+
// Check if there's already an active connection for this table in the database
|
|
65
|
+
const existingConnections = this.db.getConnectionsForTable(this.tableName);
|
|
66
|
+
if (existingConnections.length > 0 && existingConnections[0] instanceof MemoryVirtualTableConnection) {
|
|
67
|
+
const memoryVirtualConnection = existingConnections[0] as MemoryVirtualTableConnection;
|
|
68
|
+
this.connection = memoryVirtualConnection.getMemoryConnection();
|
|
69
|
+
logger.debugLog(`ensureConnection: Reused existing connection ${this.connection.connectionId} for table ${this.tableName}`);
|
|
70
|
+
} else {
|
|
71
|
+
// Establish connection state with the manager upon first use
|
|
72
|
+
this.connection = this.manager.connect();
|
|
73
|
+
|
|
74
|
+
// Create a VirtualTableConnection wrapper and register it with the database
|
|
75
|
+
const vtabConnection = new MemoryVirtualTableConnection(this.tableName, this.connection);
|
|
76
|
+
await this.db.registerConnection(vtabConnection);
|
|
77
|
+
|
|
78
|
+
logger.debugLog(`ensureConnection: Created and registered new connection ${this.connection.connectionId} for table ${this.tableName}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return this.connection;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Sets an existing connection for this table instance (for transaction reuse) */
|
|
85
|
+
setConnection(memoryConnection: MemoryTableConnection): void {
|
|
86
|
+
logger.debugLog(`Setting connection ${memoryConnection.connectionId} for table ${this.tableName}`);
|
|
87
|
+
this.connection = memoryConnection;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Creates a new VirtualTableConnection for transaction support */
|
|
91
|
+
createConnection(): VirtualTableConnection {
|
|
92
|
+
const memoryConnection = this.manager.connect();
|
|
93
|
+
return new MemoryVirtualTableConnection(this.tableName, memoryConnection);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Gets the current connection if this table maintains one internally */
|
|
97
|
+
getConnection(): VirtualTableConnection | undefined {
|
|
98
|
+
if (!this.connection) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return new MemoryVirtualTableConnection(this.tableName, this.connection);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Direct async iteration for query execution
|
|
105
|
+
async* query(filterInfo: FilterInfo): AsyncIterable<Row> {
|
|
106
|
+
const conn = await this.ensureConnection();
|
|
107
|
+
logger.debugLog(`query using connection ${conn.connectionId} (pending: ${conn.pendingTransactionLayer?.getLayerId()}, read: ${conn.readLayer.getLayerId()})`);
|
|
108
|
+
const currentSchema = this.manager.tableSchema;
|
|
109
|
+
if (!currentSchema) {
|
|
110
|
+
logger.error('query', this.tableName, 'Table schema is undefined');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const plan = buildScanPlanFromFilterInfo(filterInfo, currentSchema);
|
|
114
|
+
logger.debugLog(`query invoked for ${this.tableName} with plan: ${safeJsonStringify(plan)}`);
|
|
115
|
+
|
|
116
|
+
const startLayer = conn.pendingTransactionLayer ?? conn.readLayer;
|
|
117
|
+
logger.debugLog(`query reading from layer ${startLayer.getLayerId()}`);
|
|
118
|
+
|
|
119
|
+
// Delegate scanning to the manager, which handles layer recursion
|
|
120
|
+
yield* this.manager.scanLayer(startLayer, plan);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Note: getBestAccessPlan is handled by the MemoryTableModule, not the table instance.
|
|
124
|
+
|
|
125
|
+
/** Performs mutation through the connection's transaction layer */
|
|
126
|
+
async update(
|
|
127
|
+
operation: 'insert' | 'update' | 'delete',
|
|
128
|
+
values: Row | undefined,
|
|
129
|
+
oldKeyValues?: Row,
|
|
130
|
+
onConflict?: ConflictResolution
|
|
131
|
+
): Promise<Row | undefined> {
|
|
132
|
+
const conn = await this.ensureConnection();
|
|
133
|
+
// Delegate mutation to the manager.
|
|
134
|
+
// This assumes manager.performMutation will be updated to this signature and logic.
|
|
135
|
+
return this.manager.performMutation(conn, operation, values, oldKeyValues, onConflict);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Begins a transaction for this connection */
|
|
139
|
+
async begin(): Promise<void> {
|
|
140
|
+
(await this.ensureConnection()).begin();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Commits this connection's transaction */
|
|
144
|
+
async commit(): Promise<void> {
|
|
145
|
+
// Only commit if a connection has actually been established
|
|
146
|
+
if (this.connection) {
|
|
147
|
+
await this.connection.commit();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Rolls back this connection's transaction */
|
|
152
|
+
async rollback(): Promise<void> {
|
|
153
|
+
// Only rollback if a connection has actually been established
|
|
154
|
+
if (this.connection) {
|
|
155
|
+
this.connection.rollback();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Sync operation (currently no-op for memory table layers) */
|
|
160
|
+
async sync(): Promise<void> {
|
|
161
|
+
// This might trigger background collapse in the manager in the future
|
|
162
|
+
// await this.manager.tryCollapseLayers(); // Optional: trigger collapse on sync?
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Renames the underlying table via the manager */
|
|
167
|
+
async rename(newName: string): Promise<void> {
|
|
168
|
+
logger.operation('Rename', this.tableName, { newName });
|
|
169
|
+
await this.manager.renameTable(newName);
|
|
170
|
+
// Update this instance's schema reference after rename
|
|
171
|
+
this.tableSchema = this.manager.tableSchema;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// --- Savepoint operations ---
|
|
175
|
+
async savepoint(savepointIndex: number): Promise<void> {
|
|
176
|
+
const conn = await this.ensureConnection();
|
|
177
|
+
conn.createSavepoint(savepointIndex);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async release(savepointIndex: number): Promise<void> {
|
|
181
|
+
if (!this.connection) return; // No connection, no savepoints to release
|
|
182
|
+
this.connection.releaseSavepoint(savepointIndex);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async rollbackTo(savepointIndex: number): Promise<void> {
|
|
186
|
+
if (!this.connection) return; // No connection, no savepoints to rollback to
|
|
187
|
+
this.connection.rollbackToSavepoint(savepointIndex);
|
|
188
|
+
}
|
|
189
|
+
// --- End Savepoint operations ---
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
/** Handles schema changes via the manager */
|
|
193
|
+
async alterSchema(changeInfo: SchemaChangeInfo): Promise<void> {
|
|
194
|
+
const originalManagerSchema = this.manager.tableSchema; // For potential error recovery
|
|
195
|
+
try {
|
|
196
|
+
switch (changeInfo.type) {
|
|
197
|
+
case 'addColumn':
|
|
198
|
+
await this.manager.addColumn(changeInfo.columnDef);
|
|
199
|
+
break;
|
|
200
|
+
case 'dropColumn':
|
|
201
|
+
await this.manager.dropColumn(changeInfo.columnName);
|
|
202
|
+
break;
|
|
203
|
+
case 'renameColumn':
|
|
204
|
+
if (!('newColumnDefAst' in changeInfo)) {
|
|
205
|
+
throw new QuereusError('SchemaChangeInfo for renameColumn missing newColumnDefAst', StatusCode.INTERNAL);
|
|
206
|
+
}
|
|
207
|
+
await this.manager.renameColumn(changeInfo.oldName, changeInfo.newColumnDefAst as ASTColumnDef);
|
|
208
|
+
break;
|
|
209
|
+
default: {
|
|
210
|
+
const exhaustiveCheck: never = changeInfo;
|
|
211
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
|
+
throw new QuereusError(`Unhandled schema change: ${(exhaustiveCheck as any)?.type}`, StatusCode.INTERNAL);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.tableSchema = this.manager.tableSchema; // Refresh local schema ref
|
|
216
|
+
} catch (e) {
|
|
217
|
+
logger.error('Schema Change', this.tableName, e);
|
|
218
|
+
// Manager DDL methods should handle reverting their own BaseLayer schema updates on error.
|
|
219
|
+
// Refresh local schema ref to ensure it's consistent with manager after potential error/revert.
|
|
220
|
+
this.tableSchema = originalManagerSchema;
|
|
221
|
+
// It might be safer for manager DDL to not alter its own this.tableSchema until baseLayer op succeeds.
|
|
222
|
+
// And if baseLayer op fails, manager DDL reverts baseLayer.tableSchema.
|
|
223
|
+
// Then here, we always sync from manager: this.tableSchema = this.manager.tableSchema;
|
|
224
|
+
throw e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Disconnects this connection instance from the manager */
|
|
229
|
+
async disconnect(): Promise<void> {
|
|
230
|
+
if (this.connection) {
|
|
231
|
+
// Manager handles cleanup and potential layer collapse trigger
|
|
232
|
+
await this.manager.disconnect(this.connection.connectionId);
|
|
233
|
+
this.connection = null; // Clear connection reference on this instance
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// --- Index DDL methods delegate to the manager ---
|
|
238
|
+
async createIndex(indexSchema: IndexSchema): Promise<void> {
|
|
239
|
+
logger.operation('Create Index', this.tableName, { indexName: indexSchema.name });
|
|
240
|
+
await this.manager.createIndex(indexSchema);
|
|
241
|
+
this.tableSchema = this.manager.tableSchema; // Refresh local schema ref
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async dropIndex(indexName: string): Promise<void> {
|
|
245
|
+
logger.operation('Drop Index', this.tableName, { indexName });
|
|
246
|
+
await this.manager.dropIndex(indexName);
|
|
247
|
+
// Update schema reference
|
|
248
|
+
this.tableSchema = this.manager.tableSchema;
|
|
249
|
+
}
|
|
250
|
+
// --- End Index DDL methods ---
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Helper function (moved from MemoryTableCursor and adapted)
|
|
254
|
+
// function buildScanPlanInternal(filterInfo: FilterInfo, tableSchema: TableSchema): ScanPlan { ... MOVED ... }
|
|
255
|
+
|
|
256
|
+
|