@quereus/quereus 0.6.12 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/src/parser/lexer.d.ts +6 -0
  2. package/dist/src/parser/lexer.d.ts.map +1 -1
  3. package/dist/src/parser/lexer.js +33 -1
  4. package/dist/src/parser/lexer.js.map +1 -1
  5. package/dist/src/parser/parser.d.ts.map +1 -1
  6. package/dist/src/parser/parser.js +28 -24
  7. package/dist/src/parser/parser.js.map +1 -1
  8. package/dist/src/planner/building/select-aggregates.d.ts +6 -1
  9. package/dist/src/planner/building/select-aggregates.d.ts.map +1 -1
  10. package/dist/src/planner/building/select-aggregates.js +23 -4
  11. package/dist/src/planner/building/select-aggregates.js.map +1 -1
  12. package/dist/src/planner/building/select-modifiers.js +7 -2
  13. package/dist/src/planner/building/select-modifiers.js.map +1 -1
  14. package/dist/src/planner/building/select.d.ts.map +1 -1
  15. package/dist/src/planner/building/select.js +2 -2
  16. package/dist/src/planner/building/select.js.map +1 -1
  17. package/dist/src/planner/building/update.d.ts.map +1 -1
  18. package/dist/src/planner/building/update.js +8 -4
  19. package/dist/src/planner/building/update.js.map +1 -1
  20. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  21. package/dist/src/planner/nodes/join-node.js +6 -1
  22. package/dist/src/planner/nodes/join-node.js.map +1 -1
  23. package/dist/src/planner/rules/access/rule-select-access-path.js +15 -2
  24. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  25. package/dist/src/schema/manager.d.ts +30 -0
  26. package/dist/src/schema/manager.d.ts.map +1 -1
  27. package/dist/src/schema/manager.js +205 -0
  28. package/dist/src/schema/manager.js.map +1 -1
  29. package/dist/src/vtab/best-access-plan.d.ts +2 -0
  30. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  31. package/dist/src/vtab/best-access-plan.js.map +1 -1
  32. package/dist/src/vtab/memory/layer/scan-plan.js +2 -2
  33. package/dist/src/vtab/memory/layer/scan-plan.js.map +1 -1
  34. package/dist/src/vtab/memory/module.d.ts +1 -1
  35. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  36. package/dist/src/vtab/memory/module.js +2 -1
  37. package/dist/src/vtab/memory/module.js.map +1 -1
  38. package/dist/src/vtab/module.d.ts +2 -1
  39. package/dist/src/vtab/module.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/parser/lexer.ts +806 -771
  42. package/src/parser/parser.ts +3352 -3347
  43. package/src/planner/building/select-aggregates.ts +30 -5
  44. package/src/planner/building/select-modifiers.ts +8 -2
  45. package/src/planner/building/select.ts +567 -560
  46. package/src/planner/building/update.ts +9 -5
  47. package/src/planner/nodes/join-node.ts +6 -1
  48. package/src/planner/rules/access/rule-select-access-path.ts +399 -384
  49. package/src/schema/manager.ts +235 -1
  50. package/src/vtab/best-access-plan.ts +2 -0
  51. package/src/vtab/memory/layer/scan-plan.ts +2 -2
  52. package/src/vtab/memory/module.ts +2 -1
  53. package/src/vtab/module.ts +162 -160
@@ -1,7 +1,7 @@
1
1
  import { Schema } from './schema.js';
2
2
  import type { IntegrityAssertionSchema } from './assertion.js';
3
3
  import type { Database } from '../core/database.js';
4
- import type { TableSchema, RowConstraintSchema, IndexSchema } from './table.js';
4
+ import type { TableSchema, RowConstraintSchema, IndexSchema, IndexColumnSchema } from './table.js';
5
5
  import type { FunctionSchema } from './function.js';
6
6
  import { quereusError, QuereusError } from '../common/errors.js';
7
7
  import { StatusCode, type SqlValue } from '../common/types.js';
@@ -12,6 +12,7 @@ import { buildColumnIndexMap, columnDefToSchema, findPKDefinition, opsToMask, mu
12
12
  import type { ViewSchema } from './view.js';
13
13
  import { createLogger } from '../common/logger.js';
14
14
  import type * as AST from '../parser/ast.js';
15
+ import { Parser } from '../parser/parser.js';
15
16
  import { SchemaChangeNotifier } from './change-events.js';
16
17
  import { checkDeterministic } from '../planner/validation/determinism-validator.js';
17
18
  import { buildExpression } from '../planner/building/expression.js';
@@ -797,4 +798,237 @@ export class SchemaManager {
797
798
 
798
799
  return completeTableSchema;
799
800
  }
801
+
802
+ /**
803
+ * Import catalog objects from DDL statements without triggering storage creation.
804
+ * Used when connecting to existing storage that already contains data.
805
+ *
806
+ * This method:
807
+ * 1. Parses each DDL statement
808
+ * 2. Registers the schema objects (tables, indexes)
809
+ * 3. Calls module.connect() instead of module.create()
810
+ * 4. Skips schema change hooks (since these are existing objects)
811
+ *
812
+ * @param ddlStatements Array of DDL strings (CREATE TABLE, CREATE INDEX, etc.)
813
+ * @returns Array of imported object names
814
+ */
815
+ async importCatalog(ddlStatements: string[]): Promise<{ tables: string[]; indexes: string[] }> {
816
+ const imported = { tables: [] as string[], indexes: [] as string[] };
817
+
818
+ for (const ddl of ddlStatements) {
819
+ try {
820
+ const result = await this.importSingleDDL(ddl);
821
+ if (result.type === 'table') {
822
+ imported.tables.push(result.name);
823
+ } else if (result.type === 'index') {
824
+ imported.indexes.push(result.name);
825
+ }
826
+ } catch (e) {
827
+ const message = e instanceof Error ? e.message : String(e);
828
+ errorLog('Failed to import DDL: %s - Error: %s', ddl.substring(0, 100), message);
829
+ throw e;
830
+ }
831
+ }
832
+
833
+ log('Imported catalog: %d tables, %d indexes', imported.tables.length, imported.indexes.length);
834
+ return imported;
835
+ }
836
+
837
+ /**
838
+ * Import a single DDL statement without creating storage.
839
+ */
840
+ private async importSingleDDL(ddl: string): Promise<{ type: 'table' | 'index'; name: string }> {
841
+ // Parse the DDL using the parser
842
+ const parser = new Parser();
843
+ const statements = parser.parseAll(ddl);
844
+ if (statements.length !== 1) {
845
+ throw new QuereusError(`importCatalog expects exactly one statement per DDL, got ${statements.length}`, StatusCode.ERROR);
846
+ }
847
+
848
+ const stmt = statements[0];
849
+
850
+ if (stmt.type === 'createTable') {
851
+ return this.importTable(stmt as AST.CreateTableStmt);
852
+ } else if (stmt.type === 'createIndex') {
853
+ return this.importIndex(stmt as AST.CreateIndexStmt);
854
+ } else {
855
+ throw new QuereusError(`importCatalog does not support statement type: ${stmt.type}`, StatusCode.ERROR);
856
+ }
857
+ }
858
+
859
+ /**
860
+ * Import a table schema without calling module.create().
861
+ * Uses module.connect() to bind to existing storage.
862
+ */
863
+ private async importTable(stmt: AST.CreateTableStmt): Promise<{ type: 'table'; name: string }> {
864
+ const targetSchemaName = stmt.table.schema || this.getCurrentSchemaName();
865
+ const tableName = stmt.table.name;
866
+
867
+ let moduleName: string;
868
+ let effectiveModuleArgs: Record<string, SqlValue>;
869
+
870
+ if (stmt.moduleName) {
871
+ moduleName = stmt.moduleName;
872
+ effectiveModuleArgs = Object.freeze(stmt.moduleArgs || {});
873
+ } else {
874
+ const defaultVtab = this.getDefaultVTabModule();
875
+ moduleName = defaultVtab.name;
876
+ effectiveModuleArgs = Object.freeze(defaultVtab.args || {});
877
+ }
878
+
879
+ const moduleInfo = this.getModule(moduleName);
880
+ if (!moduleInfo || !moduleInfo.module) {
881
+ throw new QuereusError(`No virtual table module named '${moduleName}'`, StatusCode.ERROR);
882
+ }
883
+
884
+ // Get default nullability setting from database options
885
+ const defaultNullability = this.db.options.getStringOption('default_column_nullability');
886
+ const defaultNotNull = defaultNullability === 'not_null';
887
+
888
+ const astColumnsToProcess = stmt.columns || [];
889
+ const astConstraintsToProcess = stmt.constraints;
890
+
891
+ const preliminaryColumnSchemas: ColumnSchema[] = astColumnsToProcess.map(colDef => columnDefToSchema(colDef, defaultNotNull));
892
+ const pkDefinition = findPKDefinition(preliminaryColumnSchemas, astConstraintsToProcess);
893
+
894
+ const finalColumnSchemas = preliminaryColumnSchemas.map((col, idx) => {
895
+ const isPkColumn = pkDefinition.some(pkCol => pkCol.index === idx);
896
+ let pkOrder = 0;
897
+ if (isPkColumn) {
898
+ pkOrder = pkDefinition.findIndex(pkC => pkC.index === idx) + 1;
899
+ }
900
+ return {
901
+ ...col,
902
+ primaryKey: isPkColumn,
903
+ pkOrder: pkOrder,
904
+ notNull: isPkColumn ? true : col.notNull,
905
+ };
906
+ });
907
+
908
+ const checkConstraintsSchema: RowConstraintSchema[] = [];
909
+ astColumnsToProcess.forEach(colDef => {
910
+ colDef.constraints?.forEach(con => {
911
+ if (con.type === 'check' && con.expr) {
912
+ checkConstraintsSchema.push({
913
+ name: con.name ?? `_check_${colDef.name}`,
914
+ expr: con.expr,
915
+ operations: opsToMask(con.operations),
916
+ deferrable: con.deferrable,
917
+ initiallyDeferred: con.initiallyDeferred
918
+ });
919
+ }
920
+ });
921
+ });
922
+ (astConstraintsToProcess || []).forEach(con => {
923
+ if (con.type === 'check' && con.expr) {
924
+ checkConstraintsSchema.push({
925
+ name: con.name,
926
+ expr: con.expr,
927
+ operations: opsToMask(con.operations),
928
+ deferrable: con.deferrable,
929
+ initiallyDeferred: con.initiallyDeferred
930
+ });
931
+ }
932
+ });
933
+
934
+ // Process mutation context definitions if present
935
+ const mutationContextSchemas = stmt.contextDefinitions
936
+ ? stmt.contextDefinitions.map(varDef => mutationContextVarToSchema(varDef, defaultNotNull))
937
+ : undefined;
938
+
939
+ const tableSchema: TableSchema = {
940
+ name: tableName,
941
+ schemaName: targetSchemaName,
942
+ columns: Object.freeze(finalColumnSchemas),
943
+ columnIndexMap: buildColumnIndexMap(finalColumnSchemas),
944
+ primaryKeyDefinition: pkDefinition,
945
+ checkConstraints: Object.freeze(checkConstraintsSchema),
946
+ isTemporary: !!stmt.isTemporary,
947
+ isView: false,
948
+ vtabModuleName: moduleName,
949
+ vtabArgs: effectiveModuleArgs,
950
+ vtabModule: moduleInfo.module,
951
+ vtabAuxData: moduleInfo.auxData,
952
+ estimatedRows: 0,
953
+ mutationContext: mutationContextSchemas ? Object.freeze(mutationContextSchemas) : undefined,
954
+ };
955
+
956
+ // Use connect() instead of create() - the storage already exists
957
+ try {
958
+ moduleInfo.module.connect(
959
+ this.db,
960
+ moduleInfo.auxData,
961
+ moduleName,
962
+ targetSchemaName,
963
+ tableName,
964
+ effectiveModuleArgs as BaseModuleConfig,
965
+ tableSchema // Pass the full schema so the module can use it
966
+ );
967
+ } catch (e: unknown) {
968
+ const message = e instanceof Error ? e.message : String(e);
969
+ throw new QuereusError(`Module '${moduleName}' connect failed during import for table '${tableName}': ${message}`, StatusCode.ERROR);
970
+ }
971
+
972
+ // Ensure schema exists
973
+ let schema = this.getSchema(targetSchemaName);
974
+ if (!schema) {
975
+ schema = new Schema(targetSchemaName);
976
+ this.schemas.set(targetSchemaName.toLowerCase(), schema);
977
+ }
978
+
979
+ // Register without notifying change listeners (this is an import, not a create)
980
+ schema.addTable(tableSchema);
981
+ log(`Imported table %s.%s using module %s`, targetSchemaName, tableName, moduleName);
982
+
983
+ return { type: 'table', name: `${targetSchemaName}.${tableName}` };
984
+ }
985
+
986
+ /**
987
+ * Import an index schema without calling module.createIndex().
988
+ */
989
+ private async importIndex(stmt: AST.CreateIndexStmt): Promise<{ type: 'index'; name: string }> {
990
+ const targetSchemaName = stmt.table.schema || this.getCurrentSchemaName();
991
+ const tableName = stmt.table.name;
992
+ const indexName = stmt.index.name;
993
+
994
+ // Find the table
995
+ const tableSchema = this.findTable(tableName, targetSchemaName);
996
+ if (!tableSchema) {
997
+ throw new QuereusError(`Cannot import index '${indexName}': table '${tableName}' not found`, StatusCode.ERROR);
998
+ }
999
+
1000
+ // Build index columns schema
1001
+ const indexColumns: IndexColumnSchema[] = stmt.columns.map(col => {
1002
+ const colName = col.name;
1003
+ if (!colName) {
1004
+ throw new QuereusError(`Expression-based index columns are not supported during import`, StatusCode.ERROR);
1005
+ }
1006
+ const colIdx = tableSchema.columnIndexMap.get(colName.toLowerCase());
1007
+ if (colIdx === undefined) {
1008
+ throw new QuereusError(`Column '${colName}' not found in table '${tableName}'`, StatusCode.ERROR);
1009
+ }
1010
+ return {
1011
+ index: colIdx,
1012
+ desc: col.direction === 'desc',
1013
+ };
1014
+ });
1015
+
1016
+ const indexSchema: IndexSchema = {
1017
+ name: indexName,
1018
+ columns: Object.freeze(indexColumns),
1019
+ };
1020
+
1021
+ // Add index to table without calling module.createIndex()
1022
+ const updatedIndexes = [...(tableSchema.indexes || []), indexSchema];
1023
+ const updatedTableSchema: TableSchema = {
1024
+ ...tableSchema,
1025
+ indexes: Object.freeze(updatedIndexes),
1026
+ };
1027
+
1028
+ const schema = this.getSchemaOrFail(targetSchemaName);
1029
+ schema.addTable(updatedTableSchema);
1030
+ log(`Imported index %s on table %s.%s`, indexName, targetSchemaName, tableName);
1031
+
1032
+ return { type: 'index', name: `${targetSchemaName}.${tableName}.${indexName}` };
1033
+ }
800
1034
  }
@@ -85,6 +85,8 @@ export interface BestAccessPlanResult {
85
85
  rows: number | undefined;
86
86
  /** Ordering guaranteed by this access plan */
87
87
  providesOrdering?: readonly OrderingSpec[];
88
+ /** Name of the index that provides the ordering (if any) */
89
+ orderingIndexName?: string;
88
90
  /** Whether this plan guarantees unique rows (helps DISTINCT optimization) */
89
91
  isSet?: boolean;
90
92
  /** Free-text explanation for debugging */
@@ -52,12 +52,12 @@ export function buildScanPlanFromFilterInfo(filterInfo: FilterInfo, tableSchema:
52
52
  let upperBound: ScanPlanRangeBound | undefined = undefined;
53
53
  const params = new Map<string, string>();
54
54
  idxStr?.split(';').forEach(part => { const [key, value] = part.split('=', 2); if (key && value !== undefined) params.set(key, value); });
55
- const idxNameMatch = params.get('idx')?.match(/^(.*?)\\((\\d+)\\)$/);
55
+ const idxNameMatch = params.get('idx')?.match(/^(.*?)\((\d+)\)$/);
56
56
  if (idxNameMatch) indexName = idxNameMatch[1] === '_primary_' ? 'primary' : idxNameMatch[1];
57
57
  const planType = parseInt(params.get('plan') ?? '0', 10);
58
58
  descending = params.get('ordCons') === 'DESC' || planType === 1 || planType === 4;
59
59
  const argvMap = new Map<number, number>();
60
- params.get('argvMap')?.match(/\\[(\\d+),(\\d+)\\]/g)?.forEach(m => { const p = m.match(/\\[(\\d+),(\\d+)\\]/); if (p) argvMap.set(parseInt(p[1]), parseInt(p[2])); });
60
+ params.get('argvMap')?.match(/\[(\d+),(\d+)\]/g)?.forEach(m => { const p = m.match(/\[(\d+),(\d+)\]/); if (p) argvMap.set(parseInt(p[1]), parseInt(p[2])); });
61
61
  const currentSchema = tableSchema;
62
62
  const indexSchemaForPlan = indexName === 'primary' ? { name: '_primary_', columns: currentSchema.primaryKeyDefinition ?? [{ index: -1, desc: false, collation: 'BINARY' }] } : currentSchema.indexes?.find(i => i.name === indexName);
63
63
  if (planType === 2) { // EQ Plan
@@ -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
- connect(db: Database, pAux: unknown, moduleName: string, schemaName: string, tableName: string, _options: MemoryTableConfig): MemoryTable {
61
+ connect(db: Database, pAux: unknown, moduleName: string, schemaName: string, tableName: string, _options: MemoryTableConfig, _tableSchema?: TableSchema): MemoryTable {
62
62
  const tableKey = `${schemaName}.${tableName}`.toLowerCase();
63
63
  const existingManager = this.tables.get(tableKey);
64
64
 
@@ -272,6 +272,7 @@ export class MemoryTableModule implements VirtualTableModule<MemoryTable, Memory
272
272
  ...plan,
273
273
  cost: adjustedCost,
274
274
  providesOrdering: request.requiredOrdering,
275
+ orderingIndexName: index.name,
275
276
  explains: `${plan.explains} with ordering from ${index.name}`
276
277
  };
277
278
  }
@@ -1,160 +1,162 @@
1
- import type { Database } from '../core/database.js'; // Assuming Database class exists
2
- import type { VirtualTable } from './table.js';
3
- import type { IndexInfo } from './index-info.js';
4
- import type { ColumnDef } from '../parser/ast.js'; // <-- Add parser AST import
5
- import type { TableSchema, IndexSchema } from '../schema/table.js'; // Add import for TableSchema and IndexSchema
6
- import type { BestAccessPlanRequest, BestAccessPlanResult } from './best-access-plan.js';
7
- import type { PlanNode } from '../planner/nodes/plan-node.js';
8
-
9
- /**
10
- * Base interface for module-specific configuration passed to create/connect.
11
- * Modules should define their own interface extending this if they need options.
12
- */
13
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type
14
- export interface BaseModuleConfig {}
15
-
16
- /**
17
- * Assessment result from a module's supports() method indicating
18
- * whether it can execute a plan subtree and at what cost.
19
- */
20
- export interface SupportAssessment {
21
- /** Estimated cost comparable to local evaluation cost */
22
- cost: number;
23
- /** Optional context data persisted for the emitter */
24
- ctx?: unknown;
25
- }
26
-
27
- /**
28
- * Interface defining the methods for a virtual table module implementation.
29
- * The module primarily acts as a factory for connection-specific VirtualTable instances.
30
- *
31
- * @template TTable The specific type of VirtualTable managed by this module.
32
- * @template TConfig The type defining module-specific configuration options.
33
- */
34
- export interface VirtualTableModule<
35
- TTable extends VirtualTable,
36
- TConfig extends BaseModuleConfig = BaseModuleConfig
37
- > {
38
-
39
- /**
40
- * Creates the persistent definition of a virtual table.
41
- * Called by CREATE VIRTUAL TABLE to define schema and initialize storage.
42
- *
43
- * @param db The database connection
44
- * @param tableSchema The schema definition for the table being created
45
- * @returns The new VirtualTable instance
46
- * @throws QuereusError on failure
47
- */
48
- create(
49
- db: Database,
50
- tableSchema: TableSchema,
51
- ): TTable;
52
-
53
- /**
54
- * Connects to an existing virtual table definition.
55
- * Called when the schema is loaded or a connection needs to interact with the table.
56
- *
57
- * @param db The database connection
58
- * @param pAux Client data passed during module registration
59
- * @param moduleName The name the module was registered with
60
- * @param schemaName The name of the database schema
61
- * @param tableName The name of the virtual table to connect to
62
- * @param options Module-specific configuration options from the original CREATE VIRTUAL TABLE
63
- * @returns The connection-specific VirtualTable instance
64
- * @throws QuereusError on failure
65
- */
66
- connect(
67
- db: Database,
68
- pAux: unknown,
69
- moduleName: string,
70
- schemaName: string,
71
- tableName: string,
72
- options: TConfig
73
- ): TTable;
74
-
75
- /**
76
- * Determines if this module can execute a plan subtree starting at the given node.
77
- * Used for query push-down to virtual table modules that support arbitrary queries.
78
- *
79
- * @param node The root node of the subtree to evaluate
80
- * @returns Assessment with cost and optional context, or undefined if not supported
81
- */
82
- supports?(
83
- node: PlanNode
84
- ): SupportAssessment | undefined;
85
-
86
- /**
87
- * Modern, type-safe access planning interface.
88
- * Preferred over xBestIndex for new implementations.
89
- *
90
- * @param db The database connection
91
- * @param tableInfo The schema information for the table being planned
92
- * @param request Planning request with constraints and requirements
93
- * @returns Access plan result describing the chosen strategy
94
- */
95
- getBestAccessPlan?(
96
- db: Database,
97
- tableInfo: TableSchema,
98
- request: BestAccessPlanRequest
99
- ): BestAccessPlanResult;
100
-
101
-
102
-
103
- /**
104
- * Destroys the underlying persistent representation of the virtual table.
105
- * Called by DROP TABLE.
106
- *
107
- * @param db The database connection
108
- * @param pAux Client data passed during module registration
109
- * @param moduleName The name the module was registered with
110
- * @param schemaName The name of the database schema
111
- * @param tableName The name of the virtual table being destroyed
112
- * @throws QuereusError on failure
113
- */
114
- destroy(
115
- db: Database,
116
- pAux: unknown,
117
- moduleName: string,
118
- schemaName: string,
119
- tableName: string
120
- ): Promise<void>;
121
-
122
- /**
123
- * Creates an index on a virtual table.
124
- * Called by CREATE INDEX.
125
- *
126
- * @param db The database connection
127
- * @param schemaName The name of the database schema
128
- * @param tableName The name of the virtual table
129
- * @param indexSchema The schema definition for the index being created
130
- * @throws QuereusError on failure
131
- */
132
- createIndex?(
133
- db: Database,
134
- schemaName: string,
135
- tableName: string,
136
- indexSchema: IndexSchema
137
- ): Promise<void>;
138
-
139
- /**
140
- * Checks for shadow table name conflicts
141
- * @param name The name to check
142
- * @returns true if the name would conflict
143
- */
144
- shadowName?(name: string): boolean;
145
- }
146
-
147
- /**
148
- * Defines the structure for schema change information passed to xAlterSchema
149
- */
150
- export type SchemaChangeInfo =
151
- | { type: 'addColumn'; columnDef: ColumnDef }
152
- | { type: 'dropColumn'; columnName: string }
153
- | { type: 'renameColumn'; oldName: string; newName: string; newColumnDefAst?: ColumnDef };
154
-
155
- /**
156
- * Type alias for the common usage pattern where specific table and config types are not known.
157
- * Use this for storage scenarios like the SchemaManager where modules of different types are stored together.
158
- * eslint-disable-next-line @typescript-eslint/no-explicit-any
159
- */
160
- export type AnyVirtualTableModule = VirtualTableModule<any, any>;
1
+ import type { Database } from '../core/database.js'; // Assuming Database class exists
2
+ import type { VirtualTable } from './table.js';
3
+ import type { IndexInfo } from './index-info.js';
4
+ import type { ColumnDef } from '../parser/ast.js'; // <-- Add parser AST import
5
+ import type { TableSchema, IndexSchema } from '../schema/table.js'; // Add import for TableSchema and IndexSchema
6
+ import type { BestAccessPlanRequest, BestAccessPlanResult } from './best-access-plan.js';
7
+ import type { PlanNode } from '../planner/nodes/plan-node.js';
8
+
9
+ /**
10
+ * Base interface for module-specific configuration passed to create/connect.
11
+ * Modules should define their own interface extending this if they need options.
12
+ */
13
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
14
+ export interface BaseModuleConfig {}
15
+
16
+ /**
17
+ * Assessment result from a module's supports() method indicating
18
+ * whether it can execute a plan subtree and at what cost.
19
+ */
20
+ export interface SupportAssessment {
21
+ /** Estimated cost comparable to local evaluation cost */
22
+ cost: number;
23
+ /** Optional context data persisted for the emitter */
24
+ ctx?: unknown;
25
+ }
26
+
27
+ /**
28
+ * Interface defining the methods for a virtual table module implementation.
29
+ * The module primarily acts as a factory for connection-specific VirtualTable instances.
30
+ *
31
+ * @template TTable The specific type of VirtualTable managed by this module.
32
+ * @template TConfig The type defining module-specific configuration options.
33
+ */
34
+ export interface VirtualTableModule<
35
+ TTable extends VirtualTable,
36
+ TConfig extends BaseModuleConfig = BaseModuleConfig
37
+ > {
38
+
39
+ /**
40
+ * Creates the persistent definition of a virtual table.
41
+ * Called by CREATE VIRTUAL TABLE to define schema and initialize storage.
42
+ *
43
+ * @param db The database connection
44
+ * @param tableSchema The schema definition for the table being created
45
+ * @returns The new VirtualTable instance
46
+ * @throws QuereusError on failure
47
+ */
48
+ create(
49
+ db: Database,
50
+ tableSchema: TableSchema,
51
+ ): TTable;
52
+
53
+ /**
54
+ * Connects to an existing virtual table definition.
55
+ * Called when the schema is loaded or a connection needs to interact with the table.
56
+ *
57
+ * @param db The database connection
58
+ * @param pAux Client data passed during module registration
59
+ * @param moduleName The name the module was registered with
60
+ * @param schemaName The name of the database schema
61
+ * @param tableName The name of the virtual table to connect to
62
+ * @param options Module-specific configuration options from the original CREATE VIRTUAL TABLE
63
+ * @param tableSchema Optional table schema when connecting during import (columns, PK, etc.)
64
+ * @returns The connection-specific VirtualTable instance
65
+ * @throws QuereusError on failure
66
+ */
67
+ connect(
68
+ db: Database,
69
+ pAux: unknown,
70
+ moduleName: string,
71
+ schemaName: string,
72
+ tableName: string,
73
+ options: TConfig,
74
+ tableSchema?: TableSchema
75
+ ): TTable;
76
+
77
+ /**
78
+ * Determines if this module can execute a plan subtree starting at the given node.
79
+ * Used for query push-down to virtual table modules that support arbitrary queries.
80
+ *
81
+ * @param node The root node of the subtree to evaluate
82
+ * @returns Assessment with cost and optional context, or undefined if not supported
83
+ */
84
+ supports?(
85
+ node: PlanNode
86
+ ): SupportAssessment | undefined;
87
+
88
+ /**
89
+ * Modern, type-safe access planning interface.
90
+ * Preferred over xBestIndex for new implementations.
91
+ *
92
+ * @param db The database connection
93
+ * @param tableInfo The schema information for the table being planned
94
+ * @param request Planning request with constraints and requirements
95
+ * @returns Access plan result describing the chosen strategy
96
+ */
97
+ getBestAccessPlan?(
98
+ db: Database,
99
+ tableInfo: TableSchema,
100
+ request: BestAccessPlanRequest
101
+ ): BestAccessPlanResult;
102
+
103
+
104
+
105
+ /**
106
+ * Destroys the underlying persistent representation of the virtual table.
107
+ * Called by DROP TABLE.
108
+ *
109
+ * @param db The database connection
110
+ * @param pAux Client data passed during module registration
111
+ * @param moduleName The name the module was registered with
112
+ * @param schemaName The name of the database schema
113
+ * @param tableName The name of the virtual table being destroyed
114
+ * @throws QuereusError on failure
115
+ */
116
+ destroy(
117
+ db: Database,
118
+ pAux: unknown,
119
+ moduleName: string,
120
+ schemaName: string,
121
+ tableName: string
122
+ ): Promise<void>;
123
+
124
+ /**
125
+ * Creates an index on a virtual table.
126
+ * Called by CREATE INDEX.
127
+ *
128
+ * @param db The database connection
129
+ * @param schemaName The name of the database schema
130
+ * @param tableName The name of the virtual table
131
+ * @param indexSchema The schema definition for the index being created
132
+ * @throws QuereusError on failure
133
+ */
134
+ createIndex?(
135
+ db: Database,
136
+ schemaName: string,
137
+ tableName: string,
138
+ indexSchema: IndexSchema
139
+ ): Promise<void>;
140
+
141
+ /**
142
+ * Checks for shadow table name conflicts
143
+ * @param name The name to check
144
+ * @returns true if the name would conflict
145
+ */
146
+ shadowName?(name: string): boolean;
147
+ }
148
+
149
+ /**
150
+ * Defines the structure for schema change information passed to xAlterSchema
151
+ */
152
+ export type SchemaChangeInfo =
153
+ | { type: 'addColumn'; columnDef: ColumnDef }
154
+ | { type: 'dropColumn'; columnName: string }
155
+ | { type: 'renameColumn'; oldName: string; newName: string; newColumnDefAst?: ColumnDef };
156
+
157
+ /**
158
+ * Type alias for the common usage pattern where specific table and config types are not known.
159
+ * Use this for storage scenarios like the SchemaManager where modules of different types are stored together.
160
+ * eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ */
162
+ export type AnyVirtualTableModule = VirtualTableModule<any, any>;