metal-orm 1.0.39 → 1.0.41

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 (52) hide show
  1. package/dist/index.cjs +1466 -189
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +723 -51
  4. package/dist/index.d.ts +723 -51
  5. package/dist/index.js +1457 -189
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/codegen/typescript.ts +66 -5
  9. package/src/core/ast/aggregate-functions.ts +15 -15
  10. package/src/core/ast/expression-builders.ts +378 -316
  11. package/src/core/ast/expression-nodes.ts +210 -186
  12. package/src/core/ast/expression-visitor.ts +40 -30
  13. package/src/core/ast/query.ts +164 -132
  14. package/src/core/ast/window-functions.ts +86 -86
  15. package/src/core/dialect/abstract.ts +509 -479
  16. package/src/core/dialect/base/groupby-compiler.ts +6 -6
  17. package/src/core/dialect/base/join-compiler.ts +9 -12
  18. package/src/core/dialect/base/orderby-compiler.ts +20 -6
  19. package/src/core/dialect/base/sql-dialect.ts +237 -138
  20. package/src/core/dialect/mssql/index.ts +164 -185
  21. package/src/core/dialect/sqlite/index.ts +39 -34
  22. package/src/core/execution/db-executor.ts +46 -6
  23. package/src/core/execution/executors/mssql-executor.ts +39 -22
  24. package/src/core/execution/executors/mysql-executor.ts +23 -6
  25. package/src/core/execution/executors/sqlite-executor.ts +29 -3
  26. package/src/core/execution/pooling/pool-types.ts +30 -0
  27. package/src/core/execution/pooling/pool.ts +268 -0
  28. package/src/core/functions/standard-strategy.ts +46 -37
  29. package/src/decorators/bootstrap.ts +7 -7
  30. package/src/index.ts +6 -0
  31. package/src/orm/domain-event-bus.ts +49 -0
  32. package/src/orm/entity-metadata.ts +9 -9
  33. package/src/orm/entity.ts +58 -0
  34. package/src/orm/orm-session.ts +465 -270
  35. package/src/orm/orm.ts +61 -11
  36. package/src/orm/pooled-executor-factory.ts +131 -0
  37. package/src/orm/query-logger.ts +6 -12
  38. package/src/orm/relation-change-processor.ts +75 -0
  39. package/src/orm/relations/many-to-many.ts +4 -2
  40. package/src/orm/save-graph.ts +303 -0
  41. package/src/orm/transaction-runner.ts +3 -3
  42. package/src/orm/unit-of-work.ts +128 -0
  43. package/src/query-builder/delete-query-state.ts +67 -38
  44. package/src/query-builder/delete.ts +37 -1
  45. package/src/query-builder/hydration-manager.ts +93 -79
  46. package/src/query-builder/insert-query-state.ts +131 -61
  47. package/src/query-builder/insert.ts +27 -1
  48. package/src/query-builder/query-ast-service.ts +207 -170
  49. package/src/query-builder/select-query-state.ts +169 -162
  50. package/src/query-builder/select.ts +15 -23
  51. package/src/query-builder/update-query-state.ts +114 -77
  52. package/src/query-builder/update.ts +38 -1
@@ -1,4 +1,6 @@
1
- import { SelectQueryNode } from '../../ast/query.js';
1
+ import { OrderingTerm, SelectQueryNode } from '../../ast/query.js';
2
+
3
+ type TermRenderer = (term: OrderingTerm) => string;
2
4
 
3
5
  /**
4
6
  * Compiler for GROUP BY clauses in SELECT statements.
@@ -8,14 +10,12 @@ export class GroupByCompiler {
8
10
  /**
9
11
  * Compiles GROUP BY clause from a SELECT query AST.
10
12
  * @param ast - The SELECT query AST containing grouping columns.
11
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
13
+ * @param renderTerm - Function to render a grouping term.
12
14
  * @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
13
15
  */
14
- static compileGroupBy(ast: SelectQueryNode, quoteIdentifier: (id: string) => string): string {
16
+ static compileGroupBy(ast: SelectQueryNode, renderTerm: TermRenderer): string {
15
17
  if (!ast.groupBy || ast.groupBy.length === 0) return '';
16
- const cols = ast.groupBy
17
- .map(c => `${quoteIdentifier(c.table)}.${quoteIdentifier(c.name)}`)
18
- .join(', ');
18
+ const cols = ast.groupBy.map(renderTerm).join(', ');
19
19
  return ` GROUP BY ${cols}`;
20
20
  }
21
21
  }
@@ -1,22 +1,19 @@
1
- import { SelectQueryNode } from '../../ast/query.js';
2
1
  import { CompilerContext } from '../abstract.js';
2
+ import { JoinNode } from '../../ast/join.js';
3
3
 
4
4
  /**
5
5
  * Compiler for JOIN clauses in SELECT statements.
6
6
  * Handles compilation of all join types (INNER, LEFT, RIGHT, FULL, CROSS).
7
7
  */
8
8
  export class JoinCompiler {
9
- /**
10
- * Compiles all JOIN clauses from a SELECT query AST.
11
- * @param ast - The SELECT query AST containing join definitions.
12
- * @param ctx - The compiler context for expression compilation.
13
- * @param compileFrom - Function to compile table sources (tables or subqueries).
14
- * @param compileExpression - Function to compile join condition expressions.
15
- * @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
16
- */
17
- static compileJoins(ast: SelectQueryNode, ctx: CompilerContext, compileFrom: (from: any, ctx: CompilerContext) => string, compileExpression: (expr: any, ctx: CompilerContext) => string): string {
18
- if (!ast.joins || ast.joins.length === 0) return '';
19
- const parts = ast.joins.map(j => {
9
+ static compileJoins(
10
+ joins: JoinNode[] | undefined,
11
+ ctx: CompilerContext,
12
+ compileFrom: (from: any, ctx: CompilerContext) => string,
13
+ compileExpression: (expr: any, ctx: CompilerContext) => string
14
+ ): string {
15
+ if (!joins || joins.length === 0) return '';
16
+ const parts = joins.map(j => {
20
17
  const table = compileFrom(j.table as any, ctx);
21
18
  const cond = compileExpression(j.condition, ctx);
22
19
  return `${j.kind} JOIN ${table} ON ${cond}`;
@@ -1,4 +1,8 @@
1
- import { SelectQueryNode } from '../../ast/query.js';
1
+ import { OrderByNode, SelectQueryNode } from '../../ast/query.js';
2
+
3
+ type NullsRenderer = (order: OrderByNode) => string | undefined;
4
+ type CollationRenderer = (order: OrderByNode) => string | undefined;
5
+ type TermRenderer = (term: OrderByNode['term']) => string;
2
6
 
3
7
  /**
4
8
  * Compiler for ORDER BY clauses in SELECT statements.
@@ -8,14 +12,24 @@ export class OrderByCompiler {
8
12
  /**
9
13
  * Compiles ORDER BY clause from a SELECT query AST.
10
14
  * @param ast - The SELECT query AST containing sort specifications.
11
- * @param quoteIdentifier - Function to quote identifiers according to dialect rules.
15
+ * @param renderTerm - Function to render an ordering term.
16
+ * @param renderNulls - Optional function to render NULLS FIRST/LAST.
17
+ * @param renderCollation - Optional function to render COLLATE clause.
12
18
  * @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
13
19
  */
14
- static compileOrderBy(ast: SelectQueryNode, quoteIdentifier: (id: string) => string): string {
20
+ static compileOrderBy(
21
+ ast: SelectQueryNode,
22
+ renderTerm: TermRenderer,
23
+ renderNulls?: NullsRenderer,
24
+ renderCollation?: CollationRenderer
25
+ ): string {
15
26
  if (!ast.orderBy || ast.orderBy.length === 0) return '';
16
- const parts = ast.orderBy
17
- .map(o => `${quoteIdentifier(o.column.table)}.${quoteIdentifier(o.column.name)} ${o.direction}`)
18
- .join(', ');
27
+ const parts = ast.orderBy.map(o => {
28
+ const term = renderTerm(o.term);
29
+ const collation = renderCollation ? renderCollation(o) : o.collation ? ` COLLATE ${o.collation}` : '';
30
+ const nulls = renderNulls ? renderNulls(o) : o.nulls ? ` NULLS ${o.nulls}` : '';
31
+ return `${term} ${o.direction}${collation}${nulls}`;
32
+ }).join(', ');
19
33
  return ` ORDER BY ${parts}`;
20
34
  }
21
35
  }
@@ -1,138 +1,194 @@
1
- import { CompilerContext, Dialect } from '../abstract.js';
2
- import { SelectQueryNode, InsertQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode, DerivedTableNode, FunctionTableNode } from '../../ast/query.js';
3
- import { ColumnNode } from '../../ast/expression.js';
1
+ import { CompilerContext, Dialect } from '../abstract.js';
2
+ import {
3
+ SelectQueryNode,
4
+ InsertQueryNode,
5
+ UpdateQueryNode,
6
+ DeleteQueryNode,
7
+ InsertSourceNode,
8
+ TableSourceNode,
9
+ DerivedTableNode,
10
+ FunctionTableNode,
11
+ OrderByNode,
12
+ TableNode
13
+ } from '../../ast/query.js';
14
+ import { ColumnNode } from '../../ast/expression.js';
4
15
  import { FunctionTableFormatter } 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
-
16
+ import { PaginationStrategy, StandardLimitOffsetPagination } from './pagination-strategy.js';
17
+ import { CteCompiler } from './cte-compiler.js';
18
+ import { ReturningStrategy, NoReturningStrategy } from './returning-strategy.js';
19
+ import { JoinCompiler } from './join-compiler.js';
20
+ import { GroupByCompiler } from './groupby-compiler.js';
21
+ import { OrderByCompiler } from './orderby-compiler.js';
22
+
23
+
24
+ export abstract class SqlDialectBase extends Dialect {
25
+ abstract quoteIdentifier(id: string): string;
26
+
27
+ protected paginationStrategy: PaginationStrategy = new StandardLimitOffsetPagination();
28
+ protected returningStrategy: ReturningStrategy = new NoReturningStrategy();
29
+
30
+ protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
31
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
32
+ const ctes = CteCompiler.compileCtes(
33
+ ast,
34
+ ctx,
35
+ this.quoteIdentifier.bind(this),
36
+ this.compileSelectAst.bind(this),
37
+ this.normalizeSelectAst?.bind(this) ?? ((a) => a),
38
+ this.stripTrailingSemicolon.bind(this)
39
+ );
40
+ const baseAst: SelectQueryNode = hasSetOps
41
+ ? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
42
+ : ast;
43
+ const baseSelect = this.compileSelectCore(baseAst, ctx);
44
+ if (!hasSetOps) {
45
+ return `${ctes}${baseSelect}`;
46
+ }
47
+ return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
48
+ }
49
+
50
+ private compileSelectWithSetOps(
51
+ ast: SelectQueryNode,
52
+ baseSelect: string,
53
+ ctes: string,
54
+ ctx: CompilerContext
55
+ ): string {
56
+ const compound = ast.setOps!
57
+ .map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
58
+ .join(' ');
59
+ const orderBy = OrderByCompiler.compileOrderBy(
60
+ ast,
61
+ term => this.compileOrderingTerm(term, ctx),
62
+ this.renderOrderByNulls.bind(this),
63
+ this.renderOrderByCollation.bind(this)
64
+ );
65
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
66
+ const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
67
+ return `${ctes}${combined}${orderBy}${pagination}`;
68
+ }
69
+
70
+ protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
71
+ if (!ast.columns.length) {
72
+ throw new Error('INSERT queries must specify columns.');
73
+ }
74
+
75
+ const table = this.compileTableName(ast.into);
76
+ const columnList = this.compileInsertColumnList(ast.columns);
77
+ const source = this.compileInsertSource(ast.source, ctx);
78
+ const returning = this.compileReturning(ast.returning, ctx);
79
+ return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
80
+ }
81
+
82
+ protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
83
+ return this.returningStrategy.compileReturning(returning, ctx);
84
+ }
85
+
86
+ private compileInsertSource(source: InsertSourceNode, ctx: CompilerContext): string {
87
+ if (source.type === 'InsertValues') {
88
+ if (!source.rows.length) {
89
+ throw new Error('INSERT ... VALUES requires at least one row.');
90
+ }
91
+ const values = source.rows
92
+ .map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`)
93
+ .join(', ');
94
+ return `VALUES ${values}`;
95
+ }
96
+
97
+ const normalized = this.normalizeSelectAst(source.query);
98
+ return this.compileSelectAst(normalized, ctx).trim();
99
+ }
100
+
66
101
  private compileInsertColumnList(columns: ColumnNode[]): string {
67
102
  return columns.map(column => this.quoteIdentifier(column.name)).join(', ');
68
103
  }
69
-
70
- private compileInsertValues(values: any[][], ctx: CompilerContext): string {
71
- return values
72
- .map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`)
73
- .join(', ');
74
- }
75
-
76
- private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
77
- const columns = this.compileSelectColumns(ast, ctx);
78
- const from = this.compileFrom(ast.from, ctx);
79
- const joins = JoinCompiler.compileJoins(ast, ctx, this.compileFrom.bind(this), this.compileExpression.bind(this));
80
- const whereClause = this.compileWhere(ast.where, ctx);
81
- const groupBy = GroupByCompiler.compileGroupBy(ast, this.quoteIdentifier.bind(this));
82
- const having = this.compileHaving(ast, ctx);
83
- const orderBy = OrderByCompiler.compileOrderBy(ast, this.quoteIdentifier.bind(this));
84
- const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
85
- return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
86
- }
87
-
88
- protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
89
- const table = this.compileTableName(ast.table);
90
- const assignments = this.compileUpdateAssignments(ast.set, ctx);
91
- const whereClause = this.compileWhere(ast.where, ctx);
92
- const returning = this.compileReturning(ast.returning, ctx);
93
- return `UPDATE ${table} SET ${assignments}${whereClause}${returning}`;
94
- }
95
-
96
- private compileUpdateAssignments(
97
- assignments: { column: ColumnNode; value: any }[],
98
- ctx: CompilerContext
99
- ): string {
104
+
105
+ private compileSelectCore(ast: SelectQueryNode, ctx: CompilerContext): string {
106
+ const columns = this.compileSelectColumns(ast, ctx);
107
+ const from = this.compileFrom(ast.from, ctx);
108
+ const joins = JoinCompiler.compileJoins(
109
+ ast.joins,
110
+ ctx,
111
+ this.compileFrom.bind(this),
112
+ this.compileExpression.bind(this)
113
+ );
114
+ const whereClause = this.compileWhere(ast.where, ctx);
115
+ const groupBy = GroupByCompiler.compileGroupBy(ast, term => this.compileOrderingTerm(term, ctx));
116
+ const having = this.compileHaving(ast, ctx);
117
+ const orderBy = OrderByCompiler.compileOrderBy(
118
+ ast,
119
+ term => this.compileOrderingTerm(term, ctx),
120
+ this.renderOrderByNulls.bind(this),
121
+ this.renderOrderByCollation.bind(this)
122
+ );
123
+ const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
124
+ return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
125
+ }
126
+
127
+ protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
128
+ const target = this.compileTableReference(ast.table);
129
+ const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
130
+ const fromClause = this.compileUpdateFromClause(ast, ctx);
131
+ const whereClause = this.compileWhere(ast.where, ctx);
132
+ const returning = this.compileReturning(ast.returning, ctx);
133
+ return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
134
+ }
135
+
136
+ private compileUpdateAssignments(
137
+ assignments: { column: ColumnNode; value: any }[],
138
+ table: TableNode,
139
+ ctx: CompilerContext
140
+ ): string {
100
141
  return assignments
101
142
  .map(assignment => {
102
143
  const col = assignment.column;
103
- const target = this.quoteIdentifier(col.name);
144
+ const target = this.compileQualifiedColumn(col, table);
104
145
  const value = this.compileOperand(assignment.value, ctx);
105
146
  return `${target} = ${value}`;
106
147
  })
107
148
  .join(', ');
108
149
  }
109
-
110
- protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
111
- const table = this.compileTableName(ast.from);
112
- const whereClause = this.compileWhere(ast.where, ctx);
113
- const returning = this.compileReturning(ast.returning, ctx);
114
- return `DELETE FROM ${table}${whereClause}${returning}`;
115
- }
116
-
117
- protected formatReturningColumns(returning: ColumnNode[]): string {
118
- return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
119
- }
120
-
121
- protected compileDistinct(ast: SelectQueryNode): string {
122
- return ast.distinct ? 'DISTINCT ' : '';
123
- }
124
-
125
- protected compileSelectColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
126
- return ast.columns.map(c => {
127
- const expr = this.compileOperand(c, ctx);
128
- if (c.alias) {
129
- if (c.alias.includes('(')) return c.alias;
130
- return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
131
- }
132
- return expr;
133
- }).join(', ');
134
- }
135
-
150
+
151
+ protected compileQualifiedColumn(column: ColumnNode, table: TableNode): string {
152
+ const baseTableName = table.name;
153
+ const alias = table.alias;
154
+ const columnTable = column.table ?? alias ?? baseTableName;
155
+ const tableQualifier =
156
+ alias && column.table === baseTableName ? alias : columnTable;
157
+
158
+ if (!tableQualifier) {
159
+ return this.quoteIdentifier(column.name);
160
+ }
161
+
162
+ return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
163
+ }
164
+
165
+ protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
166
+ const target = this.compileTableReference(ast.from);
167
+ const usingClause = this.compileDeleteUsingClause(ast, ctx);
168
+ const whereClause = this.compileWhere(ast.where, ctx);
169
+ const returning = this.compileReturning(ast.returning, ctx);
170
+ return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
171
+ }
172
+
173
+ protected formatReturningColumns(returning: ColumnNode[]): string {
174
+ return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
175
+ }
176
+
177
+ protected compileDistinct(ast: SelectQueryNode): string {
178
+ return ast.distinct ? 'DISTINCT ' : '';
179
+ }
180
+
181
+ protected compileSelectColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
182
+ return ast.columns.map(c => {
183
+ const expr = this.compileOperand(c, ctx);
184
+ if (c.alias) {
185
+ if (c.alias.includes('(')) return c.alias;
186
+ return `${expr} AS ${this.quoteIdentifier(c.alias)}`;
187
+ }
188
+ return expr;
189
+ }).join(', ');
190
+ }
191
+
136
192
  protected compileFrom(ast: SelectQueryNode['from'], ctx?: CompilerContext): string {
137
193
  const tableSource = ast as any;
138
194
  if (tableSource.type === 'FunctionTable') {
@@ -170,24 +226,67 @@ export abstract class SqlDialectBase extends Dialect {
170
226
  return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
171
227
  }
172
228
 
173
- protected compileTableName(table: { name: string; schema?: string }): string {
229
+ protected compileTableName(table: { name: string; schema?: string; alias?: string }): string {
174
230
  if (table.schema) {
175
231
  return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
176
232
  }
177
233
  return this.quoteIdentifier(table.name);
178
234
  }
179
-
180
- protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
181
- if (!ast.having) return '';
182
- return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
183
- }
184
-
185
- protected stripTrailingSemicolon(sql: string): string {
186
- return sql.trim().replace(/;$/, '');
187
- }
188
-
189
- protected wrapSetOperand(sql: string): string {
190
- const trimmed = this.stripTrailingSemicolon(sql);
191
- return `(${trimmed})`;
192
- }
235
+
236
+ protected compileTableReference(table: { name: string; schema?: string; alias?: string }): string {
237
+ const base = this.compileTableName(table);
238
+ return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
239
+ }
240
+
241
+ private compileUpdateFromClause(ast: UpdateQueryNode, ctx: CompilerContext): string {
242
+ if (!ast.from && (!ast.joins || ast.joins.length === 0)) return '';
243
+ if (!ast.from) {
244
+ throw new Error('UPDATE with JOINs requires an explicit FROM clause.');
245
+ }
246
+ const from = this.compileFrom(ast.from, ctx);
247
+ const joins = JoinCompiler.compileJoins(
248
+ ast.joins,
249
+ ctx,
250
+ this.compileFrom.bind(this),
251
+ this.compileExpression.bind(this)
252
+ );
253
+ return ` FROM ${from}${joins}`;
254
+ }
255
+
256
+ private compileDeleteUsingClause(ast: DeleteQueryNode, ctx: CompilerContext): string {
257
+ if (!ast.using && (!ast.joins || ast.joins.length === 0)) return '';
258
+ if (!ast.using) {
259
+ throw new Error('DELETE with JOINs requires a USING clause.');
260
+ }
261
+ const usingTable = this.compileFrom(ast.using, ctx);
262
+ const joins = JoinCompiler.compileJoins(
263
+ ast.joins,
264
+ ctx,
265
+ this.compileFrom.bind(this),
266
+ this.compileExpression.bind(this)
267
+ );
268
+ return ` USING ${usingTable}${joins}`;
269
+ }
270
+
271
+ protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
272
+ if (!ast.having) return '';
273
+ return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
274
+ }
275
+
276
+ protected stripTrailingSemicolon(sql: string): string {
277
+ return sql.trim().replace(/;$/, '');
278
+ }
279
+
280
+ protected wrapSetOperand(sql: string): string {
281
+ const trimmed = this.stripTrailingSemicolon(sql);
282
+ return `(${trimmed})`;
283
+ }
284
+
285
+ protected renderOrderByNulls(order: OrderByNode): string | undefined {
286
+ return order.nulls ? ` NULLS ${order.nulls}` : '';
287
+ }
288
+
289
+ protected renderOrderByCollation(order: OrderByNode): string | undefined {
290
+ return order.collation ? ` COLLATE ${order.collation}` : '';
291
+ }
193
292
  }