metal-orm 1.1.25 → 1.1.27
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/dist/index.cjs +1151 -510
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +281 -248
- package/dist/index.d.ts +281 -248
- package/dist/index.js +1133 -508
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ddl/dialects/index.ts +5 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +129 -126
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +119 -111
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +173 -164
- package/src/core/ddl/dialects/render-reference.test.ts +37 -57
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +110 -121
- package/src/core/ddl/schema-dialect-composer.ts +129 -0
- package/src/core/ddl/schema-dialect.ts +40 -27
- package/src/core/ddl/schema-diff.ts +119 -90
- package/src/core/dialect/abstract.ts +7 -229
- package/src/core/dialect/base/sql-dialect-composer.ts +294 -0
- package/src/core/dialect/base/standard-sql-services.ts +2 -6
- package/src/core/dialect/base/upsert-strategy.ts +1 -2
- package/src/core/dialect/dialect-factory.ts +17 -49
- package/src/core/dialect/mssql/index.ts +56 -27
- package/src/core/dialect/mysql/index.ts +69 -36
- package/src/core/dialect/postgres/index.ts +71 -43
- package/src/core/dialect/sqlite/index.ts +68 -38
- package/src/core/driver/mssql-driver.ts +6 -8
- package/src/core/driver/mysql-driver.ts +6 -8
- package/src/core/driver/postgres-driver.ts +6 -8
- package/src/core/driver/sqlite-driver.ts +6 -8
- package/src/index.ts +13 -1
- package/src/core/ddl/dialects/base-schema-dialect.ts +0 -96
- package/src/core/dialect/base/sql-dialect.ts +0 -217
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DeleteQueryNode,
|
|
3
|
+
InsertQueryNode,
|
|
4
|
+
OrderByNode,
|
|
5
|
+
OrderingTerm,
|
|
6
|
+
SelectQueryNode,
|
|
7
|
+
SetOperationKind,
|
|
8
|
+
TableNode,
|
|
9
|
+
UpdateQueryNode
|
|
10
|
+
} from '../../ast/query.js';
|
|
11
|
+
import type {
|
|
12
|
+
ColumnNode,
|
|
13
|
+
ExpressionNode,
|
|
14
|
+
FunctionNode,
|
|
15
|
+
JsonPathNode,
|
|
16
|
+
OperandNode
|
|
17
|
+
} from '../../ast/expression.js';
|
|
18
|
+
import type { FunctionStrategy } from '../../functions/types.js';
|
|
19
|
+
import { StandardFunctionStrategy } from '../../functions/standard-strategy.js';
|
|
20
|
+
import type { TableFunctionStrategy } from '../../functions/table-types.js';
|
|
21
|
+
import { StandardTableFunctionStrategy } from '../../functions/standard-table-strategy.js';
|
|
22
|
+
import type { CompilerContext, Dialect } from '../abstract.js';
|
|
23
|
+
import type { ProcedureCompilerServices } from '../capabilities/procedure-compiler.js';
|
|
24
|
+
import { ExpressionCompilerRegistry } from './expression-compiler-registry.js';
|
|
25
|
+
import { SelectAstNormalizer } from './select-ast-normalizer.js';
|
|
26
|
+
import { StandardLimitOffsetPagination } from './pagination-strategy.js';
|
|
27
|
+
import type { PaginationStrategy } from './pagination-strategy.js';
|
|
28
|
+
import { NoReturningStrategy } from './returning-strategy.js';
|
|
29
|
+
import type { ReturningStrategy } from './returning-strategy.js';
|
|
30
|
+
import { NoUpsertStrategy } from './upsert-strategy.js';
|
|
31
|
+
import type { UpsertStrategy } from './upsert-strategy.js';
|
|
32
|
+
import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
|
|
33
|
+
import { StandardSelectCompiler } from './standard-select-compiler.js';
|
|
34
|
+
import { StandardInsertCompiler } from './standard-insert-compiler.js';
|
|
35
|
+
import { StandardUpdateCompiler } from './standard-update-compiler.js';
|
|
36
|
+
import { StandardDeleteCompiler } from './standard-delete-compiler.js';
|
|
37
|
+
import type { StandardSqlCompilerServices } from './standard-sql-services.js';
|
|
38
|
+
import type { SqlCompilerFactory, SqlCompilerSet } from './sql-compiler-set.js';
|
|
39
|
+
|
|
40
|
+
export interface SqlDialectExpressionApi {
|
|
41
|
+
registerExpressionCompiler<T extends ExpressionNode>(
|
|
42
|
+
type: T['type'],
|
|
43
|
+
compiler: (node: T, ctx: CompilerContext) => string
|
|
44
|
+
): void;
|
|
45
|
+
registerOperandCompiler<T extends OperandNode>(
|
|
46
|
+
type: T['type'],
|
|
47
|
+
compiler: (node: T, ctx: CompilerContext) => string
|
|
48
|
+
): void;
|
|
49
|
+
compileExpression(node: ExpressionNode, ctx: CompilerContext): string;
|
|
50
|
+
compileOperand(node: OperandNode, ctx: CompilerContext): string;
|
|
51
|
+
compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SqlDialectRuntimeServices extends ProcedureCompilerServices {
|
|
55
|
+
compileExpression(node: ExpressionNode, ctx: CompilerContext): string;
|
|
56
|
+
compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string;
|
|
57
|
+
normalizeSelectAst(ast: SelectQueryNode): SelectQueryNode;
|
|
58
|
+
compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SqlDialectComposition {
|
|
62
|
+
dialect: Dialect;
|
|
63
|
+
runtime: SqlDialectRuntimeServices;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SqlDialectConfig {
|
|
67
|
+
/** Human-readable/backend identifier used by diagnostics and strategies. */
|
|
68
|
+
name: string;
|
|
69
|
+
quoteIdentifier(id: string): string;
|
|
70
|
+
formatPlaceholder?(index: number): string;
|
|
71
|
+
compileJsonPath?(node: JsonPathNode): string;
|
|
72
|
+
functionStrategy?: FunctionStrategy;
|
|
73
|
+
tableFunctionStrategy?: TableFunctionStrategy;
|
|
74
|
+
paginationStrategy?: PaginationStrategy;
|
|
75
|
+
returningStrategy?: ReturningStrategy;
|
|
76
|
+
upsertStrategy?: UpsertStrategy;
|
|
77
|
+
compilerFactory?: SqlCompilerFactory;
|
|
78
|
+
supportsDmlReturning?: boolean;
|
|
79
|
+
supportsSetOperation?(kind: SetOperationKind): boolean;
|
|
80
|
+
compileSetTarget?(column: ColumnNode, table: TableNode): string;
|
|
81
|
+
renderOrderByNulls?(order: OrderByNode): string | undefined;
|
|
82
|
+
renderOrderByCollation?(order: OrderByNode): string | undefined;
|
|
83
|
+
configureExpressions?(api: SqlDialectExpressionApi): void;
|
|
84
|
+
describe?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const terminate = (sql: string): string => {
|
|
88
|
+
const trimmed = sql.trim();
|
|
89
|
+
return trimmed.endsWith(';') ? trimmed : `${trimmed};`;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Assembles a full SQL dialect from independent compiler components.
|
|
94
|
+
* No inheritance or concrete dialect class participates in the compilation path.
|
|
95
|
+
*/
|
|
96
|
+
export const composeSqlDialect = (config: SqlDialectConfig): SqlDialectComposition => {
|
|
97
|
+
const functionStrategy = config.functionStrategy ?? new StandardFunctionStrategy();
|
|
98
|
+
const tableFunctionStrategy = config.tableFunctionStrategy ?? new StandardTableFunctionStrategy();
|
|
99
|
+
const paginationStrategy = config.paginationStrategy ?? new StandardLimitOffsetPagination();
|
|
100
|
+
const returningStrategy = config.returningStrategy ?? new NoReturningStrategy();
|
|
101
|
+
const upsertStrategy = config.upsertStrategy ?? new NoUpsertStrategy();
|
|
102
|
+
const selectAstNormalizer = new SelectAstNormalizer(
|
|
103
|
+
kind => config.supportsSetOperation?.(kind) ?? true
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
let compilerSet!: SqlCompilerSet;
|
|
107
|
+
let expressionRegistry!: ExpressionCompilerRegistry;
|
|
108
|
+
|
|
109
|
+
const createCompilerContext = (): CompilerContext => {
|
|
110
|
+
const params: unknown[] = [];
|
|
111
|
+
let counter = 0;
|
|
112
|
+
return {
|
|
113
|
+
params,
|
|
114
|
+
addParameter(value: unknown): string {
|
|
115
|
+
counter += 1;
|
|
116
|
+
params.push(value);
|
|
117
|
+
return config.formatPlaceholder?.(counter) ?? '?';
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const compileSelectAst = (ast: SelectQueryNode, ctx: CompilerContext): string =>
|
|
123
|
+
compilerSet.select.compile(ast, ctx);
|
|
124
|
+
|
|
125
|
+
const normalizeSelectAst = (ast: SelectQueryNode): SelectQueryNode =>
|
|
126
|
+
selectAstNormalizer.normalize(ast);
|
|
127
|
+
|
|
128
|
+
const compileSelectForExists = (ast: SelectQueryNode, ctx: CompilerContext): string => {
|
|
129
|
+
const normalized = normalizeSelectAst(ast);
|
|
130
|
+
const full = compileSelectAst(normalized, ctx).trim().replace(/;$/, '');
|
|
131
|
+
if (normalized.setOps && normalized.setOps.length > 0) {
|
|
132
|
+
return `SELECT 1 FROM (${full}) AS _exists`;
|
|
133
|
+
}
|
|
134
|
+
const fromIndex = full.toUpperCase().indexOf(' FROM ');
|
|
135
|
+
return fromIndex === -1 ? full : `SELECT 1${full.slice(fromIndex)}`;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const compileJsonPath = (node: JsonPathNode): string => {
|
|
139
|
+
if (!config.compileJsonPath) {
|
|
140
|
+
throw new Error(`JSON Path not supported by dialect "${config.name}".`);
|
|
141
|
+
}
|
|
142
|
+
return config.compileJsonPath(node);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const compileFunctionOperand = (node: FunctionNode, ctx: CompilerContext): string => {
|
|
146
|
+
const compiledArgs = node.args.map(arg => expressionRegistry.compileOperand(arg, ctx));
|
|
147
|
+
const renderer = functionStrategy.getRenderer(node.name);
|
|
148
|
+
if (renderer) {
|
|
149
|
+
return renderer({
|
|
150
|
+
node,
|
|
151
|
+
compiledArgs,
|
|
152
|
+
compileOperand: operand => expressionRegistry.compileOperand(operand, ctx)
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return `${node.name}(${compiledArgs.join(', ')})`;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
expressionRegistry = new ExpressionCompilerRegistry({
|
|
159
|
+
quoteIdentifier: config.quoteIdentifier,
|
|
160
|
+
compileSelectAst,
|
|
161
|
+
compileSelectForExists,
|
|
162
|
+
compileJsonPath,
|
|
163
|
+
compileFunctionOperand,
|
|
164
|
+
describe: () => config.describe ?? config.name
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const compileSetTarget = (column: ColumnNode, table: TableNode): string => {
|
|
168
|
+
if (config.compileSetTarget) return config.compileSetTarget(column, table);
|
|
169
|
+
const columnTable = column.table ?? table.alias ?? table.name;
|
|
170
|
+
const tableQualifier = table.alias && column.table === table.name
|
|
171
|
+
? table.alias
|
|
172
|
+
: columnTable;
|
|
173
|
+
return tableQualifier
|
|
174
|
+
? `${config.quoteIdentifier(tableQualifier)}.${config.quoteIdentifier(column.name)}`
|
|
175
|
+
: config.quoteIdentifier(column.name);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
let standardUpdate!: StandardUpdateCompiler;
|
|
179
|
+
const services: StandardSqlCompilerServices = {
|
|
180
|
+
getDialectName: () => config.name,
|
|
181
|
+
getPaginationStrategy: () => paginationStrategy,
|
|
182
|
+
getTableFunctionStrategy: () => tableFunctionStrategy,
|
|
183
|
+
quoteIdentifier: config.quoteIdentifier,
|
|
184
|
+
compileOperand: (node, ctx) => expressionRegistry.compileOperand(node, ctx),
|
|
185
|
+
compileExpression: (node, ctx) => expressionRegistry.compileExpression(node, ctx),
|
|
186
|
+
compileOrderingTerm: (term, ctx) => expressionRegistry.compileOrderingTerm(term, ctx),
|
|
187
|
+
normalizeSelectAst: normalizeSelectAst,
|
|
188
|
+
compileSelectAst,
|
|
189
|
+
compileReturning: (returning, ctx) =>
|
|
190
|
+
returningStrategy.compileReturning(returning, ctx, config.quoteIdentifier),
|
|
191
|
+
compileUpsertClause: (ast, ctx) =>
|
|
192
|
+
upsertStrategy.compile(ast, ctx, {
|
|
193
|
+
getDialectName: () => config.name,
|
|
194
|
+
quoteIdentifier: config.quoteIdentifier,
|
|
195
|
+
compileOperand: (node, compilerContext) =>
|
|
196
|
+
expressionRegistry.compileOperand(node, compilerContext),
|
|
197
|
+
compileExpression: (node, compilerContext) =>
|
|
198
|
+
expressionRegistry.compileExpression(node, compilerContext),
|
|
199
|
+
compileUpdateAssignments: (assignments, table, compilerContext) =>
|
|
200
|
+
standardUpdate.compileAssignments(assignments, table, compilerContext)
|
|
201
|
+
}),
|
|
202
|
+
compileSetTarget,
|
|
203
|
+
renderOrderByNulls: order =>
|
|
204
|
+
config.renderOrderByNulls?.(order) ?? (order.nulls ? ` NULLS ${order.nulls}` : ''),
|
|
205
|
+
renderOrderByCollation: order =>
|
|
206
|
+
config.renderOrderByCollation?.(order) ?? (order.collation ? ` COLLATE ${order.collation}` : '')
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const sources = new StandardSqlSourceCompiler(services);
|
|
210
|
+
const standardSelect = new StandardSelectCompiler(services, sources);
|
|
211
|
+
const standardInsert = new StandardInsertCompiler(services, sources);
|
|
212
|
+
standardUpdate = new StandardUpdateCompiler(services, sources);
|
|
213
|
+
const standardDelete = new StandardDeleteCompiler(services, sources);
|
|
214
|
+
const overrides = config.compilerFactory?.({ services, sources }) ?? {};
|
|
215
|
+
compilerSet = {
|
|
216
|
+
select: overrides.select ?? standardSelect,
|
|
217
|
+
insert: overrides.insert ?? standardInsert,
|
|
218
|
+
update: overrides.update ?? standardUpdate,
|
|
219
|
+
delete: overrides.delete ?? standardDelete
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const expressionApi: SqlDialectExpressionApi = {
|
|
223
|
+
registerExpressionCompiler<T extends ExpressionNode>(
|
|
224
|
+
type: T['type'],
|
|
225
|
+
compiler: (node: T, ctx: CompilerContext) => string
|
|
226
|
+
): void {
|
|
227
|
+
expressionRegistry.registerExpressionCompiler(type, compiler);
|
|
228
|
+
},
|
|
229
|
+
registerOperandCompiler<T extends OperandNode>(
|
|
230
|
+
type: T['type'],
|
|
231
|
+
compiler: (node: T, ctx: CompilerContext) => string
|
|
232
|
+
): void {
|
|
233
|
+
expressionRegistry.registerOperandCompiler(type, compiler);
|
|
234
|
+
},
|
|
235
|
+
compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
|
|
236
|
+
return expressionRegistry.compileExpression(node, ctx);
|
|
237
|
+
},
|
|
238
|
+
compileOperand(node: OperandNode, ctx: CompilerContext): string {
|
|
239
|
+
return expressionRegistry.compileOperand(node, ctx);
|
|
240
|
+
},
|
|
241
|
+
compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string {
|
|
242
|
+
return expressionRegistry.compileOrderingTerm(term, ctx);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
config.configureExpressions?.(expressionApi);
|
|
246
|
+
|
|
247
|
+
const dialect: Dialect = {
|
|
248
|
+
quoteIdentifier: config.quoteIdentifier,
|
|
249
|
+
supportsDmlReturningClause: () => config.supportsDmlReturning ?? false,
|
|
250
|
+
compileSelect(ast: SelectQueryNode) {
|
|
251
|
+
const ctx = createCompilerContext();
|
|
252
|
+
return {
|
|
253
|
+
sql: terminate(compileSelectAst(normalizeSelectAst(ast), ctx)),
|
|
254
|
+
params: [...ctx.params]
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
compileInsert(ast: InsertQueryNode) {
|
|
258
|
+
const ctx = createCompilerContext();
|
|
259
|
+
return {
|
|
260
|
+
sql: terminate(compilerSet.insert.compile(ast, ctx)),
|
|
261
|
+
params: [...ctx.params]
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
compileUpdate(ast: UpdateQueryNode) {
|
|
265
|
+
const ctx = createCompilerContext();
|
|
266
|
+
return {
|
|
267
|
+
sql: terminate(compilerSet.update.compile(ast, ctx)),
|
|
268
|
+
params: [...ctx.params]
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
compileDelete(ast: DeleteQueryNode) {
|
|
272
|
+
const ctx = createCompilerContext();
|
|
273
|
+
return {
|
|
274
|
+
sql: terminate(compilerSet.delete.compile(ast, ctx)),
|
|
275
|
+
params: [...ctx.params]
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const runtime: SqlDialectRuntimeServices = {
|
|
281
|
+
quoteIdentifier: config.quoteIdentifier,
|
|
282
|
+
createCompilerContext,
|
|
283
|
+
compileOperand: (node, ctx) => expressionRegistry.compileOperand(node, ctx),
|
|
284
|
+
compileExpression: (node, ctx) => expressionRegistry.compileExpression(node, ctx),
|
|
285
|
+
compileOrderingTerm: (term, ctx) => expressionRegistry.compileOrderingTerm(term, ctx),
|
|
286
|
+
normalizeSelectAst: normalizeSelectAst,
|
|
287
|
+
compileSelectAst
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return { dialect, runtime };
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
export const createSqlDialect = (config: SqlDialectConfig): Dialect =>
|
|
294
|
+
composeSqlDialect(config).dialect;
|
|
@@ -11,19 +11,15 @@ import type {
|
|
|
11
11
|
ExpressionNode,
|
|
12
12
|
OperandNode
|
|
13
13
|
} from '../../ast/expression.js';
|
|
14
|
-
import type { DialectName } from '../../sql/sql.js';
|
|
15
14
|
import type { PaginationStrategy } from './pagination-strategy.js';
|
|
16
15
|
import type { TableFunctionStrategy } from '../../functions/table-types.js';
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Narrow callback surface consumed by the standard SQL compilers.
|
|
20
|
-
*
|
|
21
|
-
* The compilers deliberately know nothing about SqlDialectBase or any concrete
|
|
22
|
-
* backend class. A dialect can assemble these services through inheritance,
|
|
23
|
-
* composition, or a plain object.
|
|
19
|
+
* It deliberately depends on no dialect superclass or built-in dialect union.
|
|
24
20
|
*/
|
|
25
21
|
export interface StandardSqlCompilerServices {
|
|
26
|
-
getDialectName():
|
|
22
|
+
getDialectName(): string;
|
|
27
23
|
getPaginationStrategy(): PaginationStrategy;
|
|
28
24
|
getTableFunctionStrategy(): TableFunctionStrategy;
|
|
29
25
|
|
|
@@ -5,11 +5,10 @@ import type {
|
|
|
5
5
|
UpdateAssignmentNode
|
|
6
6
|
} from '../../ast/query.js';
|
|
7
7
|
import type { ExpressionNode, OperandNode } from '../../ast/expression.js';
|
|
8
|
-
import type { DialectName } from '../../sql/sql.js';
|
|
9
8
|
|
|
10
9
|
/** Narrow services needed by backend-specific UPSERT implementations. */
|
|
11
10
|
export interface UpsertCompilationServices {
|
|
12
|
-
getDialectName():
|
|
11
|
+
getDialectName(): string;
|
|
13
12
|
quoteIdentifier(id: string): string;
|
|
14
13
|
compileOperand(node: OperandNode, ctx: CompilerContext): string;
|
|
15
14
|
compileExpression(node: ExpressionNode, ctx: CompilerContext): string;
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// Dialect factory for the SQL DSL.
|
|
2
|
-
// Centralizes how we go from a symbolic name ("sqlite") to a
|
|
2
|
+
// Centralizes how we go from a symbolic name ("sqlite") to a structural Dialect.
|
|
3
3
|
|
|
4
4
|
import type { Dialect } from './abstract.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { createPostgresDialect } from './postgres/index.js';
|
|
6
|
+
import { createMySqlDialect } from './mysql/index.js';
|
|
7
|
+
import { createSqliteDialect } from './sqlite/index.js';
|
|
8
|
+
import { createSqlServerDialect } from './mssql/index.js';
|
|
9
9
|
|
|
10
10
|
export type DialectKey =
|
|
11
11
|
| 'postgres'
|
|
12
12
|
| 'mysql'
|
|
13
13
|
| 'sqlite'
|
|
14
14
|
| 'mssql'
|
|
15
|
-
| (string & {});
|
|
15
|
+
| (string & {});
|
|
16
16
|
|
|
17
|
-
type DialectFactoryFn = () => Dialect;
|
|
17
|
+
export type DialectFactoryFn = () => Dialect;
|
|
18
18
|
|
|
19
19
|
export class DialectFactory {
|
|
20
20
|
private static registry = new Map<DialectKey, DialectFactoryFn>();
|
|
@@ -24,67 +24,35 @@ export class DialectFactory {
|
|
|
24
24
|
if (this.defaultsInitialized) return;
|
|
25
25
|
this.defaultsInitialized = true;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
if (!this.registry.has('
|
|
29
|
-
|
|
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
|
-
}
|
|
27
|
+
if (!this.registry.has('postgres')) this.registry.set('postgres', createPostgresDialect);
|
|
28
|
+
if (!this.registry.has('mysql')) this.registry.set('mysql', createMySqlDialect);
|
|
29
|
+
if (!this.registry.has('sqlite')) this.registry.set('sqlite', createSqliteDialect);
|
|
30
|
+
if (!this.registry.has('mssql')) this.registry.set('mssql', createSqlServerDialect);
|
|
40
31
|
}
|
|
41
32
|
|
|
42
|
-
/**
|
|
43
|
-
* Register (or override) a dialect factory for a key.
|
|
44
|
-
*
|
|
45
|
-
* Implementations are structural: extending DialectBase/SqlDialectBase is
|
|
46
|
-
* optional. A composed object satisfying Dialect is a valid registration.
|
|
47
|
-
*/
|
|
33
|
+
/** Register or replace a structural dialect factory. */
|
|
48
34
|
public static register(key: DialectKey, factory: DialectFactoryFn): void {
|
|
49
35
|
this.registry.set(key, factory);
|
|
50
36
|
}
|
|
51
37
|
|
|
52
|
-
/**
|
|
53
|
-
* Resolve a key into a Dialect instance.
|
|
54
|
-
* Throws if the key is not registered.
|
|
55
|
-
*/
|
|
38
|
+
/** Resolve a key into a new Dialect instance. */
|
|
56
39
|
public static create(key: DialectKey): Dialect {
|
|
57
40
|
this.ensureDefaults();
|
|
58
41
|
const factory = this.registry.get(key);
|
|
59
42
|
if (!factory) {
|
|
60
43
|
throw new Error(
|
|
61
|
-
`Dialect "${String(
|
|
62
|
-
key
|
|
63
|
-
)}" is not registered. Use DialectFactory.register(...) to register it.`
|
|
44
|
+
`Dialect "${String(key)}" is not registered. Use DialectFactory.register(...) to register it.`
|
|
64
45
|
);
|
|
65
46
|
}
|
|
66
47
|
return factory();
|
|
67
48
|
}
|
|
68
49
|
|
|
69
|
-
/**
|
|
70
|
-
* Clear all registrations (mainly for tests).
|
|
71
|
-
* Built-ins will be re-registered lazily on the next create().
|
|
72
|
-
*/
|
|
50
|
+
/** Clear registrations; built-ins are restored lazily on the next create(). */
|
|
73
51
|
public static clear(): void {
|
|
74
52
|
this.registry.clear();
|
|
75
53
|
this.defaultsInitialized = false;
|
|
76
54
|
}
|
|
77
55
|
}
|
|
78
56
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
* This is what query builders will use.
|
|
82
|
-
*/
|
|
83
|
-
export const resolveDialectInput = (
|
|
84
|
-
dialect: Dialect | DialectKey
|
|
85
|
-
): Dialect => {
|
|
86
|
-
if (typeof dialect === 'string') {
|
|
87
|
-
return DialectFactory.create(dialect);
|
|
88
|
-
}
|
|
89
|
-
return dialect;
|
|
90
|
-
};
|
|
57
|
+
export const resolveDialectInput = (dialect: Dialect | DialectKey): Dialect =>
|
|
58
|
+
typeof dialect === 'string' ? DialectFactory.create(dialect) : dialect;
|
|
@@ -1,46 +1,75 @@
|
|
|
1
1
|
import type { ProcedureCallNode } from '../../ast/procedure.js';
|
|
2
2
|
import type { JsonPathNode } from '../../ast/expression.js';
|
|
3
|
+
import type {
|
|
4
|
+
DeleteQueryNode,
|
|
5
|
+
InsertQueryNode,
|
|
6
|
+
SelectQueryNode,
|
|
7
|
+
UpdateQueryNode
|
|
8
|
+
} from '../../ast/query.js';
|
|
9
|
+
import type { CompiledQuery, Dialect } from '../abstract.js';
|
|
3
10
|
import type { CompiledProcedureCall, ProcedureCompiler } from '../capabilities/procedure-compiler.js';
|
|
4
|
-
import {
|
|
11
|
+
import { composeSqlDialect } from '../base/sql-dialect-composer.js';
|
|
5
12
|
import { MssqlFunctionStrategy } from './functions.js';
|
|
6
13
|
import { createMssqlCompilerSet } from './compiler-factory.js';
|
|
7
14
|
import { MssqlOutputStrategy } from './output.js';
|
|
8
15
|
import { MssqlProcedureCompiler } from './procedure-compiler.js';
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
17
|
+
const quoteIdentifier = (id: string): string => `[${id}]`;
|
|
18
|
+
|
|
19
|
+
export type SqlServerDialectImplementation = Dialect & ProcedureCompiler;
|
|
20
|
+
|
|
21
|
+
/** Creates the SQL Server dialect entirely from composable compiler components. */
|
|
22
|
+
export const createSqlServerDialect = (): SqlServerDialectImplementation => {
|
|
23
|
+
const composition = composeSqlDialect({
|
|
24
|
+
name: 'mssql',
|
|
25
|
+
quoteIdentifier,
|
|
26
|
+
formatPlaceholder: index => `@p${index}`,
|
|
27
|
+
functionStrategy: new MssqlFunctionStrategy(),
|
|
28
|
+
returningStrategy: new MssqlOutputStrategy(),
|
|
29
|
+
compilerFactory: createMssqlCompilerSet,
|
|
30
|
+
supportsDmlReturning: true,
|
|
31
|
+
compileJsonPath(node: JsonPathNode): string {
|
|
32
|
+
const column = `${quoteIdentifier(node.column.table)}.${quoteIdentifier(node.column.name)}`;
|
|
33
|
+
return `JSON_VALUE(${column}, '${node.path}')`;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const procedures = new MssqlProcedureCompiler(composition.runtime);
|
|
38
|
+
return {
|
|
39
|
+
...composition.dialect,
|
|
40
|
+
compileProcedureCall: ast => procedures.compileProcedureCall(ast)
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Ergonomic constructor facade over the composed SQL Server dialect. */
|
|
45
|
+
export class SqlServerDialect implements Dialect, ProcedureCompiler {
|
|
46
|
+
private readonly impl: SqlServerDialectImplementation = createSqlServerDialect();
|
|
29
47
|
|
|
30
48
|
quoteIdentifier(id: string): string {
|
|
31
|
-
return
|
|
49
|
+
return this.impl.quoteIdentifier(id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
supportsDmlReturningClause(): boolean {
|
|
53
|
+
return this.impl.supportsDmlReturningClause();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
compileSelect(ast: SelectQueryNode): CompiledQuery {
|
|
57
|
+
return this.impl.compileSelect(ast);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
compileInsert(ast: InsertQueryNode): CompiledQuery {
|
|
61
|
+
return this.impl.compileInsert(ast);
|
|
32
62
|
}
|
|
33
63
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return `JSON_VALUE(${column}, '${node.path}')`;
|
|
64
|
+
compileUpdate(ast: UpdateQueryNode): CompiledQuery {
|
|
65
|
+
return this.impl.compileUpdate(ast);
|
|
37
66
|
}
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
return
|
|
68
|
+
compileDelete(ast: DeleteQueryNode): CompiledQuery {
|
|
69
|
+
return this.impl.compileDelete(ast);
|
|
41
70
|
}
|
|
42
71
|
|
|
43
72
|
compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall {
|
|
44
|
-
return this.
|
|
73
|
+
return this.impl.compileProcedureCall(ast);
|
|
45
74
|
}
|
|
46
75
|
}
|
|
@@ -1,51 +1,84 @@
|
|
|
1
1
|
import type { ProcedureCallNode } from '../../ast/procedure.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { IsDistinctExpressionNode, JsonPathNode } from '../../ast/expression.js';
|
|
3
|
+
import type {
|
|
4
|
+
DeleteQueryNode,
|
|
5
|
+
InsertQueryNode,
|
|
6
|
+
SelectQueryNode,
|
|
7
|
+
UpdateQueryNode
|
|
8
|
+
} from '../../ast/query.js';
|
|
9
|
+
import type { CompiledQuery, Dialect } from '../abstract.js';
|
|
3
10
|
import type { CompiledProcedureCall, ProcedureCompiler } from '../capabilities/procedure-compiler.js';
|
|
4
|
-
import {
|
|
11
|
+
import { composeSqlDialect } from '../base/sql-dialect-composer.js';
|
|
5
12
|
import { MysqlFunctionStrategy } from './functions.js';
|
|
6
13
|
import { MySqlProcedureCompiler } from './procedure-compiler.js';
|
|
7
14
|
import { MySqlUpsertStrategy } from './upsert.js';
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
16
|
+
const quoteIdentifier = (id: string): string => `\`${id}\``;
|
|
17
|
+
|
|
18
|
+
export type MySqlDialectImplementation = Dialect & ProcedureCompiler;
|
|
19
|
+
|
|
20
|
+
/** Creates the MySQL dialect entirely from composable compiler components. */
|
|
21
|
+
export const createMySqlDialect = (): MySqlDialectImplementation => {
|
|
22
|
+
const composition = composeSqlDialect({
|
|
23
|
+
name: 'mysql',
|
|
24
|
+
quoteIdentifier,
|
|
25
|
+
functionStrategy: new MysqlFunctionStrategy(),
|
|
26
|
+
upsertStrategy: new MySqlUpsertStrategy(),
|
|
27
|
+
compileJsonPath(node: JsonPathNode): string {
|
|
28
|
+
const column = `${quoteIdentifier(node.column.table)}.${quoteIdentifier(node.column.name)}`;
|
|
29
|
+
return `${column}->'${node.path}'`;
|
|
30
|
+
},
|
|
31
|
+
configureExpressions(api) {
|
|
32
|
+
api.registerExpressionCompiler(
|
|
33
|
+
'IsDistinctExpression',
|
|
34
|
+
(node: IsDistinctExpressionNode, ctx): string => {
|
|
35
|
+
const left = api.compileOperand(node.left, ctx);
|
|
36
|
+
const right = api.compileOperand(node.right, ctx);
|
|
37
|
+
const spaceship = `${left} <=> ${right}`;
|
|
38
|
+
return node.operator === 'IS NOT DISTINCT FROM'
|
|
39
|
+
? spaceship
|
|
40
|
+
: `NOT (${spaceship})`;
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const procedures = new MySqlProcedureCompiler(composition.runtime);
|
|
47
|
+
return {
|
|
48
|
+
...composition.dialect,
|
|
49
|
+
compileProcedureCall: ast => procedures.compileProcedureCall(ast)
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Ergonomic constructor facade over the composed MySQL dialect. */
|
|
54
|
+
export class MySqlDialect implements Dialect, ProcedureCompiler {
|
|
55
|
+
private readonly impl: MySqlDialectImplementation = createMySqlDialect();
|
|
38
56
|
|
|
39
57
|
quoteIdentifier(id: string): string {
|
|
40
|
-
return
|
|
58
|
+
return this.impl.quoteIdentifier(id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
supportsDmlReturningClause(): boolean {
|
|
62
|
+
return this.impl.supportsDmlReturningClause();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
compileSelect(ast: SelectQueryNode): CompiledQuery {
|
|
66
|
+
return this.impl.compileSelect(ast);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
compileInsert(ast: InsertQueryNode): CompiledQuery {
|
|
70
|
+
return this.impl.compileInsert(ast);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
compileUpdate(ast: UpdateQueryNode): CompiledQuery {
|
|
74
|
+
return this.impl.compileUpdate(ast);
|
|
41
75
|
}
|
|
42
76
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return `${column}->'${node.path}'`;
|
|
77
|
+
compileDelete(ast: DeleteQueryNode): CompiledQuery {
|
|
78
|
+
return this.impl.compileDelete(ast);
|
|
46
79
|
}
|
|
47
80
|
|
|
48
81
|
compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall {
|
|
49
|
-
return this.
|
|
82
|
+
return this.impl.compileProcedureCall(ast);
|
|
50
83
|
}
|
|
51
84
|
}
|