metal-orm 1.1.23 → 1.1.25
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 +832 -583
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +281 -144
- package/dist/index.d.ts +281 -144
- package/dist/index.js +807 -583
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/dialect/base/returning-strategy.ts +40 -39
- package/src/core/dialect/base/sql-compiler-set.ts +33 -0
- package/src/core/dialect/base/sql-dialect.ts +125 -222
- package/src/core/dialect/base/standard-delete-compiler.ts +37 -0
- package/src/core/dialect/base/standard-insert-compiler.ts +49 -0
- package/src/core/dialect/base/standard-select-compiler.ts +82 -0
- package/src/core/dialect/base/standard-sql-services.ts +44 -0
- package/src/core/dialect/base/standard-sql-source-compiler.ts +88 -0
- package/src/core/dialect/base/standard-update-compiler.ts +53 -0
- package/src/core/dialect/base/upsert-strategy.ts +45 -0
- package/src/core/dialect/capabilities/procedure-compiler.ts +10 -8
- package/src/core/dialect/mssql/compiler-factory.ts +12 -0
- package/src/core/dialect/mssql/delete-compiler.ts +40 -0
- package/src/core/dialect/mssql/index.ts +24 -371
- package/src/core/dialect/mssql/insert-compiler.ts +112 -0
- package/src/core/dialect/mssql/output.ts +46 -0
- package/src/core/dialect/mssql/procedure-compiler.ts +81 -0
- package/src/core/dialect/mssql/select-compiler.ts +116 -0
- package/src/core/dialect/mssql/update-compiler.ts +37 -0
- package/src/core/dialect/mysql/index.ts +24 -117
- package/src/core/dialect/mysql/procedure-compiler.ts +67 -0
- package/src/core/dialect/mysql/upsert.ts +42 -0
- package/src/core/dialect/postgres/index.ts +34 -101
- package/src/core/dialect/postgres/procedure-compiler.ts +41 -0
- package/src/core/dialect/postgres/returning.ts +4 -0
- package/src/core/dialect/postgres/upsert.ts +43 -0
- package/src/core/dialect/sqlite/index.ts +15 -70
- package/src/core/dialect/sqlite/returning.ts +30 -0
- package/src/core/dialect/sqlite/upsert.ts +43 -0
- package/src/index.ts +28 -10
package/package.json
CHANGED
|
@@ -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
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
4
|
+
export type QuoteIdentifier = (id: string) => string;
|
|
5
|
+
|
|
6
|
+
/** Backend-specific RETURNING/OUTPUT rendering strategy. */
|
|
8
7
|
export interface ReturningStrategy {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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>;
|
|
@@ -1,166 +1,157 @@
|
|
|
1
1
|
import { DialectBase } from '../abstract.js';
|
|
2
2
|
import type { CompilerContext } from '../abstract.js';
|
|
3
3
|
import type {
|
|
4
|
-
SelectQueryNode,
|
|
5
|
-
InsertQueryNode,
|
|
6
|
-
UpdateQueryNode,
|
|
7
4
|
DeleteQueryNode,
|
|
8
|
-
InsertSourceNode,
|
|
9
|
-
UpsertClause,
|
|
10
|
-
TableSourceNode,
|
|
11
5
|
DerivedTableNode,
|
|
12
6
|
FunctionTableNode,
|
|
7
|
+
InsertQueryNode,
|
|
13
8
|
OrderByNode,
|
|
14
|
-
|
|
9
|
+
SelectQueryNode,
|
|
10
|
+
TableNode,
|
|
11
|
+
TableSourceNode,
|
|
12
|
+
UpdateAssignmentNode,
|
|
13
|
+
UpdateQueryNode,
|
|
14
|
+
UpsertClause
|
|
15
15
|
} from '../../ast/query.js';
|
|
16
|
-
import type { ColumnNode
|
|
17
|
-
import {
|
|
16
|
+
import type { ColumnNode } from '../../ast/expression.js';
|
|
17
|
+
import type { FunctionStrategy } from '../../functions/types.js';
|
|
18
|
+
import type { TableFunctionStrategy } from '../../functions/table-types.js';
|
|
18
19
|
import { StandardLimitOffsetPagination } from './pagination-strategy.js';
|
|
19
20
|
import type { PaginationStrategy } from './pagination-strategy.js';
|
|
20
|
-
import { CteCompiler } from './cte-compiler.js';
|
|
21
21
|
import { NoReturningStrategy } from './returning-strategy.js';
|
|
22
22
|
import type { ReturningStrategy } from './returning-strategy.js';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
23
|
+
import { NoUpsertStrategy } from './upsert-strategy.js';
|
|
24
|
+
import type { UpsertStrategy } from './upsert-strategy.js';
|
|
25
|
+
import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
|
|
26
|
+
import { StandardSelectCompiler } from './standard-select-compiler.js';
|
|
27
|
+
import { StandardInsertCompiler } from './standard-insert-compiler.js';
|
|
28
|
+
import { StandardUpdateCompiler } from './standard-update-compiler.js';
|
|
29
|
+
import { StandardDeleteCompiler } from './standard-delete-compiler.js';
|
|
30
|
+
import type { StandardSqlCompilerServices } from './standard-sql-services.js';
|
|
31
|
+
import type { SqlCompilerFactory, SqlCompilerSet } from './sql-compiler-set.js';
|
|
32
|
+
|
|
33
|
+
export interface SqlDialectBaseOptions {
|
|
34
|
+
functionStrategy?: FunctionStrategy;
|
|
35
|
+
tableFunctionStrategy?: TableFunctionStrategy;
|
|
36
|
+
paginationStrategy?: PaginationStrategy;
|
|
37
|
+
returningStrategy?: ReturningStrategy;
|
|
38
|
+
upsertStrategy?: UpsertStrategy;
|
|
39
|
+
compilerFactory?: SqlCompilerFactory;
|
|
40
|
+
supportsDmlReturning?: boolean;
|
|
41
|
+
}
|
|
26
42
|
|
|
27
43
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
44
|
+
* Thin assembly base for dialects that use MetalORM's reusable SQL compiler pieces.
|
|
45
|
+
*
|
|
46
|
+
* Query orchestration, source rendering, upsert behavior and returning behavior are
|
|
47
|
+
* injected components. Concrete dialects keep only syntax hooks that are genuinely
|
|
48
|
+
* intrinsic to that backend.
|
|
30
49
|
*/
|
|
31
50
|
export abstract class SqlDialectBase extends DialectBase {
|
|
32
51
|
abstract quoteIdentifier(id: string): string;
|
|
33
52
|
|
|
34
|
-
protected paginationStrategy: PaginationStrategy
|
|
35
|
-
protected returningStrategy: ReturningStrategy
|
|
53
|
+
protected readonly paginationStrategy: PaginationStrategy;
|
|
54
|
+
protected readonly returningStrategy: ReturningStrategy;
|
|
55
|
+
protected readonly upsertStrategy: UpsertStrategy;
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
ctx,
|
|
42
|
-
this.quoteIdentifier.bind(this),
|
|
43
|
-
this.compileSelectAst.bind(this),
|
|
44
|
-
this.normalizeSelectAst.bind(this),
|
|
45
|
-
this.stripTrailingSemicolon.bind(this)
|
|
46
|
-
);
|
|
47
|
-
const baseAst: SelectQueryNode = hasSetOps
|
|
48
|
-
? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
|
|
49
|
-
: ast;
|
|
50
|
-
const baseSelect = this.compileSelectCore(baseAst, ctx);
|
|
51
|
-
if (!hasSetOps) return `${ctes}${baseSelect}`;
|
|
52
|
-
return this.compileSelectWithSetOps(ast, baseSelect, ctes, ctx);
|
|
53
|
-
}
|
|
57
|
+
private readonly dmlReturningSupported: boolean;
|
|
58
|
+
private readonly sourceCompiler: StandardSqlSourceCompiler;
|
|
59
|
+
private readonly standardUpdateCompiler: StandardUpdateCompiler;
|
|
60
|
+
private readonly compilerSet: SqlCompilerSet;
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
baseSelect: string,
|
|
58
|
-
ctes: string,
|
|
59
|
-
ctx: CompilerContext
|
|
60
|
-
): string {
|
|
61
|
-
const compound = ast.setOps!
|
|
62
|
-
.map(op => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`)
|
|
63
|
-
.join(' ');
|
|
64
|
-
const orderBy = OrderByCompiler.compileOrderBy(
|
|
65
|
-
ast,
|
|
66
|
-
term => this.compileOrderingTerm(term, ctx),
|
|
67
|
-
this.renderOrderByNulls.bind(this),
|
|
68
|
-
this.renderOrderByCollation.bind(this)
|
|
69
|
-
);
|
|
70
|
-
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
71
|
-
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
72
|
-
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
73
|
-
}
|
|
62
|
+
protected constructor(options: SqlDialectBaseOptions = {}) {
|
|
63
|
+
super(options.functionStrategy, options.tableFunctionStrategy);
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
this.paginationStrategy = options.paginationStrategy ?? new StandardLimitOffsetPagination();
|
|
66
|
+
this.returningStrategy = options.returningStrategy ?? new NoReturningStrategy();
|
|
67
|
+
this.upsertStrategy = options.upsertStrategy ?? new NoUpsertStrategy();
|
|
68
|
+
this.dmlReturningSupported = options.supportsDmlReturning ?? false;
|
|
69
|
+
|
|
70
|
+
const services: StandardSqlCompilerServices = {
|
|
71
|
+
getDialectName: () => this.dialect,
|
|
72
|
+
getPaginationStrategy: () => this.paginationStrategy,
|
|
73
|
+
getTableFunctionStrategy: () => this.tableFunctionStrategy,
|
|
74
|
+
quoteIdentifier: id => this.quoteIdentifier(id),
|
|
75
|
+
compileOperand: (node, ctx) => this.compileOperand(node, ctx),
|
|
76
|
+
compileExpression: (node, ctx) => this.compileExpression(node, ctx),
|
|
77
|
+
compileOrderingTerm: (term, ctx) => this.compileOrderingTerm(term, ctx),
|
|
78
|
+
normalizeSelectAst: ast => this.normalizeSelectAst(ast),
|
|
79
|
+
compileSelectAst: (ast, ctx) => this.compileSelectAst(ast, ctx),
|
|
80
|
+
compileReturning: (returning, ctx) => this.compileReturning(returning, ctx),
|
|
81
|
+
compileUpsertClause: (ast, ctx) => this.compileUpsertClause(ast, ctx),
|
|
82
|
+
compileSetTarget: (column, table) => this.compileSetTarget(column, table),
|
|
83
|
+
renderOrderByNulls: order => this.renderOrderByNulls(order),
|
|
84
|
+
renderOrderByCollation: order => this.renderOrderByCollation(order)
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
this.sourceCompiler = new StandardSqlSourceCompiler(services);
|
|
88
|
+
const standardSelect = new StandardSelectCompiler(services, this.sourceCompiler);
|
|
89
|
+
const standardInsert = new StandardInsertCompiler(services, this.sourceCompiler);
|
|
90
|
+
this.standardUpdateCompiler = new StandardUpdateCompiler(services, this.sourceCompiler);
|
|
91
|
+
const standardDelete = new StandardDeleteCompiler(services, this.sourceCompiler);
|
|
92
|
+
|
|
93
|
+
const overrides = options.compilerFactory?.({
|
|
94
|
+
services,
|
|
95
|
+
sources: this.sourceCompiler
|
|
96
|
+
}) ?? {};
|
|
97
|
+
|
|
98
|
+
this.compilerSet = {
|
|
99
|
+
select: overrides.select ?? standardSelect,
|
|
100
|
+
insert: overrides.insert ?? standardInsert,
|
|
101
|
+
update: overrides.update ?? this.standardUpdateCompiler,
|
|
102
|
+
delete: overrides.delete ?? standardDelete
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
override supportsDmlReturningClause(): boolean {
|
|
107
|
+
return this.dmlReturningSupported;
|
|
86
108
|
}
|
|
87
109
|
|
|
88
|
-
protected
|
|
89
|
-
|
|
90
|
-
if (!ast.onConflict) return '';
|
|
91
|
-
throw new Error(`UPSERT/ON CONFLICT is not supported by dialect "${this.dialect}".`);
|
|
110
|
+
protected compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
111
|
+
return this.compilerSet.select.compile(ast, ctx);
|
|
92
112
|
}
|
|
93
113
|
|
|
94
|
-
protected
|
|
95
|
-
return this.
|
|
114
|
+
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
115
|
+
return this.compilerSet.insert.compile(ast, ctx);
|
|
96
116
|
}
|
|
97
117
|
|
|
98
|
-
protected
|
|
99
|
-
|
|
100
|
-
if (!source.rows.length) {
|
|
101
|
-
throw new Error('INSERT ... VALUES requires at least one row.');
|
|
102
|
-
}
|
|
103
|
-
const values = source.rows
|
|
104
|
-
.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`)
|
|
105
|
-
.join(', ');
|
|
106
|
-
return `VALUES ${values}`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const normalized = this.normalizeSelectAst(source.query);
|
|
110
|
-
return this.compileSelectAst(normalized, ctx).trim();
|
|
118
|
+
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
119
|
+
return this.compilerSet.update.compile(ast, ctx);
|
|
111
120
|
}
|
|
112
121
|
|
|
113
|
-
protected
|
|
114
|
-
return
|
|
122
|
+
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
123
|
+
return this.compilerSet.delete.compile(ast, ctx);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
protected
|
|
118
|
-
|
|
126
|
+
protected compileUpsertClause(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
127
|
+
return this.upsertStrategy.compile(ast, ctx, {
|
|
128
|
+
getDialectName: () => this.dialect,
|
|
129
|
+
quoteIdentifier: id => this.quoteIdentifier(id),
|
|
130
|
+
compileOperand: (node, compilerContext) => this.compileOperand(node, compilerContext),
|
|
131
|
+
compileExpression: (node, compilerContext) => this.compileExpression(node, compilerContext),
|
|
132
|
+
compileUpdateAssignments: (assignments, table, compilerContext) =>
|
|
133
|
+
this.standardUpdateCompiler.compileAssignments(assignments, table, compilerContext)
|
|
134
|
+
});
|
|
119
135
|
}
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const joins = JoinCompiler.compileJoins(
|
|
125
|
-
ast.joins,
|
|
137
|
+
protected compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string {
|
|
138
|
+
return this.returningStrategy.compileReturning(
|
|
139
|
+
returning,
|
|
126
140
|
ctx,
|
|
127
|
-
this.
|
|
128
|
-
this.compileExpression.bind(this)
|
|
129
|
-
);
|
|
130
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
131
|
-
const groupBy = GroupByCompiler.compileGroupBy(ast, term => this.compileOrderingTerm(term, ctx));
|
|
132
|
-
const having = this.compileHaving(ast, ctx);
|
|
133
|
-
const orderBy = OrderByCompiler.compileOrderBy(
|
|
134
|
-
ast,
|
|
135
|
-
term => this.compileOrderingTerm(term, ctx),
|
|
136
|
-
this.renderOrderByNulls.bind(this),
|
|
137
|
-
this.renderOrderByCollation.bind(this)
|
|
141
|
+
id => this.quoteIdentifier(id)
|
|
138
142
|
);
|
|
139
|
-
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
140
|
-
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
|
-
protected
|
|
144
|
-
|
|
145
|
-
const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
|
|
146
|
-
const fromClause = this.compileUpdateFromClause(ast, ctx);
|
|
147
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
148
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
149
|
-
return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
|
|
145
|
+
protected ensureConflictColumns(clause: UpsertClause, message: string): void {
|
|
146
|
+
if (!clause.target.columns.length) throw new Error(message);
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
protected compileUpdateAssignments(
|
|
153
|
-
assignments:
|
|
150
|
+
assignments: UpdateAssignmentNode[],
|
|
154
151
|
table: TableNode,
|
|
155
152
|
ctx: CompilerContext
|
|
156
153
|
): string {
|
|
157
|
-
return assignments
|
|
158
|
-
.map(assignment => {
|
|
159
|
-
const target = this.compileSetTarget(assignment.column, table);
|
|
160
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
161
|
-
return `${target} = ${value}`;
|
|
162
|
-
})
|
|
163
|
-
.join(', ');
|
|
154
|
+
return this.standardUpdateCompiler.compileAssignments(assignments, table, ctx);
|
|
164
155
|
}
|
|
165
156
|
|
|
166
157
|
protected compileSetTarget(column: ColumnNode, table: TableNode): string {
|
|
@@ -177,131 +168,43 @@ export abstract class SqlDialectBase extends DialectBase {
|
|
|
177
168
|
return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
|
|
178
169
|
}
|
|
179
170
|
|
|
180
|
-
protected compileDeleteAst(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
181
|
-
const target = this.compileTableReference(ast.from);
|
|
182
|
-
const usingClause = this.compileDeleteUsingClause(ast, ctx);
|
|
183
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
184
|
-
const returning = this.compileReturning(ast.returning, ctx);
|
|
185
|
-
return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
171
|
protected formatReturningColumns(returning: ColumnNode[]): string {
|
|
189
|
-
return this.returningStrategy.formatReturningColumns(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return ast.distinct ? 'DISTINCT ' : '';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
protected compileSelectColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
197
|
-
if (!ast.columns || ast.columns.length === 0) return '*';
|
|
198
|
-
return ast.columns.map(column => {
|
|
199
|
-
const expr = this.compileOperand(column, ctx);
|
|
200
|
-
if (column.alias) {
|
|
201
|
-
if (column.alias.includes('(')) return column.alias;
|
|
202
|
-
return `${expr} AS ${this.quoteIdentifier(column.alias)}`;
|
|
203
|
-
}
|
|
204
|
-
return expr;
|
|
205
|
-
}).join(', ');
|
|
172
|
+
return this.returningStrategy.formatReturningColumns(
|
|
173
|
+
returning,
|
|
174
|
+
id => this.quoteIdentifier(id)
|
|
175
|
+
);
|
|
206
176
|
}
|
|
207
177
|
|
|
208
|
-
protected compileFrom(
|
|
209
|
-
|
|
210
|
-
if (ast.type === 'DerivedTable') return this.compileDerivedTable(ast, ctx);
|
|
211
|
-
return this.compileTableSource(ast);
|
|
178
|
+
protected compileFrom(source: TableSourceNode, ctx?: CompilerContext): string {
|
|
179
|
+
return this.sourceCompiler.compileFrom(source, ctx);
|
|
212
180
|
}
|
|
213
181
|
|
|
214
182
|
protected compileFunctionTable(fn: FunctionTableNode, ctx?: CompilerContext): string {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (ctx) {
|
|
218
|
-
const renderer = this.tableFunctionStrategy.getRenderer(key);
|
|
219
|
-
if (renderer) {
|
|
220
|
-
const compiledArgs = (fn.args ?? []).map(arg => this.compileOperand(arg, ctx));
|
|
221
|
-
return renderer({
|
|
222
|
-
node: fn,
|
|
223
|
-
compiledArgs,
|
|
224
|
-
compileOperand: operand => this.compileOperand(operand, ctx),
|
|
225
|
-
quoteIdentifier: this.quoteIdentifier.bind(this)
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (fn.key) {
|
|
230
|
-
throw new Error(`Table function "${key}" is not supported by dialect "${this.dialect}".`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return FunctionTableFormatter.format(fn, ctx, {
|
|
235
|
-
quoteIdentifier: id => this.quoteIdentifier(id),
|
|
236
|
-
compileOperand: (node, compilerContext) => this.compileOperand(node, compilerContext)
|
|
237
|
-
});
|
|
183
|
+
return this.sourceCompiler.compileFunctionTable(fn, ctx);
|
|
238
184
|
}
|
|
239
185
|
|
|
240
186
|
protected compileDerivedTable(table: DerivedTableNode, ctx?: CompilerContext): string {
|
|
241
|
-
|
|
242
|
-
const subquery = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx!).trim().replace(/;$/, '');
|
|
243
|
-
const columns = table.columnAliases?.length
|
|
244
|
-
? ` (${table.columnAliases.map(c => this.quoteIdentifier(c)).join(', ')})`
|
|
245
|
-
: '';
|
|
246
|
-
return `(${subquery}) AS ${this.quoteIdentifier(table.alias)}${columns}`;
|
|
187
|
+
return this.sourceCompiler.compileDerivedTable(table, ctx);
|
|
247
188
|
}
|
|
248
189
|
|
|
249
190
|
protected compileTableSource(table: TableSourceNode): string {
|
|
250
|
-
|
|
251
|
-
if (table.type === 'DerivedTable') return this.compileDerivedTable(table as DerivedTableNode);
|
|
252
|
-
const base = this.compileTableName(table);
|
|
253
|
-
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
191
|
+
return this.sourceCompiler.compileTableSource(table);
|
|
254
192
|
}
|
|
255
193
|
|
|
256
|
-
protected compileTableName(table: { name: string; schema?: string
|
|
257
|
-
|
|
258
|
-
return `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}`;
|
|
259
|
-
}
|
|
260
|
-
return this.quoteIdentifier(table.name);
|
|
194
|
+
protected compileTableName(table: { name: string; schema?: string }): string {
|
|
195
|
+
return this.sourceCompiler.compileTableName(table);
|
|
261
196
|
}
|
|
262
197
|
|
|
263
198
|
protected compileTableReference(table: { name: string; schema?: string; alias?: string }): string {
|
|
264
|
-
|
|
265
|
-
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private compileUpdateFromClause(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
269
|
-
if (!ast.from && (!ast.joins || ast.joins.length === 0)) return '';
|
|
270
|
-
if (!ast.from) throw new Error('UPDATE with JOINs requires an explicit FROM clause.');
|
|
271
|
-
const from = this.compileFrom(ast.from, ctx);
|
|
272
|
-
const joins = JoinCompiler.compileJoins(
|
|
273
|
-
ast.joins,
|
|
274
|
-
ctx,
|
|
275
|
-
this.compileFrom.bind(this),
|
|
276
|
-
this.compileExpression.bind(this)
|
|
277
|
-
);
|
|
278
|
-
return ` FROM ${from}${joins}`;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private compileDeleteUsingClause(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
282
|
-
if (!ast.using && (!ast.joins || ast.joins.length === 0)) return '';
|
|
283
|
-
if (!ast.using) throw new Error('DELETE with JOINs requires a USING clause.');
|
|
284
|
-
const usingTable = this.compileFrom(ast.using, ctx);
|
|
285
|
-
const joins = JoinCompiler.compileJoins(
|
|
286
|
-
ast.joins,
|
|
287
|
-
ctx,
|
|
288
|
-
this.compileFrom.bind(this),
|
|
289
|
-
this.compileExpression.bind(this)
|
|
290
|
-
);
|
|
291
|
-
return ` USING ${usingTable}${joins}`;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
protected compileHaving(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
295
|
-
if (!ast.having) return '';
|
|
296
|
-
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
199
|
+
return this.sourceCompiler.compileTableReference(table);
|
|
297
200
|
}
|
|
298
201
|
|
|
299
202
|
protected stripTrailingSemicolon(sql: string): string {
|
|
300
|
-
return
|
|
203
|
+
return this.sourceCompiler.stripTrailingSemicolon(sql);
|
|
301
204
|
}
|
|
302
205
|
|
|
303
206
|
protected wrapSetOperand(sql: string): string {
|
|
304
|
-
return
|
|
207
|
+
return this.sourceCompiler.wrapSetOperand(sql);
|
|
305
208
|
}
|
|
306
209
|
|
|
307
210
|
protected renderOrderByNulls(order: OrderByNode): string | undefined {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { CompilerContext } from '../abstract.js';
|
|
2
|
+
import type { DeleteQueryNode } from '../../ast/query.js';
|
|
3
|
+
import { JoinCompiler } from './join-compiler.js';
|
|
4
|
+
import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
|
|
5
|
+
import type { StandardSqlCompilerServices } from './standard-sql-services.js';
|
|
6
|
+
|
|
7
|
+
/** Standard DELETE orchestration, independent from concrete dialect classes. */
|
|
8
|
+
export class StandardDeleteCompiler {
|
|
9
|
+
public constructor(
|
|
10
|
+
private readonly services: StandardSqlCompilerServices,
|
|
11
|
+
private readonly sources: StandardSqlSourceCompiler
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
compile(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
15
|
+
const target = this.sources.compileTableReference(ast.from);
|
|
16
|
+
const using = this.compileUsingClause(ast, ctx);
|
|
17
|
+
const where = ast.where ? ` WHERE ${this.services.compileExpression(ast.where, ctx)}` : '';
|
|
18
|
+
const returning = this.services.compileReturning(ast.returning, ctx);
|
|
19
|
+
return `DELETE FROM ${target}${using}${where}${returning}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private compileUsingClause(ast: DeleteQueryNode, ctx: CompilerContext): string {
|
|
23
|
+
if (!ast.using && (!ast.joins || ast.joins.length === 0)) return '';
|
|
24
|
+
if (!ast.using) {
|
|
25
|
+
throw new Error('DELETE with JOINs requires a USING clause.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const usingTable = this.sources.compileFrom(ast.using, ctx);
|
|
29
|
+
const joins = JoinCompiler.compileJoins(
|
|
30
|
+
ast.joins,
|
|
31
|
+
ctx,
|
|
32
|
+
(source, compilerContext) => this.sources.compileFrom(source, compilerContext),
|
|
33
|
+
(expression, compilerContext) => this.services.compileExpression(expression, compilerContext)
|
|
34
|
+
);
|
|
35
|
+
return ` USING ${usingTable}${joins}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CompilerContext } from '../abstract.js';
|
|
2
|
+
import type { InsertQueryNode, InsertSourceNode, UpsertClause } from '../../ast/query.js';
|
|
3
|
+
import type { ColumnNode } from '../../ast/expression.js';
|
|
4
|
+
import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
|
|
5
|
+
import type { StandardSqlCompilerServices } from './standard-sql-services.js';
|
|
6
|
+
|
|
7
|
+
/** Standard INSERT orchestration, including VALUES/SELECT sources and upsert hook. */
|
|
8
|
+
export class StandardInsertCompiler {
|
|
9
|
+
public constructor(
|
|
10
|
+
private readonly services: StandardSqlCompilerServices,
|
|
11
|
+
private readonly sources: StandardSqlSourceCompiler
|
|
12
|
+
) {}
|
|
13
|
+
|
|
14
|
+
compile(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
15
|
+
if (!ast.columns.length) {
|
|
16
|
+
throw new Error('INSERT queries must specify columns.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const table = this.sources.compileTableName(ast.into);
|
|
20
|
+
const columnList = this.compileColumnList(ast.columns);
|
|
21
|
+
const source = this.compileSource(ast.source, ctx);
|
|
22
|
+
const upsert = this.services.compileUpsertClause(ast, ctx);
|
|
23
|
+
const returning = this.services.compileReturning(ast.returning, ctx);
|
|
24
|
+
return `INSERT INTO ${table} (${columnList}) ${source}${upsert}${returning}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
compileSource(source: InsertSourceNode, ctx: CompilerContext): string {
|
|
28
|
+
if (source.type === 'InsertValues') {
|
|
29
|
+
if (!source.rows.length) {
|
|
30
|
+
throw new Error('INSERT ... VALUES requires at least one row.');
|
|
31
|
+
}
|
|
32
|
+
const values = source.rows
|
|
33
|
+
.map(row => `(${row.map(value => this.services.compileOperand(value, ctx)).join(', ')})`)
|
|
34
|
+
.join(', ');
|
|
35
|
+
return `VALUES ${values}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const normalized = this.services.normalizeSelectAst(source.query);
|
|
39
|
+
return this.services.compileSelectAst(normalized, ctx).trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
compileColumnList(columns: ColumnNode[]): string {
|
|
43
|
+
return columns.map(column => this.services.quoteIdentifier(column.name)).join(', ');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
ensureConflictColumns(clause: UpsertClause, message: string): void {
|
|
47
|
+
if (!clause.target.columns.length) throw new Error(message);
|
|
48
|
+
}
|
|
49
|
+
}
|