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.
Files changed (32) hide show
  1. package/dist/index.cjs +1151 -510
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +281 -248
  4. package/dist/index.d.ts +281 -248
  5. package/dist/index.js +1133 -508
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/core/ddl/dialects/index.ts +5 -6
  9. package/src/core/ddl/dialects/mssql-schema-dialect.ts +129 -126
  10. package/src/core/ddl/dialects/mysql-schema-dialect.ts +119 -111
  11. package/src/core/ddl/dialects/postgres-schema-dialect.ts +173 -164
  12. package/src/core/ddl/dialects/render-reference.test.ts +37 -57
  13. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +110 -121
  14. package/src/core/ddl/schema-dialect-composer.ts +129 -0
  15. package/src/core/ddl/schema-dialect.ts +40 -27
  16. package/src/core/ddl/schema-diff.ts +119 -90
  17. package/src/core/dialect/abstract.ts +7 -229
  18. package/src/core/dialect/base/sql-dialect-composer.ts +294 -0
  19. package/src/core/dialect/base/standard-sql-services.ts +2 -6
  20. package/src/core/dialect/base/upsert-strategy.ts +1 -2
  21. package/src/core/dialect/dialect-factory.ts +17 -49
  22. package/src/core/dialect/mssql/index.ts +56 -27
  23. package/src/core/dialect/mysql/index.ts +69 -36
  24. package/src/core/dialect/postgres/index.ts +71 -43
  25. package/src/core/dialect/sqlite/index.ts +68 -38
  26. package/src/core/driver/mssql-driver.ts +6 -8
  27. package/src/core/driver/mysql-driver.ts +6 -8
  28. package/src/core/driver/postgres-driver.ts +6 -8
  29. package/src/core/driver/sqlite-driver.ts +6 -8
  30. package/src/index.ts +13 -1
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +0 -96
  32. 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(): DialectName;
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(): DialectName;
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 concrete Dialect instance.
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 { 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';
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 & {}); // allow user-defined keys without constraining too much
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
- // 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
- }
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
- * Helper to normalize either a Dialect instance OR a key into a Dialect instance.
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 { SqlDialectBase } from '../base/sql-dialect.js';
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
- /** Microsoft SQL Server dialect assembled from backend compiler components. */
11
- export class SqlServerDialect extends SqlDialectBase implements ProcedureCompiler {
12
- protected readonly dialect = 'mssql';
13
- private readonly procedureCompiler: MssqlProcedureCompiler;
14
-
15
- public constructor() {
16
- super({
17
- functionStrategy: new MssqlFunctionStrategy(),
18
- returningStrategy: new MssqlOutputStrategy(),
19
- compilerFactory: createMssqlCompilerSet,
20
- supportsDmlReturning: true
21
- });
22
-
23
- this.procedureCompiler = new MssqlProcedureCompiler({
24
- quoteIdentifier: id => this.quoteIdentifier(id),
25
- createCompilerContext: () => this.createCompilerContext(),
26
- compileOperand: (node, ctx) => this.compileOperand(node, ctx)
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 `[${id}]`;
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
- protected compileJsonPath(node: JsonPathNode): string {
35
- const column = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
36
- return `JSON_VALUE(${column}, '${node.path}')`;
64
+ compileUpdate(ast: UpdateQueryNode): CompiledQuery {
65
+ return this.impl.compileUpdate(ast);
37
66
  }
38
67
 
39
- protected formatPlaceholder(index: number): string {
40
- return `@p${index}`;
68
+ compileDelete(ast: DeleteQueryNode): CompiledQuery {
69
+ return this.impl.compileDelete(ast);
41
70
  }
42
71
 
43
72
  compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall {
44
- return this.procedureCompiler.compileProcedureCall(ast);
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 { JsonPathNode, IsDistinctExpressionNode } from '../../ast/expression.js';
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 { SqlDialectBase } from '../base/sql-dialect.js';
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
- /** MySQL dialect assembled from reusable compiler components. */
10
- export class MySqlDialect extends SqlDialectBase implements ProcedureCompiler {
11
- protected readonly dialect = 'mysql';
12
- private readonly procedureCompiler: MySqlProcedureCompiler;
13
-
14
- public constructor() {
15
- super({
16
- functionStrategy: new MysqlFunctionStrategy(),
17
- upsertStrategy: new MySqlUpsertStrategy()
18
- });
19
-
20
- this.procedureCompiler = new MySqlProcedureCompiler({
21
- quoteIdentifier: id => this.quoteIdentifier(id),
22
- createCompilerContext: () => this.createCompilerContext(),
23
- compileOperand: (node, ctx) => this.compileOperand(node, ctx)
24
- });
25
-
26
- this.registerExpressionCompiler(
27
- 'IsDistinctExpression',
28
- (node: IsDistinctExpressionNode, ctx): string => {
29
- const left = this.compileOperand(node.left, ctx);
30
- const right = this.compileOperand(node.right, ctx);
31
- const spaceship = `${left} <=> ${right}`;
32
- return node.operator === 'IS NOT DISTINCT FROM'
33
- ? spaceship
34
- : `NOT (${spaceship})`;
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 `\`${id}\``;
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
- protected compileJsonPath(node: JsonPathNode): string {
44
- const column = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
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.procedureCompiler.compileProcedureCall(ast);
82
+ return this.impl.compileProcedureCall(ast);
50
83
  }
51
84
  }