metal-orm 1.0.14 → 1.0.16

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 (129) hide show
  1. package/README.md +69 -67
  2. package/dist/decorators/index.cjs +1983 -224
  3. package/dist/decorators/index.cjs.map +1 -1
  4. package/dist/decorators/index.d.cts +6 -6
  5. package/dist/decorators/index.d.ts +6 -6
  6. package/dist/decorators/index.js +1982 -224
  7. package/dist/decorators/index.js.map +1 -1
  8. package/dist/index.cjs +5284 -3751
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +524 -169
  11. package/dist/index.d.ts +524 -169
  12. package/dist/index.js +5197 -3736
  13. package/dist/index.js.map +1 -1
  14. package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
  15. package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
  16. package/package.json +1 -1
  17. package/src/codegen/naming-strategy.ts +64 -0
  18. package/src/codegen/typescript.ts +19 -21
  19. package/src/core/ast/adapters.ts +21 -0
  20. package/src/core/ast/aggregate-functions.ts +13 -13
  21. package/src/core/ast/builders.ts +56 -43
  22. package/src/core/ast/expression-builders.ts +34 -34
  23. package/src/core/ast/expression-nodes.ts +18 -16
  24. package/src/core/ast/expression-visitor.ts +122 -69
  25. package/src/core/ast/expression.ts +6 -4
  26. package/src/core/ast/join-metadata.ts +15 -0
  27. package/src/core/ast/join-node.ts +22 -20
  28. package/src/core/ast/join.ts +5 -5
  29. package/src/core/ast/query.ts +52 -88
  30. package/src/core/ast/types.ts +20 -0
  31. package/src/core/ast/window-functions.ts +55 -55
  32. package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
  33. package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
  34. package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
  35. package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
  37. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
  39. package/src/core/ddl/introspect/context.ts +9 -0
  40. package/src/core/ddl/introspect/functions/postgres.ts +26 -0
  41. package/src/core/ddl/introspect/mssql.ts +149 -149
  42. package/src/core/ddl/introspect/mysql.ts +99 -99
  43. package/src/core/ddl/introspect/postgres.ts +245 -154
  44. package/src/core/ddl/introspect/registry.ts +26 -0
  45. package/src/core/ddl/introspect/run-select.ts +25 -0
  46. package/src/core/ddl/introspect/sqlite.ts +7 -7
  47. package/src/core/ddl/introspect/types.ts +23 -19
  48. package/src/core/ddl/introspect/utils.ts +1 -1
  49. package/src/core/ddl/naming-strategy.ts +10 -0
  50. package/src/core/ddl/schema-dialect.ts +41 -0
  51. package/src/core/ddl/schema-diff.ts +211 -179
  52. package/src/core/ddl/schema-generator.ts +17 -90
  53. package/src/core/ddl/schema-introspect.ts +25 -32
  54. package/src/core/ddl/schema-plan-executor.ts +17 -0
  55. package/src/core/ddl/schema-types.ts +46 -39
  56. package/src/core/ddl/sql-writing.ts +170 -0
  57. package/src/core/dialect/abstract.ts +172 -126
  58. package/src/core/dialect/base/cte-compiler.ts +33 -0
  59. package/src/core/dialect/base/function-table-formatter.ts +132 -0
  60. package/src/core/dialect/base/groupby-compiler.ts +21 -0
  61. package/src/core/dialect/base/join-compiler.ts +26 -0
  62. package/src/core/dialect/base/orderby-compiler.ts +21 -0
  63. package/src/core/dialect/base/pagination-strategy.ts +32 -0
  64. package/src/core/dialect/base/returning-strategy.ts +56 -0
  65. package/src/core/dialect/base/sql-dialect.ts +181 -204
  66. package/src/core/dialect/dialect-factory.ts +91 -0
  67. package/src/core/dialect/mssql/functions.ts +101 -0
  68. package/src/core/dialect/mssql/index.ts +128 -126
  69. package/src/core/dialect/mysql/functions.ts +101 -0
  70. package/src/core/dialect/mysql/index.ts +20 -18
  71. package/src/core/dialect/postgres/functions.ts +95 -0
  72. package/src/core/dialect/postgres/index.ts +30 -28
  73. package/src/core/dialect/sqlite/functions.ts +115 -0
  74. package/src/core/dialect/sqlite/index.ts +30 -28
  75. package/src/core/driver/database-driver.ts +11 -0
  76. package/src/core/driver/mssql-driver.ts +20 -0
  77. package/src/core/driver/mysql-driver.ts +20 -0
  78. package/src/core/driver/postgres-driver.ts +20 -0
  79. package/src/core/driver/sqlite-driver.ts +20 -0
  80. package/src/core/execution/db-executor.ts +63 -0
  81. package/src/core/execution/executors/mssql-executor.ts +39 -0
  82. package/src/core/execution/executors/mysql-executor.ts +47 -0
  83. package/src/core/execution/executors/postgres-executor.ts +32 -0
  84. package/src/core/execution/executors/sqlite-executor.ts +31 -0
  85. package/src/core/functions/datetime.ts +132 -0
  86. package/src/core/functions/numeric.ts +179 -0
  87. package/src/core/functions/standard-strategy.ts +47 -0
  88. package/src/core/functions/text.ts +147 -0
  89. package/src/core/functions/types.ts +18 -0
  90. package/src/core/hydration/types.ts +57 -0
  91. package/src/decorators/bootstrap.ts +10 -0
  92. package/src/decorators/column.ts +13 -4
  93. package/src/decorators/relations.ts +15 -0
  94. package/src/index.ts +37 -19
  95. package/src/orm/entity-context.ts +30 -0
  96. package/src/orm/entity-meta.ts +2 -2
  97. package/src/orm/entity-metadata.ts +8 -6
  98. package/src/orm/entity.ts +72 -41
  99. package/src/orm/execute.ts +42 -25
  100. package/src/orm/execution-context.ts +12 -0
  101. package/src/orm/hydration-context.ts +14 -0
  102. package/src/orm/hydration.ts +25 -17
  103. package/src/orm/identity-map.ts +4 -0
  104. package/src/orm/interceptor-pipeline.ts +29 -0
  105. package/src/orm/lazy-batch.ts +50 -6
  106. package/src/orm/orm-session.ts +234 -0
  107. package/src/orm/orm.ts +58 -0
  108. package/src/orm/query-logger.ts +1 -1
  109. package/src/orm/relation-change-processor.ts +48 -3
  110. package/src/orm/relations/belongs-to.ts +45 -44
  111. package/src/orm/relations/has-many.ts +44 -43
  112. package/src/orm/relations/has-one.ts +140 -0
  113. package/src/orm/relations/many-to-many.ts +46 -45
  114. package/src/orm/transaction-runner.ts +1 -1
  115. package/src/orm/unit-of-work.ts +66 -61
  116. package/src/query-builder/delete.ts +22 -5
  117. package/src/query-builder/hydration-manager.ts +2 -1
  118. package/src/query-builder/hydration-planner.ts +8 -7
  119. package/src/query-builder/insert.ts +22 -5
  120. package/src/query-builder/relation-conditions.ts +9 -8
  121. package/src/query-builder/relation-service.ts +3 -2
  122. package/src/query-builder/select.ts +575 -64
  123. package/src/query-builder/update.ts +22 -5
  124. package/src/schema/column.ts +246 -246
  125. package/src/schema/relation.ts +35 -1
  126. package/src/schema/table.ts +28 -28
  127. package/src/schema/types.ts +41 -31
  128. package/src/orm/db-executor.ts +0 -11
  129. package/src/orm/orm-context.ts +0 -159
@@ -19,81 +19,134 @@ import {
19
19
  /**
20
20
  * Visitor for expression nodes
21
21
  */
22
- export interface ExpressionVisitor<R> {
23
- visitBinaryExpression(node: BinaryExpressionNode): R;
24
- visitLogicalExpression(node: LogicalExpressionNode): R;
25
- visitNullExpression(node: NullExpressionNode): R;
26
- visitInExpression(node: InExpressionNode): R;
27
- visitExistsExpression(node: ExistsExpressionNode): R;
28
- visitBetweenExpression(node: BetweenExpressionNode): R;
29
- }
30
-
31
- /**
32
- * Visitor for operand nodes
33
- */
34
- export interface OperandVisitor<R> {
35
- visitColumn(node: ColumnNode): R;
36
- visitLiteral(node: LiteralNode): R;
37
- visitFunction(node: FunctionNode): R;
38
- visitJsonPath(node: JsonPathNode): R;
39
- visitScalarSubquery(node: ScalarSubqueryNode): R;
40
- visitCaseExpression(node: CaseExpressionNode): R;
41
- visitWindowFunction(node: WindowFunctionNode): R;
42
- }
43
-
44
- const unsupportedExpression = (node: ExpressionNode): never => {
45
- throw new Error(`Unsupported expression type "${(node as any)?.type ?? 'unknown'}"`);
46
- };
22
+ export interface ExpressionVisitor<R> {
23
+ visitBinaryExpression?(node: BinaryExpressionNode): R;
24
+ visitLogicalExpression?(node: LogicalExpressionNode): R;
25
+ visitNullExpression?(node: NullExpressionNode): R;
26
+ visitInExpression?(node: InExpressionNode): R;
27
+ visitExistsExpression?(node: ExistsExpressionNode): R;
28
+ visitBetweenExpression?(node: BetweenExpressionNode): R;
29
+ otherwise?(node: ExpressionNode): R;
30
+ }
31
+
32
+ /**
33
+ * Visitor for operand nodes
34
+ */
35
+ export interface OperandVisitor<R> {
36
+ visitColumn?(node: ColumnNode): R;
37
+ visitLiteral?(node: LiteralNode): R;
38
+ visitFunction?(node: FunctionNode): R;
39
+ visitJsonPath?(node: JsonPathNode): R;
40
+ visitScalarSubquery?(node: ScalarSubqueryNode): R;
41
+ visitCaseExpression?(node: CaseExpressionNode): R;
42
+ visitWindowFunction?(node: WindowFunctionNode): R;
43
+ otherwise?(node: OperandNode): R;
44
+ }
45
+
46
+ type ExpressionDispatch = <R>(node: any, visitor: ExpressionVisitor<R>) => R;
47
+ type OperandDispatch = <R>(node: any, visitor: OperandVisitor<R>) => R;
48
+
49
+ const expressionDispatchers = new Map<string, ExpressionDispatch>();
50
+ const operandDispatchers = new Map<string, OperandDispatch>();
51
+
52
+ /**
53
+ * Registers a dispatcher for a custom expression node type.
54
+ * Allows new node kinds without modifying the core switch.
55
+ */
56
+ export const registerExpressionDispatcher = (type: string, dispatcher: ExpressionDispatch): void => {
57
+ expressionDispatchers.set(type, dispatcher);
58
+ };
59
+
60
+ /**
61
+ * Registers a dispatcher for a custom operand node type.
62
+ * Allows new node kinds without modifying the core switch.
63
+ */
64
+ export const registerOperandDispatcher = (type: string, dispatcher: OperandDispatch): void => {
65
+ operandDispatchers.set(type, dispatcher);
66
+ };
67
+
68
+ /**
69
+ * Clears all registered dispatchers. Primarily for tests.
70
+ */
71
+ export const clearExpressionDispatchers = (): void => expressionDispatchers.clear();
72
+ export const clearOperandDispatchers = (): void => operandDispatchers.clear();
73
+
74
+ const unsupportedExpression = (node: ExpressionNode): never => {
75
+ throw new Error(`Unsupported expression type "${(node as any)?.type ?? 'unknown'}"`);
76
+ };
47
77
 
48
78
  const unsupportedOperand = (node: OperandNode): never => {
49
79
  throw new Error(`Unsupported operand type "${(node as any)?.type ?? 'unknown'}"`);
50
80
  };
51
81
  /**
52
82
  * Dispatches an expression node to the visitor
53
- * @param node - Expression node to visit
54
- * @param visitor - Visitor implementation
55
- */
56
- export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>): R => {
57
- switch (node.type) {
58
- case 'BinaryExpression':
59
- return visitor.visitBinaryExpression(node);
60
- case 'LogicalExpression':
61
- return visitor.visitLogicalExpression(node);
62
- case 'NullExpression':
63
- return visitor.visitNullExpression(node);
64
- case 'InExpression':
65
- return visitor.visitInExpression(node);
66
- case 'ExistsExpression':
67
- return visitor.visitExistsExpression(node);
68
- case 'BetweenExpression':
69
- return visitor.visitBetweenExpression(node);
70
- default:
71
- return unsupportedExpression(node);
72
- }
73
- };
83
+ * @param node - Expression node to visit
84
+ * @param visitor - Visitor implementation
85
+ */
86
+ export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>): R => {
87
+ const dynamic = expressionDispatchers.get((node as any)?.type);
88
+ if (dynamic) return dynamic(node as any, visitor);
89
+
90
+ switch (node.type) {
91
+ case 'BinaryExpression':
92
+ if (visitor.visitBinaryExpression) return visitor.visitBinaryExpression(node);
93
+ break;
94
+ case 'LogicalExpression':
95
+ if (visitor.visitLogicalExpression) return visitor.visitLogicalExpression(node);
96
+ break;
97
+ case 'NullExpression':
98
+ if (visitor.visitNullExpression) return visitor.visitNullExpression(node);
99
+ break;
100
+ case 'InExpression':
101
+ if (visitor.visitInExpression) return visitor.visitInExpression(node);
102
+ break;
103
+ case 'ExistsExpression':
104
+ if (visitor.visitExistsExpression) return visitor.visitExistsExpression(node);
105
+ break;
106
+ case 'BetweenExpression':
107
+ if (visitor.visitBetweenExpression) return visitor.visitBetweenExpression(node);
108
+ break;
109
+ default:
110
+ break;
111
+ }
112
+ if (visitor.otherwise) return visitor.otherwise(node);
113
+ return unsupportedExpression(node);
114
+ };
74
115
 
75
116
  /**
76
117
  * Dispatches an operand node to the visitor
77
- * @param node - Operand node to visit
78
- * @param visitor - Visitor implementation
79
- */
80
- export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>): R => {
81
- switch (node.type) {
82
- case 'Column':
83
- return visitor.visitColumn(node);
84
- case 'Literal':
85
- return visitor.visitLiteral(node);
86
- case 'Function':
87
- return visitor.visitFunction(node);
88
- case 'JsonPath':
89
- return visitor.visitJsonPath(node);
90
- case 'ScalarSubquery':
91
- return visitor.visitScalarSubquery(node);
92
- case 'CaseExpression':
93
- return visitor.visitCaseExpression(node);
94
- case 'WindowFunction':
95
- return visitor.visitWindowFunction(node);
96
- default:
97
- return unsupportedOperand(node);
98
- }
99
- };
118
+ * @param node - Operand node to visit
119
+ * @param visitor - Visitor implementation
120
+ */
121
+ export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>): R => {
122
+ const dynamic = operandDispatchers.get((node as any)?.type);
123
+ if (dynamic) return dynamic(node as any, visitor);
124
+
125
+ switch (node.type) {
126
+ case 'Column':
127
+ if (visitor.visitColumn) return visitor.visitColumn(node);
128
+ break;
129
+ case 'Literal':
130
+ if (visitor.visitLiteral) return visitor.visitLiteral(node);
131
+ break;
132
+ case 'Function':
133
+ if (visitor.visitFunction) return visitor.visitFunction(node);
134
+ break;
135
+ case 'JsonPath':
136
+ if (visitor.visitJsonPath) return visitor.visitJsonPath(node);
137
+ break;
138
+ case 'ScalarSubquery':
139
+ if (visitor.visitScalarSubquery) return visitor.visitScalarSubquery(node);
140
+ break;
141
+ case 'CaseExpression':
142
+ if (visitor.visitCaseExpression) return visitor.visitCaseExpression(node);
143
+ break;
144
+ case 'WindowFunction':
145
+ if (visitor.visitWindowFunction) return visitor.visitWindowFunction(node);
146
+ break;
147
+ default:
148
+ break;
149
+ }
150
+ if (visitor.otherwise) return visitor.otherwise(node);
151
+ return unsupportedOperand(node);
152
+ };
@@ -1,5 +1,7 @@
1
1
  export * from './expression-nodes.js';
2
- export * from './expression-builders.js';
3
- export * from './window-functions.js';
4
- export * from './aggregate-functions.js';
5
- export * from './expression-visitor.js';
2
+ export * from './expression-builders.js';
3
+ export * from './window-functions.js';
4
+ export * from './aggregate-functions.js';
5
+ export * from './expression-visitor.js';
6
+ export * from './types.js';
7
+ export * from './adapters.js';
@@ -0,0 +1,15 @@
1
+ import { JoinNode } from './join.js';
2
+
3
+ /**
4
+ * Metadata stored on JoinNode.meta for higher-level concerns.
5
+ */
6
+ export interface JoinMetadata {
7
+ relationName?: string;
8
+ [key: string]: unknown;
9
+ }
10
+
11
+ /**
12
+ * Retrieves the relation name from join metadata if present.
13
+ */
14
+ export const getJoinRelationName = (join: JoinNode): string | undefined =>
15
+ (join.meta as JoinMetadata | undefined)?.relationName;
@@ -1,20 +1,22 @@
1
- import { JoinNode } from './join.js';
2
- import { ExpressionNode } from './expression.js';
3
- import { JoinKind } from '../sql/sql.js';
4
-
5
- /**
6
- * Creates a JoinNode ready for AST insertion.
7
- * Centralizing this avoids copy/pasted object literals when multiple services need to synthesize joins.
8
- */
9
- export const createJoinNode = (
10
- kind: JoinKind,
11
- tableName: string,
12
- condition: ExpressionNode,
13
- relationName?: string
14
- ): JoinNode => ({
15
- type: 'Join',
16
- kind,
17
- table: { type: 'Table', name: tableName },
18
- condition,
19
- relationName
20
- });
1
+ import { JoinNode } from './join.js';
2
+ import { ExpressionNode } from './expression.js';
3
+ import { JoinKind } from '../sql/sql.js';
4
+ import { JoinMetadata } from './join-metadata.js';
5
+ import { TableNode, FunctionTableNode } from './query.js';
6
+
7
+ /**
8
+ * Creates a JoinNode ready for AST insertion.
9
+ * Centralizing this avoids copy/pasted object literals when multiple services need to synthesize joins.
10
+ */
11
+ export const createJoinNode = (
12
+ kind: JoinKind,
13
+ tableName: string | TableNode | FunctionTableNode,
14
+ condition: ExpressionNode,
15
+ relationName?: string
16
+ ): JoinNode => ({
17
+ type: 'Join',
18
+ kind,
19
+ table: typeof tableName === 'string' ? { type: 'Table', name: tableName } as TableNode : (tableName as TableNode | FunctionTableNode),
20
+ condition,
21
+ meta: relationName ? ({ relationName } as JoinMetadata) : undefined
22
+ });
@@ -1,4 +1,4 @@
1
- import { TableNode } from './query.js';
1
+ import { TableNode, FunctionTableNode } from './query.js';
2
2
  import { ExpressionNode } from './expression.js';
3
3
  import { JoinKind } from '../sql/sql.js';
4
4
 
@@ -10,9 +10,9 @@ export interface JoinNode {
10
10
  /** Type of join (INNER, LEFT, RIGHT, etc.) */
11
11
  kind: JoinKind;
12
12
  /** Table to join */
13
- table: TableNode;
14
- /** Join condition expression */
13
+ table: TableNode | FunctionTableNode;
14
+ /** Join condition expression */
15
15
  condition: ExpressionNode;
16
- /** Optional relation name for code generation */
17
- relationName?: string;
16
+ /** Optional metadata for non-SQL concerns (e.g., relation name) */
17
+ meta?: Record<string, unknown>;
18
18
  }
@@ -8,7 +8,6 @@ import {
8
8
  OperandNode
9
9
  } from './expression.js';
10
10
  import { JoinNode } from './join.js';
11
- import { RelationType } from '../../schema/relation.js';
12
11
  import { OrderDirection } from '../sql/sql.js';
13
12
 
14
13
  /**
@@ -24,6 +23,27 @@ export interface TableNode {
24
23
  alias?: string;
25
24
  }
26
25
 
26
+ /**
27
+ * AST node representing a function used as a table source (table-valued function)
28
+ */
29
+ export interface FunctionTableNode {
30
+ type: 'FunctionTable';
31
+ /** Function name */
32
+ name: string;
33
+ /** Optional schema for the function (some dialects) */
34
+ schema?: string;
35
+ /** Function arguments as operand nodes */
36
+ args?: any[]; // use any to avoid circular import here; caller should supply OperandNode
37
+ /** Optional alias for the function table */
38
+ alias?: string;
39
+ /** LATERAL flag */
40
+ lateral?: boolean;
41
+ /** WITH ORDINALITY flag */
42
+ withOrdinality?: boolean;
43
+ /** Optional column aliases */
44
+ columnAliases?: string[];
45
+ }
46
+
27
47
  /**
28
48
  * AST node representing an ORDER BY clause
29
49
  */
@@ -36,101 +56,45 @@ export interface OrderByNode {
36
56
  }
37
57
 
38
58
  /**
39
- * Plan describing pivot columns needed for hydration
40
- */
41
- export interface HydrationPivotPlan {
42
- table: string;
43
- primaryKey: string;
44
- aliasPrefix: string;
45
- columns: string[];
46
- }
47
-
48
- /**
49
- * Plan for hydrating relationship data
59
+ * AST node representing a Common Table Expression (CTE)
50
60
  */
51
- export interface HydrationRelationPlan {
52
- /** Name of the relationship */
61
+ export interface CommonTableExpressionNode {
62
+ type: 'CommonTableExpression';
63
+ /** CTE name */
53
64
  name: string;
54
- /** Alias prefix for the relationship */
55
- aliasPrefix: string;
56
- /** Type of relationship */
57
- type: RelationType;
58
- /** Target table name */
59
- targetTable: string;
60
- /** Target table primary key */
61
- targetPrimaryKey: string;
62
- /** Foreign key column */
63
- foreignKey: string;
64
- /** Local key column */
65
- localKey: string;
66
- /** Columns to include */
67
- columns: string[];
68
- /** Optional pivot plan for many-to-many relationships */
69
- pivot?: HydrationPivotPlan;
65
+ /** Optional column names */
66
+ columns?: string[];
67
+ /** CTE query */
68
+ query: SelectQueryNode;
69
+ /** Whether the CTE is recursive */
70
+ recursive: boolean;
70
71
  }
71
72
 
72
73
  /**
73
- * Complete hydration plan for a query
74
+ * Supported set operation kinds for compound SELECT queries
74
75
  */
75
- export interface HydrationPlan {
76
- /** Root table name */
77
- rootTable: string;
78
- /** Root table primary key */
79
- rootPrimaryKey: string;
80
- /** Root table columns */
81
- rootColumns: string[];
82
- /** Relationship hydration plans */
83
- relations: HydrationRelationPlan[];
84
- }
76
+ export type SetOperationKind = 'UNION' | 'UNION ALL' | 'INTERSECT' | 'EXCEPT';
85
77
 
86
78
  /**
87
- * Query metadata including hydration information
79
+ * AST node representing a set operation (UNION, INTERSECT, etc.)
88
80
  */
89
- export interface QueryMetadata {
90
- /** Optional hydration plan */
91
- hydration?: HydrationPlan;
81
+ export interface SetOperationNode {
82
+ type: 'SetOperation';
83
+ /** Operator to combine queries */
84
+ operator: SetOperationKind;
85
+ /** Right-hand query in the compound expression */
86
+ query: SelectQueryNode;
92
87
  }
93
88
 
94
89
  /**
95
- * AST node representing a Common Table Expression (CTE)
90
+ * AST node representing a complete SELECT query
96
91
  */
97
- export interface CommonTableExpressionNode {
98
- type: 'CommonTableExpression';
99
- /** CTE name */
100
- name: string;
101
- /** Optional column names */
102
- columns?: string[];
103
- /** CTE query */
104
- query: SelectQueryNode;
105
- /** Whether the CTE is recursive */
106
- recursive: boolean;
107
- }
108
-
109
- /**
110
- * Supported set operation kinds for compound SELECT queries
111
- */
112
- export type SetOperationKind = 'UNION' | 'UNION ALL' | 'INTERSECT' | 'EXCEPT';
113
-
114
- /**
115
- * AST node representing a set operation (UNION, INTERSECT, etc.)
116
- */
117
- export interface SetOperationNode {
118
- type: 'SetOperation';
119
- /** Operator to combine queries */
120
- operator: SetOperationKind;
121
- /** Right-hand query in the compound expression */
122
- query: SelectQueryNode;
123
- }
124
-
125
- /**
126
- * AST node representing a complete SELECT query
127
- */
128
- export interface SelectQueryNode {
129
- type: 'SelectQuery';
92
+ export interface SelectQueryNode {
93
+ type: 'SelectQuery';
130
94
  /** Optional CTEs (WITH clauses) */
131
95
  ctes?: CommonTableExpressionNode[];
132
- /** FROM clause table */
133
- from: TableNode;
96
+ /** FROM clause table (either a Table or a FunctionTable) */
97
+ from: TableNode | FunctionTableNode;
134
98
  /** SELECT clause columns */
135
99
  columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
136
100
  /** JOIN clauses */
@@ -147,13 +111,13 @@ export interface SelectQueryNode {
147
111
  limit?: number;
148
112
  /** Optional OFFSET clause */
149
113
  offset?: number;
150
- /** Optional query metadata */
151
- meta?: QueryMetadata;
152
- /** Optional DISTINCT clause */
153
- distinct?: ColumnNode[];
154
- /** Optional set operations chaining this query with others */
155
- setOps?: SetOperationNode[];
156
- }
114
+ /** Optional query metadata */
115
+ meta?: Record<string, unknown>;
116
+ /** Optional DISTINCT clause */
117
+ distinct?: ColumnNode[];
118
+ /** Optional set operations chaining this query with others */
119
+ setOps?: SetOperationNode[];
120
+ }
157
121
 
158
122
  export interface InsertQueryNode {
159
123
  type: 'InsertQuery';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Minimal column reference used by AST builders.
3
+ * Accepts any object with a name and optional table/alias fields
4
+ * (schema ColumnDef/TableDef remain structurally compatible).
5
+ */
6
+ export interface ColumnRef {
7
+ name: string;
8
+ table?: string;
9
+ alias?: string;
10
+ }
11
+
12
+ /**
13
+ * Minimal table reference used by AST builders.
14
+ * Keeps AST decoupled from full schema TableDef shape.
15
+ */
16
+ export interface TableRef {
17
+ name: string;
18
+ schema?: string;
19
+ alias?: string;
20
+ }
@@ -1,8 +1,8 @@
1
- import { ColumnDef } from '../../schema/column.js';
2
- import { ColumnNode, LiteralNode, JsonPathNode, WindowFunctionNode } from './expression-nodes.js';
3
- import { columnOperand } from './expression-builders.js';
4
- import { OrderDirection } from '../sql/sql.js';
5
- import { OrderByNode } from './query.js';
1
+ import { ColumnNode, LiteralNode, JsonPathNode, WindowFunctionNode } from './expression-nodes.js';
2
+ import { columnOperand } from './expression-builders.js';
3
+ import { OrderDirection } from '../sql/sql.js';
4
+ import { OrderByNode } from './query.js';
5
+ import { ColumnRef } from './types.js';
6
6
 
7
7
  const buildWindowFunction = (
8
8
  name: string,
@@ -50,21 +50,21 @@ export const denseRank = (): WindowFunctionNode => buildWindowFunction('DENSE_RA
50
50
  * @param n - Number of buckets
51
51
  * @returns Window function node for NTILE
52
52
  */
53
- export const ntile = (n: number): WindowFunctionNode =>
54
- buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
53
+ export const ntile = (n: number): WindowFunctionNode =>
54
+ buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
55
55
 
56
56
  /**
57
57
  * Creates a LAG window function
58
58
  * @param col - Column to lag
59
59
  * @param offset - Offset (defaults to 1)
60
- * @param defaultValue - Default value if no row exists
61
- * @returns Window function node for LAG
62
- */
63
- export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
64
- const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
65
- columnOperand(col),
66
- { type: 'Literal', value: offset }
67
- ];
60
+ * @param defaultValue - Default value if no row exists
61
+ * @returns Window function node for LAG
62
+ */
63
+ export const lag = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
64
+ const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
65
+ columnOperand(col),
66
+ { type: 'Literal', value: offset }
67
+ ];
68
68
  if (defaultValue !== undefined) {
69
69
  args.push({ type: 'Literal', value: defaultValue });
70
70
  }
@@ -75,14 +75,14 @@ export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValu
75
75
  * Creates a LEAD window function
76
76
  * @param col - Column to lead
77
77
  * @param offset - Offset (defaults to 1)
78
- * @param defaultValue - Default value if no row exists
79
- * @returns Window function node for LEAD
80
- */
81
- export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
82
- const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
83
- columnOperand(col),
84
- { type: 'Literal', value: offset }
85
- ];
78
+ * @param defaultValue - Default value if no row exists
79
+ * @returns Window function node for LEAD
80
+ */
81
+ export const lead = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
82
+ const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
83
+ columnOperand(col),
84
+ { type: 'Literal', value: offset }
85
+ ];
86
86
  if (defaultValue !== undefined) {
87
87
  args.push({ type: 'Literal', value: defaultValue });
88
88
  }
@@ -91,19 +91,19 @@ export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultVal
91
91
 
92
92
  /**
93
93
  * Creates a FIRST_VALUE window function
94
- * @param col - Column to get first value from
95
- * @returns Window function node for FIRST_VALUE
96
- */
97
- export const firstValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
98
- buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
94
+ * @param col - Column to get first value from
95
+ * @returns Window function node for FIRST_VALUE
96
+ */
97
+ export const firstValue = (col: ColumnRef | ColumnNode): WindowFunctionNode =>
98
+ buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
99
99
 
100
100
  /**
101
101
  * Creates a LAST_VALUE window function
102
- * @param col - Column to get last value from
103
- * @returns Window function node for LAST_VALUE
104
- */
105
- export const lastValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
106
- buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
102
+ * @param col - Column to get last value from
103
+ * @returns Window function node for LAST_VALUE
104
+ */
105
+ export const lastValue = (col: ColumnRef | ColumnNode): WindowFunctionNode =>
106
+ buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
107
107
 
108
108
  /**
109
109
  * Creates a custom window function
@@ -113,28 +113,28 @@ export const lastValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
113
113
  * @param orderBy - Optional ORDER BY clauses
114
114
  * @returns Window function node
115
115
  */
116
- export const windowFunction = (
117
- name: string,
118
- args: (ColumnDef | ColumnNode | LiteralNode | JsonPathNode)[] = [],
119
- partitionBy?: (ColumnDef | ColumnNode)[],
120
- orderBy?: { column: ColumnDef | ColumnNode; direction: OrderDirection }[]
121
- ): WindowFunctionNode => {
122
- const nodeArgs = args.map(arg => {
123
- if (typeof (arg as LiteralNode).value !== 'undefined') {
124
- return arg as LiteralNode;
125
- }
126
- if ('path' in arg) {
127
- return arg as JsonPathNode;
128
- }
129
- return columnOperand(arg as ColumnDef | ColumnNode);
130
- });
131
-
132
- const partitionNodes = partitionBy?.map(col => columnOperand(col)) ?? undefined;
133
- const orderNodes: OrderByNode[] | undefined = orderBy?.map(o => ({
134
- type: 'OrderBy',
135
- column: columnOperand(o.column),
136
- direction: o.direction
137
- }));
116
+ export const windowFunction = (
117
+ name: string,
118
+ args: (ColumnRef | ColumnNode | LiteralNode | JsonPathNode)[] = [],
119
+ partitionBy?: (ColumnRef | ColumnNode)[],
120
+ orderBy?: { column: ColumnRef | ColumnNode; direction: OrderDirection }[]
121
+ ): WindowFunctionNode => {
122
+ const nodeArgs = args.map(arg => {
123
+ if (typeof (arg as LiteralNode).value !== 'undefined') {
124
+ return arg as LiteralNode;
125
+ }
126
+ if ('path' in arg) {
127
+ return arg as JsonPathNode;
128
+ }
129
+ return columnOperand(arg as ColumnRef | ColumnNode);
130
+ });
131
+
132
+ const partitionNodes = partitionBy?.map(col => columnOperand(col)) ?? undefined;
133
+ const orderNodes: OrderByNode[] | undefined = orderBy?.map(o => ({
134
+ type: 'OrderBy',
135
+ column: columnOperand(o.column),
136
+ direction: o.direction
137
+ }));
138
138
 
139
139
  return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
140
140
  };