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.
- package/README.md +69 -67
- package/dist/decorators/index.cjs +1983 -224
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -6
- package/dist/decorators/index.d.ts +6 -6
- package/dist/decorators/index.js +1982 -224
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +5284 -3751
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -169
- package/dist/index.d.ts +524 -169
- package/dist/index.js +5197 -3736
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-BKZrMRCQ.d.cts} +555 -94
- package/dist/{select-CCp1oz9p.d.ts → select-BKZrMRCQ.d.ts} +555 -94
- package/package.json +1 -1
- package/src/codegen/naming-strategy.ts +64 -0
- package/src/codegen/typescript.ts +19 -21
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +17 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +172 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/column.ts +13 -4
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +37 -19
- package/src/orm/entity-context.ts +30 -0
- package/src/orm/entity-meta.ts +2 -2
- package/src/orm/entity-metadata.ts +8 -6
- package/src/orm/entity.ts +72 -41
- package/src/orm/execute.ts +42 -25
- package/src/orm/execution-context.ts +12 -0
- package/src/orm/hydration-context.ts +14 -0
- package/src/orm/hydration.ts +25 -17
- package/src/orm/identity-map.ts +4 -0
- package/src/orm/interceptor-pipeline.ts +29 -0
- package/src/orm/lazy-batch.ts +50 -6
- package/src/orm/orm-session.ts +234 -0
- package/src/orm/orm.ts +58 -0
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +48 -3
- package/src/orm/relations/belongs-to.ts +45 -44
- package/src/orm/relations/has-many.ts +44 -43
- package/src/orm/relations/has-one.ts +140 -0
- package/src/orm/relations/many-to-many.ts +46 -45
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +66 -61
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +575 -64
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
- 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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
case '
|
|
65
|
-
return visitor.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
case '
|
|
89
|
-
return visitor.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
case '
|
|
95
|
-
return visitor.
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
});
|
package/src/core/ast/join.ts
CHANGED
|
@@ -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
|
-
|
|
13
|
+
table: TableNode | FunctionTableNode;
|
|
14
|
+
/** Join condition expression */
|
|
15
15
|
condition: ExpressionNode;
|
|
16
|
-
/** Optional
|
|
17
|
-
|
|
16
|
+
/** Optional metadata for non-SQL concerns (e.g., relation name) */
|
|
17
|
+
meta?: Record<string, unknown>;
|
|
18
18
|
}
|
package/src/core/ast/query.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
52
|
-
|
|
61
|
+
export interface CommonTableExpressionNode {
|
|
62
|
+
type: 'CommonTableExpression';
|
|
63
|
+
/** CTE name */
|
|
53
64
|
name: string;
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
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
|
-
*
|
|
74
|
+
* Supported set operation kinds for compound SELECT queries
|
|
74
75
|
*/
|
|
75
|
-
export
|
|
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
|
-
*
|
|
79
|
+
* AST node representing a set operation (UNION, INTERSECT, etc.)
|
|
88
80
|
*/
|
|
89
|
-
export interface
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
90
|
+
* AST node representing a complete SELECT query
|
|
96
91
|
*/
|
|
97
|
-
export interface
|
|
98
|
-
type: '
|
|
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?:
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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: (
|
|
119
|
-
partitionBy?: (
|
|
120
|
-
orderBy?: { column:
|
|
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
|
|
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
|
};
|