metal-orm 1.1.24 → 1.1.26

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 (35) hide show
  1. package/dist/index.cjs +770 -710
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +294 -318
  4. package/dist/index.d.ts +294 -318
  5. package/dist/index.js +745 -709
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/core/dialect/abstract.ts +7 -229
  9. package/src/core/dialect/base/returning-strategy.ts +40 -39
  10. package/src/core/dialect/base/sql-compiler-set.ts +33 -0
  11. package/src/core/dialect/base/sql-dialect-composer.ts +294 -0
  12. package/src/core/dialect/base/standard-sql-services.ts +2 -6
  13. package/src/core/dialect/base/upsert-strategy.ts +44 -0
  14. package/src/core/dialect/capabilities/procedure-compiler.ts +10 -8
  15. package/src/core/dialect/dialect-factory.ts +17 -49
  16. package/src/core/dialect/mssql/compiler-factory.ts +12 -0
  17. package/src/core/dialect/mssql/delete-compiler.ts +40 -0
  18. package/src/core/dialect/mssql/index.ts +52 -370
  19. package/src/core/dialect/mssql/insert-compiler.ts +112 -0
  20. package/src/core/dialect/mssql/output.ts +46 -0
  21. package/src/core/dialect/mssql/procedure-compiler.ts +81 -0
  22. package/src/core/dialect/mssql/select-compiler.ts +116 -0
  23. package/src/core/dialect/mssql/update-compiler.ts +37 -0
  24. package/src/core/dialect/mysql/index.ts +66 -126
  25. package/src/core/dialect/mysql/procedure-compiler.ts +67 -0
  26. package/src/core/dialect/mysql/upsert.ts +42 -0
  27. package/src/core/dialect/postgres/index.ts +75 -114
  28. package/src/core/dialect/postgres/procedure-compiler.ts +41 -0
  29. package/src/core/dialect/postgres/returning.ts +4 -0
  30. package/src/core/dialect/postgres/upsert.ts +43 -0
  31. package/src/core/dialect/sqlite/index.ts +65 -90
  32. package/src/core/dialect/sqlite/returning.ts +30 -0
  33. package/src/core/dialect/sqlite/upsert.ts +43 -0
  34. package/src/index.ts +22 -10
  35. package/src/core/dialect/base/sql-dialect.ts +0 -176
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,25 +1,9 @@
1
1
  import type {
2
- SelectQueryNode,
3
- InsertQueryNode,
4
- UpdateQueryNode,
5
2
  DeleteQueryNode,
6
- SetOperationKind,
7
- OrderingTerm
3
+ InsertQueryNode,
4
+ SelectQueryNode,
5
+ UpdateQueryNode
8
6
  } from '../ast/query.js';
9
- import type {
10
- ExpressionNode,
11
- ColumnNode,
12
- OperandNode,
13
- FunctionNode,
14
- JsonPathNode
15
- } from '../ast/expression.js';
16
- import type { DialectName } from '../sql/sql.js';
17
- import type { FunctionStrategy } from '../functions/types.js';
18
- import { StandardFunctionStrategy } from '../functions/standard-strategy.js';
19
- import type { TableFunctionStrategy } from '../functions/table-types.js';
20
- import { StandardTableFunctionStrategy } from '../functions/standard-table-strategy.js';
21
- import { ExpressionCompilerRegistry } from './base/expression-compiler-registry.js';
22
- import { SelectAstNormalizer } from './base/select-ast-normalizer.js';
23
7
 
24
8
  /** Context for SQL compilation with parameter management. */
25
9
  export interface CompilerContext {
@@ -50,219 +34,13 @@ export interface DeleteCompiler {
50
34
  }
51
35
 
52
36
  /**
53
- * Public dialect contract consumed by builders and the ORM runtime.
54
- * Optional backend features such as stored procedures live in dedicated
55
- * capability interfaces; mutation-wide behavior shared by the runtime stays
56
- * in this small core contract.
37
+ * Structural contract consumed by query builders and the ORM runtime.
38
+ *
39
+ * A dialect is assembled from compiler components. There is intentionally no
40
+ * base class: inheritance is not part of the extension model.
57
41
  */
58
42
  export interface Dialect
59
43
  extends SelectCompiler, InsertCompiler, UpdateCompiler, DeleteCompiler {
60
44
  quoteIdentifier(id: string): string;
61
45
  supportsDmlReturningClause(): boolean;
62
46
  }
63
-
64
- /**
65
- * Shared implementation infrastructure for SQL dialects.
66
- *
67
- * This is deliberately separate from the public Dialect contract: custom
68
- * dialects may extend this class, extend SqlDialectBase, or use composition.
69
- */
70
- export abstract class DialectBase implements Dialect {
71
- protected abstract readonly dialect: DialectName;
72
-
73
- private readonly expressionCompilerRegistry: ExpressionCompilerRegistry;
74
- private readonly selectAstNormalizer: SelectAstNormalizer;
75
- protected readonly functionStrategy: FunctionStrategy;
76
- protected readonly tableFunctionStrategy: TableFunctionStrategy;
77
-
78
- protected constructor(
79
- functionStrategy?: FunctionStrategy,
80
- tableFunctionStrategy?: TableFunctionStrategy
81
- ) {
82
- this.functionStrategy = functionStrategy ?? new StandardFunctionStrategy();
83
- this.tableFunctionStrategy = tableFunctionStrategy ?? new StandardTableFunctionStrategy();
84
- this.selectAstNormalizer = new SelectAstNormalizer(kind => this.supportsSetOperation(kind));
85
- this.expressionCompilerRegistry = new ExpressionCompilerRegistry({
86
- quoteIdentifier: id => this.quoteIdentifier(id),
87
- compileSelectAst: (ast, ctx) => this.compileSelectAst(ast, ctx),
88
- compileSelectForExists: (ast, ctx) => this.compileSelectForExists(ast, ctx),
89
- compileJsonPath: node => this.compileJsonPath(node),
90
- compileFunctionOperand: (node, ctx) => this.compileFunctionOperand(node, ctx),
91
- describe: () => this.constructor.name
92
- });
93
- }
94
-
95
- compileSelect(ast: SelectQueryNode): CompiledQuery {
96
- const ctx = this.createCompilerContext();
97
- const normalized = this.normalizeSelectAst(ast);
98
- const rawSql = this.compileSelectAst(normalized, ctx).trim();
99
- return {
100
- sql: rawSql.endsWith(';') ? rawSql : `${rawSql};`,
101
- params: [...ctx.params]
102
- };
103
- }
104
-
105
- compileInsert(ast: InsertQueryNode): CompiledQuery {
106
- const ctx = this.createCompilerContext();
107
- const rawSql = this.compileInsertAst(ast, ctx).trim();
108
- return {
109
- sql: rawSql.endsWith(';') ? rawSql : `${rawSql};`,
110
- params: [...ctx.params]
111
- };
112
- }
113
-
114
- compileUpdate(ast: UpdateQueryNode): CompiledQuery {
115
- const ctx = this.createCompilerContext();
116
- const rawSql = this.compileUpdateAst(ast, ctx).trim();
117
- return {
118
- sql: rawSql.endsWith(';') ? rawSql : `${rawSql};`,
119
- params: [...ctx.params]
120
- };
121
- }
122
-
123
- compileDelete(ast: DeleteQueryNode): CompiledQuery {
124
- const ctx = this.createCompilerContext();
125
- const rawSql = this.compileDeleteAst(ast, ctx).trim();
126
- return {
127
- sql: rawSql.endsWith(';') ? rawSql : `${rawSql};`,
128
- params: [...ctx.params]
129
- };
130
- }
131
-
132
- supportsDmlReturningClause(): boolean {
133
- return false;
134
- }
135
-
136
- protected abstract compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
137
- protected abstract compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string;
138
- protected abstract compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string;
139
- protected abstract compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string;
140
-
141
- abstract quoteIdentifier(id: string): string;
142
-
143
- protected compileWhere(where: ExpressionNode | undefined, ctx: CompilerContext): string {
144
- if (!where) return '';
145
- return ` WHERE ${this.compileExpression(where, ctx)}`;
146
- }
147
-
148
- protected compileReturning(
149
- returning: ColumnNode[] | undefined,
150
- _ctx: CompilerContext
151
- ): string {
152
- void _ctx;
153
- if (!returning || returning.length === 0) return '';
154
- throw new Error('RETURNING is not supported by this dialect.');
155
- }
156
-
157
- protected compileSelectForExists(ast: SelectQueryNode, ctx: CompilerContext): string {
158
- const normalized = this.normalizeSelectAst(ast);
159
- const full = this.compileSelectAst(normalized, ctx).trim().replace(/;$/, '');
160
-
161
- if (normalized.setOps && normalized.setOps.length > 0) {
162
- return `SELECT 1 FROM (${full}) AS _exists`;
163
- }
164
-
165
- const upper = full.toUpperCase();
166
- const fromIndex = upper.indexOf(' FROM ');
167
- if (fromIndex === -1) return full;
168
-
169
- return `SELECT 1${full.slice(fromIndex)}`;
170
- }
171
-
172
- protected createCompilerContext(): CompilerContext {
173
- const params: unknown[] = [];
174
- let counter = 0;
175
- return {
176
- params,
177
- addParameter: (value: unknown) => {
178
- counter += 1;
179
- params.push(value);
180
- return this.formatPlaceholder(counter);
181
- }
182
- };
183
- }
184
-
185
- protected formatPlaceholder(_index: number): string {
186
- void _index;
187
- return '?';
188
- }
189
-
190
- protected supportsSetOperation(_kind: SetOperationKind): boolean {
191
- void _kind;
192
- return true;
193
- }
194
-
195
- protected normalizeSelectAst(ast: SelectQueryNode): SelectQueryNode {
196
- return this.selectAstNormalizer.normalize(ast);
197
- }
198
-
199
- protected registerExpressionCompiler<T extends ExpressionNode>(
200
- type: T['type'],
201
- compiler: (node: T, ctx: CompilerContext) => string
202
- ): void {
203
- this.expressionCompilerRegistry.registerExpressionCompiler(type, compiler);
204
- }
205
-
206
- protected registerOperandCompiler<T extends OperandNode>(
207
- type: T['type'],
208
- compiler: (node: T, ctx: CompilerContext) => string
209
- ): void {
210
- this.expressionCompilerRegistry.registerOperandCompiler(type, compiler);
211
- }
212
-
213
- protected compileExpression(node: ExpressionNode, ctx: CompilerContext): string {
214
- return this.expressionCompilerRegistry.compileExpression(node, ctx);
215
- }
216
-
217
- protected compileOperand(node: OperandNode, ctx: CompilerContext): string {
218
- return this.expressionCompilerRegistry.compileOperand(node, ctx);
219
- }
220
-
221
- protected compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string {
222
- return this.expressionCompilerRegistry.compileOrderingTerm(term, ctx);
223
- }
224
-
225
- protected compileJsonPath(_node: JsonPathNode): string {
226
- void _node;
227
- throw new Error('JSON Path not supported by this dialect');
228
- }
229
-
230
- protected compileFunctionOperand(fnNode: FunctionNode, ctx: CompilerContext): string {
231
- const compiledArgs = fnNode.args.map(arg => this.compileOperand(arg, ctx));
232
- const renderer = this.functionStrategy.getRenderer(fnNode.name);
233
- if (renderer) {
234
- return renderer({
235
- node: fnNode,
236
- compiledArgs,
237
- compileOperand: operand => this.compileOperand(operand, ctx)
238
- });
239
- }
240
- return `${fnNode.name}(${compiledArgs.join(', ')})`;
241
- }
242
-
243
- /** Creates a minimal dialect implementation for isolated compiler tests. */
244
- static create(
245
- functionStrategy?: FunctionStrategy,
246
- tableFunctionStrategy?: TableFunctionStrategy
247
- ): Dialect {
248
- class TestDialect extends DialectBase {
249
- protected readonly dialect: DialectName = 'sqlite';
250
- quoteIdentifier(id: string): string {
251
- return `"${id}"`;
252
- }
253
- protected compileSelectAst(): never {
254
- throw new Error('Not implemented');
255
- }
256
- protected compileInsertAst(): never {
257
- throw new Error('Not implemented');
258
- }
259
- protected compileUpdateAst(): never {
260
- throw new Error('Not implemented');
261
- }
262
- protected compileDeleteAst(): never {
263
- throw new Error('Not implemented');
264
- }
265
- }
266
- return new TestDialect(functionStrategy, tableFunctionStrategy);
267
- }
268
- }
@@ -1,51 +1,39 @@
1
- import { ColumnNode } from '../../ast/expression.js';
2
- import { CompilerContext } from '../abstract.js';
1
+ import type { ColumnNode } from '../../ast/expression.js';
2
+ import type { CompilerContext } from '../abstract.js';
3
3
 
4
- /**
5
- * Strategy interface for handling RETURNING clauses in DML statements (INSERT, UPDATE, DELETE).
6
- * Different SQL dialects have varying levels of support for RETURNING clauses.
7
- */
4
+ export type QuoteIdentifier = (id: string) => string;
5
+
6
+ /** Backend-specific RETURNING/OUTPUT rendering strategy. */
8
7
  export interface ReturningStrategy {
9
- /**
10
- * Compiles a RETURNING clause for DML statements.
11
- * @param returning - Array of columns to return, or undefined if none.
12
- * @param ctx - The compiler context for expression compilation.
13
- * @returns SQL RETURNING clause or empty string if not supported.
14
- * @throws Error if RETURNING is not supported by this dialect.
15
- */
16
- compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string;
17
- /**
18
- * Formats column list for RETURNING clause.
19
- * @param returning - Array of columns to format.
20
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
21
- * @returns Formatted column list (e.g., "table.col1, table.col2").
22
- */
23
- formatReturningColumns(returning: ColumnNode[], quoteIdentifier: (id: string) => string): string;
8
+ compileReturning(
9
+ returning: ColumnNode[] | undefined,
10
+ ctx: CompilerContext,
11
+ quoteIdentifier: QuoteIdentifier
12
+ ): string;
13
+
14
+ formatReturningColumns(
15
+ returning: ColumnNode[],
16
+ quoteIdentifier: QuoteIdentifier
17
+ ): string;
24
18
  }
25
19
 
26
- /**
27
- * Default RETURNING strategy that throws an error when RETURNING is used.
28
- * Use this for dialects that don't support RETURNING clauses.
29
- */
20
+ /** Default RETURNING strategy for dialects without support. */
30
21
  export class NoReturningStrategy implements ReturningStrategy {
31
- /**
32
- * Throws an error as RETURNING is not supported.
33
- * @param returning - Columns to return (causes error if non-empty).
34
- * @param _ctx - Compiler context (unused).
35
- * @throws Error indicating RETURNING is not supported.
36
- */
37
- compileReturning(returning: ColumnNode[] | undefined, _ctx: CompilerContext): string {
22
+ compileReturning(
23
+ returning: ColumnNode[] | undefined,
24
+ _ctx: CompilerContext,
25
+ _quoteIdentifier: QuoteIdentifier
26
+ ): string {
38
27
  void _ctx;
28
+ void _quoteIdentifier;
39
29
  if (!returning || returning.length === 0) return '';
40
30
  throw new Error('RETURNING is not supported by this dialect.');
41
31
  }
42
- /**
43
- * Formats column names for RETURNING clause.
44
- * @param returning - Columns to format.
45
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
46
- * @returns Simple comma-separated column names.
47
- */
48
- formatReturningColumns(returning: ColumnNode[], quoteIdentifier: (id: string) => string): string {
32
+
33
+ formatReturningColumns(
34
+ returning: ColumnNode[],
35
+ quoteIdentifier: QuoteIdentifier
36
+ ): string {
49
37
  return returning
50
38
  .map(column => {
51
39
  const tablePart = column.table ? `${quoteIdentifier(column.table)}.` : '';
@@ -55,3 +43,16 @@ export class NoReturningStrategy implements ReturningStrategy {
55
43
  .join(', ');
56
44
  }
57
45
  }
46
+
47
+ /** Standard SQL RETURNING implementation with qualified column support. */
48
+ export class StandardReturningStrategy extends NoReturningStrategy {
49
+ override compileReturning(
50
+ returning: ColumnNode[] | undefined,
51
+ _ctx: CompilerContext,
52
+ quoteIdentifier: QuoteIdentifier
53
+ ): string {
54
+ void _ctx;
55
+ if (!returning || returning.length === 0) return '';
56
+ return ` RETURNING ${this.formatReturningColumns(returning, quoteIdentifier)}`;
57
+ }
58
+ }
@@ -0,0 +1,33 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type {
3
+ DeleteQueryNode,
4
+ InsertQueryNode,
5
+ SelectQueryNode,
6
+ UpdateQueryNode
7
+ } from '../../ast/query.js';
8
+ import type { StandardSqlCompilerServices } from './standard-sql-services.js';
9
+ import type { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
10
+
11
+ export interface SqlAstCompiler<TAst> {
12
+ compile(ast: TAst, ctx: CompilerContext): string;
13
+ }
14
+
15
+ export interface SqlCompilerSet {
16
+ select: SqlAstCompiler<SelectQueryNode>;
17
+ insert: SqlAstCompiler<InsertQueryNode>;
18
+ update: SqlAstCompiler<UpdateQueryNode>;
19
+ delete: SqlAstCompiler<DeleteQueryNode>;
20
+ }
21
+
22
+ export interface SqlCompilerAssemblyContext {
23
+ services: StandardSqlCompilerServices;
24
+ sources: StandardSqlSourceCompiler;
25
+ }
26
+
27
+ /**
28
+ * Allows a backend to replace only the standard query compilers whose SQL
29
+ * grammar genuinely differs from the common implementation.
30
+ */
31
+ export type SqlCompilerFactory = (
32
+ context: SqlCompilerAssemblyContext
33
+ ) => Partial<SqlCompilerSet>;
@@ -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;