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.
- package/dist/index.cjs +1466 -189
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +723 -51
- package/dist/index.d.ts +723 -51
- package/dist/index.js +1457 -189
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen/typescript.ts +66 -5
- package/src/core/ast/aggregate-functions.ts +15 -15
- package/src/core/ast/expression-builders.ts +378 -316
- package/src/core/ast/expression-nodes.ts +210 -186
- package/src/core/ast/expression-visitor.ts +40 -30
- package/src/core/ast/query.ts +164 -132
- package/src/core/ast/window-functions.ts +86 -86
- package/src/core/dialect/abstract.ts +509 -479
- package/src/core/dialect/base/groupby-compiler.ts +6 -6
- package/src/core/dialect/base/join-compiler.ts +9 -12
- package/src/core/dialect/base/orderby-compiler.ts +20 -6
- package/src/core/dialect/base/sql-dialect.ts +237 -138
- package/src/core/dialect/mssql/index.ts +164 -185
- package/src/core/dialect/sqlite/index.ts +39 -34
- package/src/core/execution/db-executor.ts +46 -6
- package/src/core/execution/executors/mssql-executor.ts +39 -22
- package/src/core/execution/executors/mysql-executor.ts +23 -6
- package/src/core/execution/executors/sqlite-executor.ts +29 -3
- package/src/core/execution/pooling/pool-types.ts +30 -0
- package/src/core/execution/pooling/pool.ts +268 -0
- package/src/core/functions/standard-strategy.ts +46 -37
- package/src/decorators/bootstrap.ts +7 -7
- package/src/index.ts +6 -0
- package/src/orm/domain-event-bus.ts +49 -0
- package/src/orm/entity-metadata.ts +9 -9
- package/src/orm/entity.ts +58 -0
- package/src/orm/orm-session.ts +465 -270
- package/src/orm/orm.ts +61 -11
- package/src/orm/pooled-executor-factory.ts +131 -0
- package/src/orm/query-logger.ts +6 -12
- package/src/orm/relation-change-processor.ts +75 -0
- package/src/orm/relations/many-to-many.ts +4 -2
- package/src/orm/save-graph.ts +303 -0
- package/src/orm/transaction-runner.ts +3 -3
- package/src/orm/unit-of-work.ts +128 -0
- package/src/query-builder/delete-query-state.ts +67 -38
- package/src/query-builder/delete.ts +37 -1
- package/src/query-builder/hydration-manager.ts +93 -79
- package/src/query-builder/insert-query-state.ts +131 -61
- package/src/query-builder/insert.ts +27 -1
- package/src/query-builder/query-ast-service.ts +207 -170
- package/src/query-builder/select-query-state.ts +169 -162
- package/src/query-builder/select.ts +15 -23
- package/src/query-builder/update-query-state.ts +114 -77
- 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
|
|
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,
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
18
|
-
|
|
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 {
|
|
3
|
-
|
|
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(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
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
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
181
|
-
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
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
|
}
|