metal-orm 1.0.14 → 1.0.15
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 +40 -45
- package/dist/decorators/index.cjs +1600 -27
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -2
- package/dist/decorators/index.d.ts +6 -2
- package/dist/decorators/index.js +1599 -27
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +4608 -3429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -159
- package/dist/index.d.ts +511 -159
- package/dist/index.js +4526 -3415
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
- package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
- package/package.json +1 -1
- package/src/codegen/typescript.ts +38 -35
- 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 +16 -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 +144 -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/relations.ts +15 -0
- package/src/index.ts +30 -19
- package/src/orm/entity-metadata.ts +7 -0
- package/src/orm/entity.ts +58 -27
- package/src/orm/hydration.ts +25 -17
- package/src/orm/lazy-batch.ts +46 -2
- package/src/orm/orm-context.ts +60 -60
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +43 -2
- package/src/orm/relations/has-one.ts +139 -0
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +60 -60
- 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 +66 -61
- 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
|
@@ -1,204 +1,181 @@
|
|
|
1
|
-
import { CompilerContext, Dialect } from '../abstract.js';
|
|
2
|
-
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
|
|
3
|
-
import { ColumnNode } from '../../ast/expression.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
protected
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (!ast.ctes || ast.ctes.length === 0) return '';
|
|
183
|
-
const hasRecursive = ast.ctes.some(cte => cte.recursive);
|
|
184
|
-
const prefix = hasRecursive ? 'WITH RECURSIVE ' : 'WITH ';
|
|
185
|
-
const cteDefs = ast.ctes.map(cte => {
|
|
186
|
-
const name = this.quoteIdentifier(cte.name);
|
|
187
|
-
const cols = cte.columns && cte.columns.length
|
|
188
|
-
? `(${cte.columns.map(c => this.quoteIdentifier(c)).join(', ')})`
|
|
189
|
-
: '';
|
|
190
|
-
const query = this.stripTrailingSemicolon(this.compileSelectAst(this.normalizeSelectAst(cte.query), ctx));
|
|
191
|
-
return `${name}${cols} AS (${query})`;
|
|
192
|
-
}).join(', ');
|
|
193
|
-
return `${prefix}${cteDefs} `;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
protected stripTrailingSemicolon(sql: string): string {
|
|
197
|
-
return sql.trim().replace(/;$/, '');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
protected wrapSetOperand(sql: string): string {
|
|
201
|
-
const trimmed = this.stripTrailingSemicolon(sql);
|
|
202
|
-
return `(${trimmed})`;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
1
|
+
import { CompilerContext, Dialect } from '../abstract.js';
|
|
2
|
+
import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode } from '../../ast/query.js';
|
|
3
|
+
import { ColumnNode } from '../../ast/expression.js';
|
|
4
|
+
import { FunctionTableFormatter, FunctionTableNode } from './function-table-formatter.js';
|
|
5
|
+
import { PaginationStrategy, StandardLimitOffsetPagination } from './pagination-strategy.js';
|
|
6
|
+
import { CteCompiler } from './cte-compiler.js';
|
|
7
|
+
import { ReturningStrategy, NoReturningStrategy } from './returning-strategy.js';
|
|
8
|
+
import { JoinCompiler } from './join-compiler.js';
|
|
9
|
+
import { GroupByCompiler } from './groupby-compiler.js';
|
|
10
|
+
import { OrderByCompiler } from './orderby-compiler.js';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export abstract class SqlDialectBase extends Dialect {
|
|
14
|
+
abstract quoteIdentifier(id: string): string;
|
|
15
|
+
|
|
16
|
+
protected paginationStrategy: PaginationStrategy = new StandardLimitOffsetPagination();
|
|
17
|
+
protected returningStrategy: ReturningStrategy = new NoReturningStrategy();
|
|
18
|
+
|
|
19
|
+
protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
20
|
+
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
21
|
+
const ctes = CteCompiler.compileCtes(
|
|
22
|
+
ast,
|
|
23
|
+
ctx,
|
|
24
|
+
this.quoteIdentifier.bind(this),
|
|
25
|
+
this.compileSelectAst.bind(this),
|
|
26
|
+
this.normalizeSelectAst?.bind(this) ?? ((a) => a),
|
|
27
|
+
this.stripTrailingSemicolon.bind(this)
|
|
28
|
+
);
|
|
29
|
+
const baseAst: SelectQueryNode = hasSetOps
|
|
30
|
+
? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
|
|
31
|
+
: ast;
|
|
32
|
+
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
33
|
+
if (!hasSetOps) {
|
|
34
|
+
return `${ctes}${baseSelect}`;
|
|
35
|
+
}
|
|
36
|
+
return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private compileSelectWithSetOps(
|
|
40
|
+
ast: SelectQueryNode,
|
|
41
|
+
baseSelect: string,
|
|
42
|
+
ctes: string,
|
|
43
|
+
ctx: CompilerContext
|
|
44
|
+
): string {
|
|
45
|
+
const compound = ast.setOps!
|
|
46
|
+
.map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
|
|
47
|
+
.join(' ');
|
|
48
|
+
const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
|
|
49
|
+
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
50
|
+
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
51
|
+
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
55
|
+
const table = this.compileTableName(ast.into);
|
|
56
|
+
const columnList = this.compileInsertColumnList(ast.columns);
|
|
57
|
+
const values = this.compileInsertValues(ast.values, ctx);
|
|
58
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
59
|
+
return `INSERT INTO ${table} (${columnList}) VALUES ${values}${returning}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
|
|
63
|
+
return this.returningStrategy.compileReturning(returning, ctx);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private compileInsertColumnList(columns: ColumnNode[]): string {
|
|
67
|
+
return columns
|
|
68
|
+
.map(column => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`)
|
|
69
|
+
.join(', ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private compileInsertValues(values: any[][], ctx: CompilerContext): string {
|
|
73
|
+
return values
|
|
74
|
+
.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`)
|
|
75
|
+
.join(', ');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
79
|
+
const columns = this.compileSelectColumns(ast, ctx);
|
|
80
|
+
const from = this.compileFrom(ast.from, ctx);
|
|
81
|
+
const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
|
|
82
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
83
|
+
const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
|
|
84
|
+
const having = this.compileHaving(ast, ctx);
|
|
85
|
+
const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
|
|
86
|
+
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
87
|
+
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
91
|
+
const table = this.compileTableName(ast.table);
|
|
92
|
+
const assignments = this.compileUpdateAssignments(ast.set, ctx);
|
|
93
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
94
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
95
|
+
return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private compileUpdateAssignments(
|
|
99
|
+
assignments: { column: ColumnNode; value: any }[],
|
|
100
|
+
ctx: CompilerContext
|
|
101
|
+
): string {
|
|
102
|
+
return assignments
|
|
103
|
+
.map(assignment => {
|
|
104
|
+
const col = assignment.column;
|
|
105
|
+
const target = `${this.quoteIdentifier(col.table)}.${this.quoteIdentifier(col.name)}`;
|
|
106
|
+
const value = this.compileOperand(assignment.value, ctx);
|
|
107
|
+
return `${target} = ${value}`;
|
|
108
|
+
})
|
|
109
|
+
.join(', ');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
113
|
+
const table = this.compileTableName(ast.from);
|
|
114
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
115
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
116
|
+
return `DELETE FROM ${table}${whereClause}${returning}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected formatReturningColumns(returning: ColumnNode[]): string {
|
|
120
|
+
return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected compileDistinct(ast: SelectQueryNode): string {
|
|
124
|
+
return ast.distinct ? 'DISTINCT ' : '';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected compileSelectColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
128
|
+
return ast.columns.map(c => {
|
|
129
|
+
const expr = this.compileOperand(c, ctx);
|
|
130
|
+
if (c.alias) {
|
|
131
|
+
if (c.alias.includes('(')) return c.alias;
|
|
132
|
+
return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
|
|
133
|
+
}
|
|
134
|
+
return expr;
|
|
135
|
+
}).join(', ');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
protected compileFrom(ast: SelectQueryNode['from'], ctx?: CompilerContext): string {
|
|
139
|
+
const tableSource = ast as any;
|
|
140
|
+
if (tableSource.type === 'FunctionTable') {
|
|
141
|
+
return this.compileFunctionTable(tableSource, ctx);
|
|
142
|
+
}
|
|
143
|
+
return this.compileTableSource(tableSource);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected compileFunctionTable(fn: FunctionTableNode, ctx?: CompilerContext): string {
|
|
147
|
+
return FunctionTableFormatter.format(fn, ctx, this);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
protected compileTableSource(table: TableSourceNode): string {
|
|
151
|
+
const base = this.compileTableName(table);
|
|
152
|
+
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
protected compileTableName(table: { name: string; schema?: string }): string {
|
|
156
|
+
if (table.schema) {
|
|
157
|
+
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
158
|
+
}
|
|
159
|
+
return this.quoteIdentifier(table.name);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
163
|
+
if (!ast.having) return '';
|
|
164
|
+
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
protected stripTrailingSemicolon(sql: string): string {
|
|
168
|
+
return sql.trim().replace(/;$/, '');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
protected wrapSetOperand(sql: string): string {
|
|
172
|
+
const trimmed = this.stripTrailingSemicolon(sql);
|
|
173
|
+
return `(${trimmed})`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
interface TableSourceNode {
|
|
178
|
+
name: string;
|
|
179
|
+
schema?: string;
|
|
180
|
+
alias?: string;
|
|
181
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Dialect factory for the SQL DSL.
|
|
2
|
+
// Centralizes how we go from a symbolic name ("sqlite") to a concrete Dialect instance.
|
|
3
|
+
|
|
4
|
+
import { Dialect } from './abstract.js';
|
|
5
|
+
import { PostgresDialect } from './postgres/index.js';
|
|
6
|
+
import { MySqlDialect } from './mysql/index.js';
|
|
7
|
+
import { SqliteDialect } from './sqlite/index.js';
|
|
8
|
+
import { SqlServerDialect } from './mssql/index.js';
|
|
9
|
+
|
|
10
|
+
export type DialectKey =
|
|
11
|
+
| 'postgres'
|
|
12
|
+
| 'mysql'
|
|
13
|
+
| 'sqlite'
|
|
14
|
+
| 'mssql'
|
|
15
|
+
| (string & {}); // allow user-defined keys without constraining too much
|
|
16
|
+
|
|
17
|
+
type DialectFactoryFn = () => Dialect;
|
|
18
|
+
|
|
19
|
+
export class DialectFactory {
|
|
20
|
+
private static registry = new Map<DialectKey, DialectFactoryFn>();
|
|
21
|
+
private static defaultsInitialized = false;
|
|
22
|
+
|
|
23
|
+
private static ensureDefaults(): void {
|
|
24
|
+
if (this.defaultsInitialized) return;
|
|
25
|
+
this.defaultsInitialized = true;
|
|
26
|
+
|
|
27
|
+
// Register built-in dialects only if no override exists yet.
|
|
28
|
+
if (!this.registry.has('postgres')) {
|
|
29
|
+
this.registry.set('postgres', () => new PostgresDialect());
|
|
30
|
+
}
|
|
31
|
+
if (!this.registry.has('mysql')) {
|
|
32
|
+
this.registry.set('mysql', () => new MySqlDialect());
|
|
33
|
+
}
|
|
34
|
+
if (!this.registry.has('sqlite')) {
|
|
35
|
+
this.registry.set('sqlite', () => new SqliteDialect());
|
|
36
|
+
}
|
|
37
|
+
if (!this.registry.has('mssql')) {
|
|
38
|
+
this.registry.set('mssql', () => new SqlServerDialect());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Register (or override) a dialect factory for a key.
|
|
44
|
+
*
|
|
45
|
+
* Examples:
|
|
46
|
+
* DialectFactory.register('sqlite', () => new SqliteDialect());
|
|
47
|
+
* DialectFactory.register('my-tenant-dialect', () => new CustomDialect());
|
|
48
|
+
*/
|
|
49
|
+
public static register(key: DialectKey, factory: DialectFactoryFn): void {
|
|
50
|
+
this.registry.set(key, factory);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a key into a Dialect instance.
|
|
55
|
+
* Throws if the key is not registered.
|
|
56
|
+
*/
|
|
57
|
+
public static create(key: DialectKey): Dialect {
|
|
58
|
+
this.ensureDefaults();
|
|
59
|
+
const factory = this.registry.get(key);
|
|
60
|
+
if (!factory) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Dialect "${String(
|
|
63
|
+
key
|
|
64
|
+
)}" is not registered. Use DialectFactory.register(...) to register it.`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return factory();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Clear all registrations (mainly for tests).
|
|
72
|
+
* Built-ins will be re-registered lazily on the next create().
|
|
73
|
+
*/
|
|
74
|
+
public static clear(): void {
|
|
75
|
+
this.registry.clear();
|
|
76
|
+
this.defaultsInitialized = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Helper to normalize either a Dialect instance OR a key into a Dialect instance.
|
|
82
|
+
* This is what query builders will use.
|
|
83
|
+
*/
|
|
84
|
+
export const resolveDialectInput = (
|
|
85
|
+
dialect: Dialect | DialectKey
|
|
86
|
+
): Dialect => {
|
|
87
|
+
if (typeof dialect === 'string') {
|
|
88
|
+
return DialectFactory.create(dialect);
|
|
89
|
+
}
|
|
90
|
+
return dialect;
|
|
91
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
2
|
+
import { FunctionRenderContext } from '../../functions/types.js';
|
|
3
|
+
import { LiteralNode } from '../../ast/expression.js';
|
|
4
|
+
|
|
5
|
+
export class MssqlFunctionStrategy extends StandardFunctionStrategy {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.registerOverrides();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private registerOverrides() {
|
|
12
|
+
// Override Standard/Abstract definitions with MSSQL specifics
|
|
13
|
+
|
|
14
|
+
// Date/Time functions
|
|
15
|
+
this.add('NOW', () => `GETDATE()`);
|
|
16
|
+
this.add('CURRENT_DATE', () => `CAST(GETDATE() AS DATE)`);
|
|
17
|
+
this.add('CURRENT_TIME', () => `CAST(GETDATE() AS TIME)`);
|
|
18
|
+
this.add('UTC_NOW', () => `GETUTCDATE()`);
|
|
19
|
+
|
|
20
|
+
this.add('EXTRACT', ({ compiledArgs }) => {
|
|
21
|
+
if (compiledArgs.length !== 2) throw new Error('EXTRACT expects 2 arguments (part, date)');
|
|
22
|
+
const [part, date] = compiledArgs;
|
|
23
|
+
const partClean = part.replace(/['"]/g, '').toLowerCase();
|
|
24
|
+
return `DATEPART(${partClean}, ${date})`;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
this.add('YEAR', ({ compiledArgs }) => {
|
|
28
|
+
if (compiledArgs.length !== 1) throw new Error('YEAR expects 1 argument');
|
|
29
|
+
return `YEAR(${compiledArgs[0]})`;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
this.add('MONTH', ({ compiledArgs }) => {
|
|
33
|
+
if (compiledArgs.length !== 1) throw new Error('MONTH expects 1 argument');
|
|
34
|
+
return `MONTH(${compiledArgs[0]})`;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.add('DAY', ({ compiledArgs }) => {
|
|
38
|
+
if (compiledArgs.length !== 1) throw new Error('DAY expects 1 argument');
|
|
39
|
+
return `DAY(${compiledArgs[0]})`;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.add('DATE_ADD', ({ node, compiledArgs }) => {
|
|
43
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_ADD expects 3 arguments (date, interval, unit)');
|
|
44
|
+
const [date, interval] = compiledArgs;
|
|
45
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
46
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
47
|
+
return `DATEADD(${unitClean}, ${interval}, ${date})`;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.add('DATE_SUB', ({ node, compiledArgs }) => {
|
|
51
|
+
if (compiledArgs.length !== 3) throw new Error('DATE_SUB expects 3 arguments (date, interval, unit)');
|
|
52
|
+
const [date, interval] = compiledArgs;
|
|
53
|
+
const unitArg = node.args[2] as LiteralNode;
|
|
54
|
+
const unitClean = String(unitArg.value).replace(/['"]/g, '').toLowerCase();
|
|
55
|
+
return `DATEADD(${unitClean}, -${interval}, ${date})`;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
this.add('DATE_DIFF', ({ compiledArgs }) => {
|
|
59
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_DIFF expects 2 arguments');
|
|
60
|
+
const [date1, date2] = compiledArgs;
|
|
61
|
+
return `DATEDIFF(day, ${date2}, ${date1})`;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.add('DATE_FORMAT', ({ compiledArgs }) => {
|
|
65
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_FORMAT expects 2 arguments');
|
|
66
|
+
const [date, format] = compiledArgs;
|
|
67
|
+
return `FORMAT(${date}, ${format})`;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.add('UNIX_TIMESTAMP', () => `DATEDIFF(SECOND, '1970-01-01', GETUTCDATE())`);
|
|
71
|
+
|
|
72
|
+
this.add('FROM_UNIXTIME', ({ compiledArgs }) => {
|
|
73
|
+
if (compiledArgs.length !== 1) throw new Error('FROM_UNIXTIME expects 1 argument');
|
|
74
|
+
return `DATEADD(SECOND, ${compiledArgs[0]}, '1970-01-01')`;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
this.add('END_OF_MONTH', ({ compiledArgs }) => {
|
|
78
|
+
if (compiledArgs.length !== 1) throw new Error('END_OF_MONTH expects 1 argument');
|
|
79
|
+
return `EOMONTH(${compiledArgs[0]})`;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.add('DAY_OF_WEEK', ({ compiledArgs }) => {
|
|
83
|
+
if (compiledArgs.length !== 1) throw new Error('DAY_OF_WEEK expects 1 argument');
|
|
84
|
+
return `DATEPART(dw, ${compiledArgs[0]})`;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
this.add('WEEK_OF_YEAR', ({ compiledArgs }) => {
|
|
88
|
+
if (compiledArgs.length !== 1) throw new Error('WEEK_OF_YEAR expects 1 argument');
|
|
89
|
+
return `DATEPART(wk, ${compiledArgs[0]})`;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.add('DATE_TRUNC', ({ node, compiledArgs }) => {
|
|
93
|
+
if (compiledArgs.length !== 2) throw new Error('DATE_TRUNC expects 2 arguments (part, date)');
|
|
94
|
+
const [, date] = compiledArgs;
|
|
95
|
+
const partArg = node.args[0] as LiteralNode;
|
|
96
|
+
const partClean = String(partArg.value).replace(/['"]/g, '').toLowerCase();
|
|
97
|
+
// SQL Server 2022+ has DATETRUNC
|
|
98
|
+
return `DATETRUNC(${partClean}, ${date})`;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|