@quereus/quereus 0.4.6 → 0.4.9
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 +2 -2
- package/dist/src/config/loader.d.ts +41 -0
- package/dist/src/config/loader.d.ts.map +1 -0
- package/dist/src/config/loader.js +102 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/core/database.d.ts +1 -1
- package/dist/src/core/database.js +2 -2
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/planner/building/create-view.d.ts.map +1 -1
- package/dist/src/planner/building/create-view.js +3 -25
- package/dist/src/planner/building/create-view.js.map +1 -1
- package/dist/src/planner/building/select.d.ts.map +1 -1
- package/dist/src/planner/building/select.js +20 -1
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/nodes/dml-executor-node.d.ts +1 -1
- package/dist/src/planner/nodes/dml-executor-node.js +1 -1
- package/dist/src/runtime/emit/dml-executor.js +3 -3
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/remote-query.d.ts +1 -1
- package/dist/src/runtime/emit/remote-query.js +5 -5
- package/dist/src/runtime/emit/remote-query.js.map +1 -1
- package/dist/src/runtime/emit/scan.js +10 -10
- package/dist/src/runtime/emit/scan.js.map +1 -1
- package/dist/src/runtime/emit/update.js +1 -1
- package/dist/src/runtime/emit/update.js.map +1 -1
- package/dist/src/runtime/utils.js +6 -6
- package/dist/src/runtime/utils.js.map +1 -1
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +18 -3
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/manager.d.ts +4 -4
- package/dist/src/schema/manager.js +20 -20
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/schema-differ.js +3 -3
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/util/ast-stringify.d.ts +1 -0
- package/dist/src/util/ast-stringify.d.ts.map +1 -1
- package/dist/src/util/ast-stringify.js +1 -1
- package/dist/src/util/ast-stringify.js.map +1 -1
- package/dist/src/vtab/best-access-plan.d.ts +0 -6
- package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
- package/dist/src/vtab/best-access-plan.js +0 -27
- package/dist/src/vtab/best-access-plan.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +4 -10
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +4 -16
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/memory/table.d.ts +14 -14
- package/dist/src/vtab/memory/table.d.ts.map +1 -1
- package/dist/src/vtab/memory/table.js +21 -21
- package/dist/src/vtab/memory/table.js.map +1 -1
- package/dist/src/vtab/module.d.ts +6 -18
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/dist/src/vtab/table.d.ts +17 -17
- package/dist/src/vtab/table.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/config/loader.ts +140 -0
- package/src/core/database.ts +2 -2
- package/src/index.ts +9 -0
- package/src/planner/building/create-view.ts +3 -30
- package/src/planner/building/select.ts +29 -1
- package/src/planner/nodes/dml-executor-node.ts +1 -1
- package/src/runtime/emit/dml-executor.ts +3 -3
- package/src/runtime/emit/remote-query.ts +5 -5
- package/src/runtime/emit/scan.ts +10 -10
- package/src/runtime/emit/update.ts +1 -1
- package/src/runtime/utils.ts +6 -6
- package/src/schema/catalog.ts +17 -3
- package/src/schema/manager.ts +20 -20
- package/src/schema/schema-differ.ts +3 -3
- package/src/util/ast-stringify.ts +1 -1
- package/src/vtab/best-access-plan.ts +0 -30
- package/src/vtab/memory/module.ts +4 -17
- package/src/vtab/memory/table.ts +21 -21
- package/src/vtab/module.ts +7 -17
- package/src/vtab/table.ts +17 -17
|
@@ -22,7 +22,7 @@ export function emitDmlExecutor(plan: DmlExecutorNode, ctx: EmissionContext): In
|
|
|
22
22
|
try {
|
|
23
23
|
for await (const flatRow of rows) {
|
|
24
24
|
const newRow = extractNewRowFromFlat(flatRow, tableSchema.columns.length);
|
|
25
|
-
await vtab.
|
|
25
|
+
await vtab.update!('insert', newRow, undefined, plan.onConflict ?? ConflictResolution.ABORT);
|
|
26
26
|
// Track change (INSERT): record NEW primary key
|
|
27
27
|
const pkValues = tableSchema.primaryKeyDefinition.map(def => newRow[def.index]);
|
|
28
28
|
ctx.db._recordInsert(`${tableSchema.schemaName}.${tableSchema.name}`, pkValues);
|
|
@@ -48,7 +48,7 @@ export function emitDmlExecutor(plan: DmlExecutorNode, ctx: EmissionContext): In
|
|
|
48
48
|
}
|
|
49
49
|
return oldRow[pkColIdx];
|
|
50
50
|
});
|
|
51
|
-
await vtab.
|
|
51
|
+
await vtab.update!('update', newRow, keyValues, ConflictResolution.ABORT);
|
|
52
52
|
// Track change (UPDATE): record OLD and NEW primary keys
|
|
53
53
|
const newKeyValues: SqlValue[] = tableSchema.primaryKeyDefinition.map(pkColDef => newRow[pkColDef.index]);
|
|
54
54
|
ctx.db._recordUpdate(`${tableSchema.schemaName}.${tableSchema.name}`, keyValues, newKeyValues);
|
|
@@ -72,7 +72,7 @@ export function emitDmlExecutor(plan: DmlExecutorNode, ctx: EmissionContext): In
|
|
|
72
72
|
}
|
|
73
73
|
return oldRow[pkColIdx];
|
|
74
74
|
});
|
|
75
|
-
await vtab.
|
|
75
|
+
await vtab.update!('delete', undefined, keyValues, ConflictResolution.ABORT);
|
|
76
76
|
// Track change (DELETE): record OLD primary key
|
|
77
77
|
ctx.db._recordDelete(`${tableSchema.schemaName}.${tableSchema.name}`, keyValues);
|
|
78
78
|
yield flatRow;
|
|
@@ -8,7 +8,7 @@ import type { AnyVirtualTableModule } from '../../vtab/module.js';
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Emitter for RemoteQueryNode.
|
|
11
|
-
* Calls the virtual table's
|
|
11
|
+
* Calls the virtual table's executePlan() method to execute the pushed-down pipeline.
|
|
12
12
|
*/
|
|
13
13
|
export function emitRemoteQuery(plan: RemoteQueryNode, _ctx: EmissionContext): Instruction {
|
|
14
14
|
async function* run(rctx: RuntimeContext): AsyncIterable<Row> {
|
|
@@ -19,7 +19,7 @@ export function emitRemoteQuery(plan: RemoteQueryNode, _ctx: EmissionContext): I
|
|
|
19
19
|
const vtabModule = (moduleCtx as { vtabModule?: AnyVirtualTableModule } | undefined)?.vtabModule ?? tableRef.vtabModule;
|
|
20
20
|
|
|
21
21
|
// Connect to the table to get the instance
|
|
22
|
-
const table = vtabModule.
|
|
22
|
+
const table = vtabModule.connect(
|
|
23
23
|
rctx.db,
|
|
24
24
|
undefined, // pAux
|
|
25
25
|
tableSchema.vtabModuleName,
|
|
@@ -28,15 +28,15 @@ export function emitRemoteQuery(plan: RemoteQueryNode, _ctx: EmissionContext): I
|
|
|
28
28
|
{} // empty config for now
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
-
if (!table.
|
|
31
|
+
if (!table.executePlan) {
|
|
32
32
|
throw new QuereusError(
|
|
33
|
-
`Virtual table module for '${tableSchema.name}' does not implement
|
|
33
|
+
`Virtual table module for '${tableSchema.name}' does not implement executePlan() ` +
|
|
34
34
|
`despite indicating support via supports() method.`,
|
|
35
35
|
StatusCode.INTERNAL
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
yield* table.
|
|
39
|
+
yield* table.executePlan(rctx.db, plan.source, plan.moduleCtx);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return {
|
package/src/runtime/emit/scan.ts
CHANGED
|
@@ -38,14 +38,14 @@ export function emitSeqScan(plan: SeqScanNode | IndexScanNode | IndexSeekNode, c
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const module = capturedModuleInfo.module;
|
|
41
|
-
if (typeof module.
|
|
42
|
-
throw new QuereusError(`Virtual table module '${schema.vtabModuleName}' does not implement
|
|
41
|
+
if (typeof module.connect !== 'function') {
|
|
42
|
+
throw new QuereusError(`Virtual table module '${schema.vtabModuleName}' does not implement connect`, StatusCode.MISUSE);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
let vtabInstance: VirtualTable;
|
|
46
46
|
try {
|
|
47
47
|
const options: BaseModuleConfig = (schema.vtabArgs ?? {}) as BaseModuleConfig;
|
|
48
|
-
vtabInstance = module.
|
|
48
|
+
vtabInstance = module.connect(
|
|
49
49
|
runtimeCtx.db,
|
|
50
50
|
capturedModuleInfo.auxData,
|
|
51
51
|
schema.vtabModuleName,
|
|
@@ -55,13 +55,13 @@ export function emitSeqScan(plan: SeqScanNode | IndexScanNode | IndexSeekNode, c
|
|
|
55
55
|
);
|
|
56
56
|
} catch (e: any) {
|
|
57
57
|
const message = e instanceof Error ? e.message : String(e);
|
|
58
|
-
throw new QuereusError(`Module '${schema.vtabModuleName}'
|
|
58
|
+
throw new QuereusError(`Module '${schema.vtabModuleName}' connect failed for table '${schema.name}': ${message}`, e instanceof QuereusError ? e.code : StatusCode.ERROR, e instanceof Error ? e : undefined);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
if (typeof vtabInstance.
|
|
62
|
-
// Fallback or error if
|
|
63
|
-
// Later, we could implement the
|
|
64
|
-
throw new QuereusError(`Virtual table '${schema.name}' does not support
|
|
61
|
+
if (typeof vtabInstance.query !== 'function') {
|
|
62
|
+
// Fallback or error if query is not available. For now, throwing an error.
|
|
63
|
+
// Later, we could implement the open/filter/next loop here as a fallback.
|
|
64
|
+
throw new QuereusError(`Virtual table '${schema.name}' does not support query.`, StatusCode.UNSUPPORTED);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
const rowSlot = createRowSlot(runtimeCtx, rowDescriptor);
|
|
@@ -74,14 +74,14 @@ export function emitSeqScan(plan: SeqScanNode | IndexScanNode | IndexSeekNode, c
|
|
|
74
74
|
return plan.filterInfo;
|
|
75
75
|
})();
|
|
76
76
|
|
|
77
|
-
const asyncRowIterable = vtabInstance.
|
|
77
|
+
const asyncRowIterable = vtabInstance.query(effectiveFilterInfo);
|
|
78
78
|
for await (const row of asyncRowIterable) {
|
|
79
79
|
rowSlot.set(row);
|
|
80
80
|
yield row;
|
|
81
81
|
}
|
|
82
82
|
} catch (e: any) {
|
|
83
83
|
const message = e instanceof Error ? e.message : String(e);
|
|
84
|
-
throw new QuereusError(`Error during
|
|
84
|
+
throw new QuereusError(`Error during query on table '${schema.name}': ${message}`, e instanceof QuereusError ? e.code : StatusCode.ERROR, e instanceof Error ? e : undefined);
|
|
85
85
|
} finally {
|
|
86
86
|
rowSlot.close();
|
|
87
87
|
// Properly disconnect the VirtualTable instance
|
|
@@ -51,7 +51,7 @@ export function emitUpdate(plan: UpdateNode, ctx: EmissionContext): Instruction
|
|
|
51
51
|
|
|
52
52
|
// Yield the flat row for constraint checking
|
|
53
53
|
// NOTE: UpdateNode only transforms rows - it does NOT execute the actual update
|
|
54
|
-
// The UpdateExecutorNode is responsible for calling vtab.
|
|
54
|
+
// The UpdateExecutorNode is responsible for calling vtab.update
|
|
55
55
|
yield flatRow;
|
|
56
56
|
});
|
|
57
57
|
}
|
package/src/runtime/utils.ts
CHANGED
|
@@ -86,11 +86,11 @@ export async function getVTable(ctx: RuntimeContext, tableSchema: TableSchema):
|
|
|
86
86
|
throw new QuereusError(`Virtual table module '${tableSchema.vtabModuleName}' not found for table '${tableSchema.name}'`, StatusCode.ERROR);
|
|
87
87
|
}
|
|
88
88
|
const module = moduleInfo.module;
|
|
89
|
-
if (typeof module.
|
|
90
|
-
throw new QuereusError(`Virtual table module '${tableSchema.vtabModuleName}' does not implement
|
|
89
|
+
if (typeof module.connect !== 'function') {
|
|
90
|
+
throw new QuereusError(`Virtual table module '${tableSchema.vtabModuleName}' does not implement connect`, StatusCode.MISUSE);
|
|
91
91
|
}
|
|
92
92
|
const vtabArgs = tableSchema.vtabArgs || {};
|
|
93
|
-
const vtabInstance = module.
|
|
93
|
+
const vtabInstance = module.connect(ctx.db, moduleInfo.auxData, tableSchema.vtabModuleName, tableSchema.schemaName, tableSchema.name, vtabArgs);
|
|
94
94
|
|
|
95
95
|
// If we have an active connection for this table, inject it into the VirtualTable
|
|
96
96
|
const tableName = tableSchema.name;
|
|
@@ -112,9 +112,9 @@ export async function getVTable(ctx: RuntimeContext, tableSchema: TableSchema):
|
|
|
112
112
|
*/
|
|
113
113
|
export async function disconnectVTable(ctx: RuntimeContext, vtab: VirtualTable): Promise<void> {
|
|
114
114
|
// Disconnect the VirtualTable instance
|
|
115
|
-
if (typeof vtab.
|
|
116
|
-
await vtab.
|
|
117
|
-
errorLog(`Error during
|
|
115
|
+
if (typeof vtab.disconnect === 'function') {
|
|
116
|
+
await vtab.disconnect().catch((e: any) => {
|
|
117
|
+
errorLog(`Error during disconnect for table '${vtab.tableName}': ${e}`);
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
120
|
}
|
package/src/schema/catalog.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Database } from '../core/database.js';
|
|
|
2
2
|
import type { TableSchema } from './table.js';
|
|
3
3
|
import type { ViewSchema } from './view.js';
|
|
4
4
|
import type { IntegrityAssertionSchema } from './assertion.js';
|
|
5
|
-
import { createTableToString } from '../util/ast-stringify.js';
|
|
5
|
+
import { createTableToString, createViewToString } from '../util/ast-stringify.js';
|
|
6
6
|
import type * as AST from '../parser/ast.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -196,9 +196,23 @@ export function generateDeclaredDDL(declaredSchema: AST.DeclareSchemaStmt, targe
|
|
|
196
196
|
case 'declaredIndex':
|
|
197
197
|
// TODO: Implement index DDL generation
|
|
198
198
|
break;
|
|
199
|
-
case 'declaredView':
|
|
200
|
-
//
|
|
199
|
+
case 'declaredView': {
|
|
200
|
+
// Qualify view name with schema if specified
|
|
201
|
+
const viewStmt = item.viewStmt;
|
|
202
|
+
if (targetSchema && targetSchema !== 'main' && !viewStmt.view.schema) {
|
|
203
|
+
const qualifiedStmt: AST.CreateViewStmt = {
|
|
204
|
+
...viewStmt,
|
|
205
|
+
view: {
|
|
206
|
+
...viewStmt.view,
|
|
207
|
+
schema: targetSchema
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
ddlStatements.push(createViewToString(qualifiedStmt));
|
|
211
|
+
} else {
|
|
212
|
+
ddlStatements.push(createViewToString(viewStmt));
|
|
213
|
+
}
|
|
201
214
|
break;
|
|
215
|
+
}
|
|
202
216
|
}
|
|
203
217
|
}
|
|
204
218
|
|
package/src/schema/manager.ts
CHANGED
|
@@ -368,23 +368,23 @@ export class SchemaManager {
|
|
|
368
368
|
|
|
369
369
|
let destroyPromise: Promise<void> | null = null;
|
|
370
370
|
|
|
371
|
-
// Call
|
|
371
|
+
// Call destroy on the module, providing table details
|
|
372
372
|
if (tableSchema.vtabModuleName) { // tableSchema is guaranteed to be defined here
|
|
373
373
|
const moduleRegistration = this.getModule(tableSchema.vtabModuleName);
|
|
374
|
-
if (moduleRegistration && moduleRegistration.module && moduleRegistration.module.
|
|
375
|
-
log(`Calling
|
|
376
|
-
destroyPromise = moduleRegistration.module.
|
|
374
|
+
if (moduleRegistration && moduleRegistration.module && moduleRegistration.module.destroy) {
|
|
375
|
+
log(`Calling destroy for VTab %s.%s via module %s`, schemaName, tableName, tableSchema.vtabModuleName);
|
|
376
|
+
destroyPromise = moduleRegistration.module.destroy(
|
|
377
377
|
this.db,
|
|
378
378
|
moduleRegistration.auxData,
|
|
379
379
|
tableSchema.vtabModuleName,
|
|
380
380
|
schemaName,
|
|
381
381
|
tableName
|
|
382
382
|
).catch(err => {
|
|
383
|
-
errorLog(`Error during VTab module
|
|
384
|
-
// Potentially re-throw or handle as a critical error if
|
|
383
|
+
errorLog(`Error during VTab module destroy for %s.%s: %O`, schemaName, tableName, err);
|
|
384
|
+
// Potentially re-throw or handle as a critical error if destroy failure is problematic
|
|
385
385
|
});
|
|
386
386
|
} else {
|
|
387
|
-
warnLog(`VTab module %s (for table %s.%s) or its
|
|
387
|
+
warnLog(`VTab module %s (for table %s.%s) or its destroy method not found during dropTable.`, tableSchema.vtabModuleName, schemaName, tableName);
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
@@ -408,7 +408,7 @@ export class SchemaManager {
|
|
|
408
408
|
|
|
409
409
|
// Process destruction asynchronously
|
|
410
410
|
if (destroyPromise) {
|
|
411
|
-
void destroyPromise.then(() => log(`
|
|
411
|
+
void destroyPromise.then(() => log(`destroy completed for VTab %s.%s`, schemaName, tableName));
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
return removed; // True if removed from schema, false if not found and ifExists was true.
|
|
@@ -469,11 +469,11 @@ export class SchemaManager {
|
|
|
469
469
|
|
|
470
470
|
/**
|
|
471
471
|
* Creates a new index on an existing table based on an AST.CreateIndexStmt.
|
|
472
|
-
* This method validates the index definition and calls the virtual table's
|
|
472
|
+
* This method validates the index definition and calls the virtual table's createIndex method.
|
|
473
473
|
*
|
|
474
474
|
* @param stmt The AST node for the CREATE INDEX statement.
|
|
475
475
|
* @returns A Promise that resolves when the index is created.
|
|
476
|
-
* @throws QuereusError on errors (e.g., table not found, column not found,
|
|
476
|
+
* @throws QuereusError on errors (e.g., table not found, column not found, createIndex fails).
|
|
477
477
|
*/
|
|
478
478
|
async createIndex(stmt: AST.CreateIndexStmt): Promise<void> {
|
|
479
479
|
const targetSchemaName = stmt.table.schema || this.getCurrentSchemaName();
|
|
@@ -486,8 +486,8 @@ export class SchemaManager {
|
|
|
486
486
|
throw new QuereusError(`no such table: ${tableName}`, StatusCode.ERROR, undefined, stmt.table.loc?.start.line, stmt.table.loc?.start.column);
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
-
// Check if the virtual table module supports
|
|
490
|
-
if (!tableSchema.vtabModule.
|
|
489
|
+
// Check if the virtual table module supports createIndex
|
|
490
|
+
if (!tableSchema.vtabModule.createIndex) {
|
|
491
491
|
throw new QuereusError(`Virtual table module '${tableSchema.vtabModuleName}' for table '${tableName}' does not support CREATE INDEX.`, StatusCode.ERROR, undefined, stmt.table.loc?.start.line, stmt.table.loc?.start.column);
|
|
492
492
|
}
|
|
493
493
|
|
|
@@ -531,8 +531,8 @@ export class SchemaManager {
|
|
|
531
531
|
};
|
|
532
532
|
|
|
533
533
|
try {
|
|
534
|
-
// Call
|
|
535
|
-
await tableSchema.vtabModule.
|
|
534
|
+
// Call createIndex on the virtual table module
|
|
535
|
+
await tableSchema.vtabModule.createIndex(
|
|
536
536
|
this.db,
|
|
537
537
|
targetSchemaName,
|
|
538
538
|
tableName,
|
|
@@ -563,18 +563,18 @@ export class SchemaManager {
|
|
|
563
563
|
} catch (e: unknown) {
|
|
564
564
|
const message = e instanceof Error ? e.message : String(e);
|
|
565
565
|
const code = e instanceof QuereusError ? e.code : StatusCode.ERROR;
|
|
566
|
-
throw new QuereusError(`
|
|
566
|
+
throw new QuereusError(`createIndex failed for index '${indexName}' on table '${tableName}': ${message}`, code, e instanceof Error ? e : undefined, stmt.loc?.start.line, stmt.loc?.start.column);
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
/**
|
|
571
571
|
* Defines a new table in the schema based on an AST.CreateTableStmt.
|
|
572
|
-
* This method encapsulates the logic for interacting with VTab modules (
|
|
572
|
+
* This method encapsulates the logic for interacting with VTab modules (create)
|
|
573
573
|
* and registering the new table schema.
|
|
574
574
|
*
|
|
575
575
|
* @param stmt The AST node for the CREATE TABLE statement.
|
|
576
576
|
* @returns A Promise that resolves to the created TableSchema.
|
|
577
|
-
* @throws QuereusError on errors (e.g., module not found,
|
|
577
|
+
* @throws QuereusError on errors (e.g., module not found, create fails, table exists).
|
|
578
578
|
*/
|
|
579
579
|
async createTable(stmt: AST.CreateTableStmt): Promise<TableSchema> {
|
|
580
580
|
const targetSchemaName = stmt.table.schema || this.getCurrentSchemaName();
|
|
@@ -670,14 +670,14 @@ export class SchemaManager {
|
|
|
670
670
|
|
|
671
671
|
let tableInstance: VirtualTable;
|
|
672
672
|
try {
|
|
673
|
-
tableInstance = moduleInfo.module.
|
|
673
|
+
tableInstance = moduleInfo.module.create(
|
|
674
674
|
this.db,
|
|
675
675
|
baseTableSchema
|
|
676
676
|
);
|
|
677
677
|
} catch (e: unknown) {
|
|
678
678
|
const message = e instanceof Error ? e.message : String(e);
|
|
679
679
|
const code = e instanceof QuereusError ? e.code : StatusCode.ERROR;
|
|
680
|
-
throw new QuereusError(`Module '${moduleName}'
|
|
680
|
+
throw new QuereusError(`Module '${moduleName}' create failed for table '${tableName}': ${message}`, code, e instanceof Error ? e : undefined, stmt.loc?.start.line, stmt.loc?.start.column);
|
|
681
681
|
}
|
|
682
682
|
|
|
683
683
|
const schema = this.getSchema(targetSchemaName);
|
|
@@ -687,7 +687,7 @@ export class SchemaManager {
|
|
|
687
687
|
|
|
688
688
|
const finalRegisteredSchema = tableInstance.tableSchema;
|
|
689
689
|
if (!finalRegisteredSchema) {
|
|
690
|
-
throw new QuereusError(`Module '${moduleName}'
|
|
690
|
+
throw new QuereusError(`Module '${moduleName}' create did not provide a tableSchema for '${tableName}'.`, StatusCode.INTERNAL);
|
|
691
691
|
}
|
|
692
692
|
|
|
693
693
|
// Create a properly typed schema object instead of mutating properties
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SchemaCatalog } from './catalog.js';
|
|
2
2
|
import type * as AST from '../parser/ast.js';
|
|
3
|
-
import { createTableToString } from '../util/ast-stringify.js';
|
|
3
|
+
import { createTableToString, createViewToString } from '../util/ast-stringify.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Represents the difference between a declared schema and actual database state
|
|
@@ -104,8 +104,8 @@ export function computeSchemaDiff(
|
|
|
104
104
|
// Find views to create/drop
|
|
105
105
|
for (const [name, declaredView] of declaredViews) {
|
|
106
106
|
if (!actualViews.has(name)) {
|
|
107
|
-
//
|
|
108
|
-
diff.viewsToCreate.push(
|
|
107
|
+
// Generate proper view DDL using AST stringifier
|
|
108
|
+
diff.viewsToCreate.push(createViewToString(declaredView.viewStmt));
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -629,7 +629,7 @@ function createIndexToString(stmt: AST.CreateIndexStmt): string {
|
|
|
629
629
|
return parts.join(' ');
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
-
function createViewToString(stmt: AST.CreateViewStmt): string {
|
|
632
|
+
export function createViewToString(stmt: AST.CreateViewStmt): string {
|
|
633
633
|
const parts: string[] = ['create'];
|
|
634
634
|
if (stmt.isTemporary) parts.push('temp');
|
|
635
635
|
parts.push('view');
|
|
@@ -238,34 +238,4 @@ export function validateAccessPlan(
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
/**
|
|
242
|
-
* Helper to convert old IndexConstraint to new PredicateConstraint
|
|
243
|
-
* @deprecated Use direct PredicateConstraint construction instead
|
|
244
|
-
*/
|
|
245
|
-
export function convertIndexConstraint(
|
|
246
|
-
constraint: any, // Old IndexConstraint type
|
|
247
|
-
value?: SqlValue
|
|
248
|
-
): PredicateConstraint {
|
|
249
|
-
// Map old constraint operators to new ones
|
|
250
|
-
const opMap: Record<string, ConstraintOp> = {
|
|
251
|
-
'EQ': '=',
|
|
252
|
-
'GT': '>',
|
|
253
|
-
'GE': '>=',
|
|
254
|
-
'LT': '<',
|
|
255
|
-
'LE': '<=',
|
|
256
|
-
'MATCH': 'MATCH',
|
|
257
|
-
'LIKE': 'LIKE',
|
|
258
|
-
'GLOB': 'GLOB',
|
|
259
|
-
'ISNULL': 'IS NULL',
|
|
260
|
-
'ISNOTNULL': 'IS NOT NULL'
|
|
261
|
-
};
|
|
262
241
|
|
|
263
|
-
const op = opMap[constraint.op] || '=';
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
columnIndex: constraint.iColumn,
|
|
267
|
-
op,
|
|
268
|
-
value,
|
|
269
|
-
usable: constraint.usable
|
|
270
|
-
};
|
|
271
|
-
}
|
|
@@ -27,7 +27,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
|
|
|
27
27
|
/**
|
|
28
28
|
* Creates a new memory table definition
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
create(db: Database, tableSchema: TableSchema): MemoryTable {
|
|
31
31
|
// Ensure table doesn't already exist
|
|
32
32
|
const tableKey = `${tableSchema.schemaName}.${tableSchema.name}`.toLowerCase();
|
|
33
33
|
if (this.tables.has(tableKey)) {
|
|
@@ -58,7 +58,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
|
|
|
58
58
|
/**
|
|
59
59
|
* Connects to an existing memory table definition
|
|
60
60
|
*/
|
|
61
|
-
|
|
61
|
+
connect(db: Database, pAux: unknown, moduleName: string, schemaName: string, tableName: string, _options: MemoryTableConfig): MemoryTable {
|
|
62
62
|
const tableKey = `${schemaName}.${tableName}`.toLowerCase();
|
|
63
63
|
const existingManager = this.tables.get(tableKey);
|
|
64
64
|
|
|
@@ -305,20 +305,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
|
|
|
305
305
|
return true;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
/**
|
|
309
|
-
* Determines the best query plan for executing a query against a memory table
|
|
310
|
-
* @deprecated Use getBestAccessPlan instead for better type safety and extensibility
|
|
311
|
-
*/
|
|
312
|
-
xBestIndex(db: Database, tableInfo: TableSchema, indexInfo: IndexInfo): number {
|
|
313
|
-
const planningContext = this.createPlanningContext(tableInfo, indexInfo);
|
|
314
|
-
const availableIndexes = this.gatherAvailableIndexes(tableInfo);
|
|
315
|
-
const bestPlan = this.findBestPlan(planningContext, availableIndexes, indexInfo);
|
|
316
|
-
this.populateIndexInfoOutput(indexInfo, bestPlan, availableIndexes);
|
|
317
308
|
|
|
318
|
-
logger.debugLog(`[xBestIndex] Selected plan for ${tableInfo.name}: ${bestPlan.planType} on index ${bestPlan.indexId} (cost: ${bestPlan.cost})`);
|
|
319
|
-
|
|
320
|
-
return StatusCode.OK;
|
|
321
|
-
}
|
|
322
309
|
|
|
323
310
|
private createPlanningContext(_tableInfo: TableSchema, _indexInfo: IndexInfo) {
|
|
324
311
|
return {
|
|
@@ -644,7 +631,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
|
|
|
644
631
|
/**
|
|
645
632
|
* Destroys a memory table and frees associated resources
|
|
646
633
|
*/
|
|
647
|
-
async
|
|
634
|
+
async destroy(db: Database, pAux: unknown, moduleName: string, schemaName: string, tableName: string): Promise<void> {
|
|
648
635
|
const tableKey = `${schemaName}.${tableName}`.toLowerCase();
|
|
649
636
|
const manager = this.tables.get(tableKey);
|
|
650
637
|
|
|
@@ -659,7 +646,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
|
|
|
659
646
|
/**
|
|
660
647
|
* Creates an index on a memory table
|
|
661
648
|
*/
|
|
662
|
-
async
|
|
649
|
+
async createIndex(db: Database, schemaName: string, tableName: string, indexSchema: IndexSchema): Promise<void> {
|
|
663
650
|
const tableKey = `${schemaName}.${tableName}`.toLowerCase();
|
|
664
651
|
const manager = this.tables.get(tableKey);
|
|
665
652
|
|
package/src/vtab/memory/table.ts
CHANGED
|
@@ -30,7 +30,7 @@ export class MemoryTable extends VirtualTable {
|
|
|
30
30
|
private connection: MemoryTableConnection | null = null;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* @internal - Use MemoryTableModule.
|
|
33
|
+
* @internal - Use MemoryTableModule.connect or create
|
|
34
34
|
* Creates a connection-specific instance linked to a manager.
|
|
35
35
|
*/
|
|
36
36
|
constructor(
|
|
@@ -101,29 +101,29 @@ export class MemoryTable extends VirtualTable {
|
|
|
101
101
|
return new MemoryVirtualTableConnection(this.tableName, this.connection);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
async*
|
|
104
|
+
// Direct async iteration for query execution
|
|
105
|
+
async* query(filterInfo: FilterInfo): AsyncIterable<Row> {
|
|
106
106
|
const conn = await this.ensureConnection();
|
|
107
|
-
logger.debugLog(`
|
|
107
|
+
logger.debugLog(`query using connection ${conn.connectionId} (pending: ${conn.pendingTransactionLayer?.getLayerId()}, read: ${conn.readLayer.getLayerId()})`);
|
|
108
108
|
const currentSchema = this.manager.tableSchema;
|
|
109
109
|
if (!currentSchema) {
|
|
110
|
-
logger.error('
|
|
110
|
+
logger.error('query', this.tableName, 'Table schema is undefined');
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
const plan = buildScanPlanFromFilterInfo(filterInfo, currentSchema);
|
|
114
|
-
logger.debugLog(`
|
|
114
|
+
logger.debugLog(`query invoked for ${this.tableName} with plan: ${safeJsonStringify(plan)}`);
|
|
115
115
|
|
|
116
116
|
const startLayer = conn.pendingTransactionLayer ?? conn.readLayer;
|
|
117
|
-
logger.debugLog(`
|
|
117
|
+
logger.debugLog(`query reading from layer ${startLayer.getLayerId()}`);
|
|
118
118
|
|
|
119
119
|
// Delegate scanning to the manager, which handles layer recursion
|
|
120
120
|
yield* this.manager.scanLayer(startLayer, plan);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
// Note:
|
|
123
|
+
// Note: getBestAccessPlan is handled by the MemoryTableModule, not the table instance.
|
|
124
124
|
|
|
125
125
|
/** Performs mutation through the connection's transaction layer */
|
|
126
|
-
async
|
|
126
|
+
async update(
|
|
127
127
|
operation: 'insert' | 'update' | 'delete',
|
|
128
128
|
values: Row | undefined,
|
|
129
129
|
oldKeyValues?: Row,
|
|
@@ -136,12 +136,12 @@ export class MemoryTable extends VirtualTable {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/** Begins a transaction for this connection */
|
|
139
|
-
async
|
|
139
|
+
async begin(): Promise<void> {
|
|
140
140
|
(await this.ensureConnection()).begin();
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/** Commits this connection's transaction */
|
|
144
|
-
async
|
|
144
|
+
async commit(): Promise<void> {
|
|
145
145
|
// Only commit if a connection has actually been established
|
|
146
146
|
if (this.connection) {
|
|
147
147
|
await this.connection.commit();
|
|
@@ -149,7 +149,7 @@ export class MemoryTable extends VirtualTable {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
/** Rolls back this connection's transaction */
|
|
152
|
-
async
|
|
152
|
+
async rollback(): Promise<void> {
|
|
153
153
|
// Only rollback if a connection has actually been established
|
|
154
154
|
if (this.connection) {
|
|
155
155
|
this.connection.rollback();
|
|
@@ -157,14 +157,14 @@ export class MemoryTable extends VirtualTable {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/** Sync operation (currently no-op for memory table layers) */
|
|
160
|
-
async
|
|
160
|
+
async sync(): Promise<void> {
|
|
161
161
|
// This might trigger background collapse in the manager in the future
|
|
162
162
|
// await this.manager.tryCollapseLayers(); // Optional: trigger collapse on sync?
|
|
163
163
|
return Promise.resolve();
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
/** Renames the underlying table via the manager */
|
|
167
|
-
async
|
|
167
|
+
async rename(newName: string): Promise<void> {
|
|
168
168
|
logger.operation('Rename', this.tableName, { newName });
|
|
169
169
|
await this.manager.renameTable(newName);
|
|
170
170
|
// Update this instance's schema reference after rename
|
|
@@ -172,17 +172,17 @@ export class MemoryTable extends VirtualTable {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
// --- Savepoint operations ---
|
|
175
|
-
async
|
|
175
|
+
async savepoint(savepointIndex: number): Promise<void> {
|
|
176
176
|
const conn = await this.ensureConnection();
|
|
177
177
|
conn.createSavepoint(savepointIndex);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
async
|
|
180
|
+
async release(savepointIndex: number): Promise<void> {
|
|
181
181
|
if (!this.connection) return; // No connection, no savepoints to release
|
|
182
182
|
this.connection.releaseSavepoint(savepointIndex);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
async
|
|
185
|
+
async rollbackTo(savepointIndex: number): Promise<void> {
|
|
186
186
|
if (!this.connection) return; // No connection, no savepoints to rollback to
|
|
187
187
|
this.connection.rollbackToSavepoint(savepointIndex);
|
|
188
188
|
}
|
|
@@ -190,7 +190,7 @@ export class MemoryTable extends VirtualTable {
|
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
/** Handles schema changes via the manager */
|
|
193
|
-
async
|
|
193
|
+
async alterSchema(changeInfo: SchemaChangeInfo): Promise<void> {
|
|
194
194
|
const originalManagerSchema = this.manager.tableSchema; // For potential error recovery
|
|
195
195
|
try {
|
|
196
196
|
switch (changeInfo.type) {
|
|
@@ -226,7 +226,7 @@ export class MemoryTable extends VirtualTable {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/** Disconnects this connection instance from the manager */
|
|
229
|
-
async
|
|
229
|
+
async disconnect(): Promise<void> {
|
|
230
230
|
if (this.connection) {
|
|
231
231
|
// Manager handles cleanup and potential layer collapse trigger
|
|
232
232
|
await this.manager.disconnect(this.connection.connectionId);
|
|
@@ -235,13 +235,13 @@ export class MemoryTable extends VirtualTable {
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// --- Index DDL methods delegate to the manager ---
|
|
238
|
-
async
|
|
238
|
+
async createIndex(indexSchema: IndexSchema): Promise<void> {
|
|
239
239
|
logger.operation('Create Index', this.tableName, { indexName: indexSchema.name });
|
|
240
240
|
await this.manager.createIndex(indexSchema);
|
|
241
241
|
this.tableSchema = this.manager.tableSchema; // Refresh local schema ref
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
async
|
|
244
|
+
async dropIndex(indexName: string): Promise<void> {
|
|
245
245
|
logger.operation('Drop Index', this.tableName, { indexName });
|
|
246
246
|
await this.manager.dropIndex(indexName);
|
|
247
247
|
// Update schema reference
|
package/src/vtab/module.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { BestAccessPlanRequest, BestAccessPlanResult } from './best-access-
|
|
|
7
7
|
import type { PlanNode } from '../planner/nodes/plan-node.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Base interface for module-specific configuration passed to
|
|
10
|
+
* Base interface for module-specific configuration passed to create/connect.
|
|
11
11
|
* Modules should define their own interface extending this if they need options.
|
|
12
12
|
*/
|
|
13
13
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
@@ -45,7 +45,7 @@ export interface VirtualTableModule<
|
|
|
45
45
|
* @returns The new VirtualTable instance
|
|
46
46
|
* @throws QuereusError on failure
|
|
47
47
|
*/
|
|
48
|
-
|
|
48
|
+
create(
|
|
49
49
|
db: Database,
|
|
50
50
|
tableSchema: TableSchema,
|
|
51
51
|
): TTable;
|
|
@@ -63,7 +63,7 @@ export interface VirtualTableModule<
|
|
|
63
63
|
* @returns The connection-specific VirtualTable instance
|
|
64
64
|
* @throws QuereusError on failure
|
|
65
65
|
*/
|
|
66
|
-
|
|
66
|
+
connect(
|
|
67
67
|
db: Database,
|
|
68
68
|
pAux: unknown,
|
|
69
69
|
moduleName: string,
|
|
@@ -98,17 +98,7 @@ export interface VirtualTableModule<
|
|
|
98
98
|
request: BestAccessPlanRequest
|
|
99
99
|
): BestAccessPlanResult;
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
* Determines the best query plan for a given set of constraints and orderings.
|
|
103
|
-
* This method MUST be synchronous for performance. It modifies the passed IndexInfo object.
|
|
104
|
-
*
|
|
105
|
-
* @deprecated Use getBestAccessPlan instead for better type safety and extensibility
|
|
106
|
-
* @param db The database connection
|
|
107
|
-
* @param tableInfo The schema information for the table being planned
|
|
108
|
-
* @param indexInfo Input constraints/orderings and output plan details
|
|
109
|
-
* @returns StatusCode.OK on success, or an error code
|
|
110
|
-
*/
|
|
111
|
-
xBestIndex(db: Database, tableInfo: TableSchema, indexInfo: IndexInfo): number;
|
|
101
|
+
|
|
112
102
|
|
|
113
103
|
/**
|
|
114
104
|
* Destroys the underlying persistent representation of the virtual table.
|
|
@@ -121,7 +111,7 @@ export interface VirtualTableModule<
|
|
|
121
111
|
* @param tableName The name of the virtual table being destroyed
|
|
122
112
|
* @throws QuereusError on failure
|
|
123
113
|
*/
|
|
124
|
-
|
|
114
|
+
destroy(
|
|
125
115
|
db: Database,
|
|
126
116
|
pAux: unknown,
|
|
127
117
|
moduleName: string,
|
|
@@ -139,7 +129,7 @@ export interface VirtualTableModule<
|
|
|
139
129
|
* @param indexSchema The schema definition for the index being created
|
|
140
130
|
* @throws QuereusError on failure
|
|
141
131
|
*/
|
|
142
|
-
|
|
132
|
+
createIndex?(
|
|
143
133
|
db: Database,
|
|
144
134
|
schemaName: string,
|
|
145
135
|
tableName: string,
|
|
@@ -151,7 +141,7 @@ export interface VirtualTableModule<
|
|
|
151
141
|
* @param name The name to check
|
|
152
142
|
* @returns true if the name would conflict
|
|
153
143
|
*/
|
|
154
|
-
|
|
144
|
+
shadowName?(name: string): boolean;
|
|
155
145
|
}
|
|
156
146
|
|
|
157
147
|
/**
|