metal-orm 1.0.118 → 1.1.0
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/README.md +26 -14
- package/dist/index.cjs +728 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +419 -7
- package/dist/index.d.ts +419 -7
- package/dist/index.js +720 -17
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cache/adapters/index.ts +2 -0
- package/src/cache/adapters/keyv-cache-adapter.ts +81 -0
- package/src/cache/adapters/memory-cache-adapter.ts +127 -0
- package/src/cache/cache-interfaces.ts +70 -0
- package/src/cache/duration-utils.ts +82 -0
- package/src/cache/index.ts +28 -0
- package/src/cache/query-cache-manager.ts +130 -0
- package/src/cache/strategies/cache-strategy.ts +29 -0
- package/src/cache/strategies/default-cache-strategy.ts +96 -0
- package/src/cache/strategies/index.ts +2 -0
- package/src/cache/tag-index.ts +128 -0
- package/src/core/dialect/abstract.ts +565 -565
- package/src/core/dialect/mssql/index.ts +68 -3
- package/src/core/dialect/postgres/index.ts +1 -1
- package/src/core/dialect/sqlite/index.ts +1 -1
- package/src/core/execution/db-executor.ts +107 -103
- package/src/core/execution/executors/mysql-executor.ts +9 -2
- package/src/index.ts +3 -0
- package/src/orm/orm-session.ts +616 -563
- package/src/orm/orm.ts +108 -71
- package/src/orm/unit-of-work.ts +22 -4
- package/src/query-builder/select/cache-facet.ts +67 -0
- package/src/query-builder/select.ts +125 -57
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { CompilerContext } from '../abstract.js';
|
|
2
2
|
import {
|
|
3
3
|
SelectQueryNode,
|
|
4
|
+
InsertQueryNode,
|
|
5
|
+
UpdateQueryNode,
|
|
4
6
|
DeleteQueryNode
|
|
5
7
|
} from '../../ast/query.js';
|
|
6
|
-
import { JsonPathNode } from '../../ast/expression.js';
|
|
8
|
+
import { JsonPathNode, ColumnNode } from '../../ast/expression.js';
|
|
7
9
|
import { MssqlFunctionStrategy } from './functions.js';
|
|
8
10
|
import { OrderByCompiler } from '../base/orderby-compiler.js';
|
|
9
11
|
import { JoinCompiler } from '../base/join-compiler.js';
|
|
@@ -99,8 +101,24 @@ export class SqlServerDialect extends SqlDialectBase {
|
|
|
99
101
|
this.compileExpression.bind(this)
|
|
100
102
|
);
|
|
101
103
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
102
|
-
const returning = this.
|
|
103
|
-
return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}
|
|
104
|
+
const returning = this.compileOutputClause(ast.returning, 'deleted');
|
|
105
|
+
return `DELETE ${this.quoteIdentifier(alias)}${returning} FROM ${target}${joins}${whereClause}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected compileUpdateAst(ast: UpdateQueryNode, ctx: CompilerContext): string {
|
|
109
|
+
const target = this.compileTableReference(ast.table);
|
|
110
|
+
const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
|
|
111
|
+
const output = this.compileReturning(ast.returning, ctx);
|
|
112
|
+
const fromClause = ast.from ? ` FROM ${this.compileFrom(ast.from, ctx)}` : '';
|
|
113
|
+
const joins = ast.joins
|
|
114
|
+
? ast.joins.map(j => {
|
|
115
|
+
const table = this.compileFrom(j.table, ctx);
|
|
116
|
+
const cond = this.compileExpression(j.condition, ctx);
|
|
117
|
+
return ` ${j.kind} JOIN ${table} ON ${cond}`;
|
|
118
|
+
}).join('')
|
|
119
|
+
: '';
|
|
120
|
+
const whereClause = this.compileWhere(ast.where, ctx);
|
|
121
|
+
return `UPDATE ${target} SET ${assignments}${output}${fromClause}${joins}${whereClause}`;
|
|
104
122
|
}
|
|
105
123
|
|
|
106
124
|
private compileSelectCoreForMssql(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
@@ -175,6 +193,53 @@ export class SqlServerDialect extends SqlDialectBase {
|
|
|
175
193
|
return pagination;
|
|
176
194
|
}
|
|
177
195
|
|
|
196
|
+
supportsDmlReturningClause(): boolean {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
protected compileReturning(returning: ColumnNode[] | undefined, _ctx: CompilerContext): string {
|
|
201
|
+
void _ctx;
|
|
202
|
+
return this.compileOutputClause(returning, 'inserted');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private compileOutputClause(returning: ColumnNode[] | undefined, prefix: 'inserted' | 'deleted'): string {
|
|
206
|
+
if (!returning || returning.length === 0) return '';
|
|
207
|
+
const columns = returning
|
|
208
|
+
.map(column => {
|
|
209
|
+
const colName = this.quoteIdentifier(column.name);
|
|
210
|
+
const alias = column.alias ? ` AS ${this.quoteIdentifier(column.alias)}` : '';
|
|
211
|
+
return `${prefix}.${colName}${alias}`;
|
|
212
|
+
})
|
|
213
|
+
.join(', ');
|
|
214
|
+
return ` OUTPUT ${columns}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
protected compileInsertAst(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
218
|
+
if (!ast.columns.length) {
|
|
219
|
+
throw new Error('INSERT queries must specify columns.');
|
|
220
|
+
}
|
|
221
|
+
const table = this.compileTableName(ast.into);
|
|
222
|
+
const columnList = ast.columns.map(column => this.quoteIdentifier(column.name)).join(', ');
|
|
223
|
+
const output = this.compileReturning(ast.returning, ctx);
|
|
224
|
+
const source = this.compileInsertValues(ast, ctx);
|
|
225
|
+
return `INSERT INTO ${table} (${columnList})${output} ${source}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private compileInsertValues(ast: InsertQueryNode, ctx: CompilerContext): string {
|
|
229
|
+
const source = ast.source;
|
|
230
|
+
if (source.type === 'InsertValues') {
|
|
231
|
+
if (!source.rows.length) {
|
|
232
|
+
throw new Error('INSERT ... VALUES requires at least one row.');
|
|
233
|
+
}
|
|
234
|
+
const values = source.rows
|
|
235
|
+
.map(row => `(${row.map(value => this.compileOperand(value, ctx)).join(', ')})`)
|
|
236
|
+
.join(', ');
|
|
237
|
+
return `VALUES ${values}`;
|
|
238
|
+
}
|
|
239
|
+
const normalized = this.normalizeSelectAst(source.query);
|
|
240
|
+
return this.compileSelectAst(normalized, ctx).trim();
|
|
241
|
+
}
|
|
242
|
+
|
|
178
243
|
private compileCtes(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
179
244
|
if (!ast.ctes || ast.ctes.length === 0) return '';
|
|
180
245
|
// MSSQL does not use RECURSIVE keyword, but supports recursion when CTE references itself.
|
|
@@ -1,103 +1,107 @@
|
|
|
1
|
-
// src/core/execution/db-executor.ts
|
|
2
|
-
|
|
3
|
-
// low-level canonical shape
|
|
4
|
-
export type QueryResult = {
|
|
5
|
-
columns: string[];
|
|
6
|
-
values: unknown[][];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
// src/core/execution/db-executor.ts
|
|
2
|
+
|
|
3
|
+
// low-level canonical shape
|
|
4
|
+
export type QueryResult = {
|
|
5
|
+
columns: string[];
|
|
6
|
+
values: unknown[][];
|
|
7
|
+
meta?: {
|
|
8
|
+
insertId?: number | string;
|
|
9
|
+
rowsAffected?: number;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export interface DbExecutor {
|
|
14
|
+
/** Capability flags so the runtime can make correct decisions without relying on optional methods. */
|
|
15
|
+
readonly capabilities: {
|
|
16
|
+
/** True if begin/commit/rollback are real and should be used to provide atomicity. */
|
|
17
|
+
transactions: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
executeSql(sql: string, params?: unknown[]): Promise<QueryResult[]>;
|
|
21
|
+
|
|
22
|
+
beginTransaction(): Promise<void>;
|
|
23
|
+
commitTransaction(): Promise<void>;
|
|
24
|
+
rollbackTransaction(): Promise<void>;
|
|
25
|
+
|
|
26
|
+
/** Release any underlying resources (connections, pool leases, etc). Must be idempotent. */
|
|
27
|
+
dispose(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// --- helpers ---
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert an array of row objects into a QueryResult.
|
|
34
|
+
*/
|
|
35
|
+
export function rowsToQueryResult(
|
|
36
|
+
rows: Array<Record<string, unknown>>
|
|
37
|
+
): QueryResult {
|
|
38
|
+
if (rows.length === 0) {
|
|
39
|
+
return { columns: [], values: [] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const columns = Object.keys(rows[0]);
|
|
43
|
+
const values = rows.map(row => columns.map(c => row[c]));
|
|
44
|
+
return { columns, values };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Minimal contract that most SQL clients can implement.
|
|
49
|
+
*/
|
|
50
|
+
export interface SimpleQueryRunner {
|
|
51
|
+
query(
|
|
52
|
+
sql: string,
|
|
53
|
+
params?: unknown[]
|
|
54
|
+
): Promise<Array<Record<string, unknown>>>;
|
|
55
|
+
|
|
56
|
+
/** Optional: used to support real transactions. */
|
|
57
|
+
beginTransaction?(): Promise<void>;
|
|
58
|
+
commitTransaction?(): Promise<void>;
|
|
59
|
+
rollbackTransaction?(): Promise<void>;
|
|
60
|
+
|
|
61
|
+
/** Optional: release resources (connection close, pool lease release, etc). */
|
|
62
|
+
dispose?(): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generic factory: turn any SimpleQueryRunner into a DbExecutor.
|
|
67
|
+
*/
|
|
68
|
+
export function createExecutorFromQueryRunner(
|
|
69
|
+
runner: SimpleQueryRunner
|
|
70
|
+
): DbExecutor {
|
|
71
|
+
const supportsTransactions =
|
|
72
|
+
typeof runner.beginTransaction === 'function' &&
|
|
73
|
+
typeof runner.commitTransaction === 'function' &&
|
|
74
|
+
typeof runner.rollbackTransaction === 'function';
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
capabilities: {
|
|
78
|
+
transactions: supportsTransactions,
|
|
79
|
+
},
|
|
80
|
+
async executeSql(sql, params) {
|
|
81
|
+
const rows = await runner.query(sql, params);
|
|
82
|
+
const result = rowsToQueryResult(rows);
|
|
83
|
+
return [result];
|
|
84
|
+
},
|
|
85
|
+
async beginTransaction() {
|
|
86
|
+
if (!supportsTransactions) {
|
|
87
|
+
throw new Error('Transactions are not supported by this executor');
|
|
88
|
+
}
|
|
89
|
+
await runner.beginTransaction!.call(runner);
|
|
90
|
+
},
|
|
91
|
+
async commitTransaction() {
|
|
92
|
+
if (!supportsTransactions) {
|
|
93
|
+
throw new Error('Transactions are not supported by this executor');
|
|
94
|
+
}
|
|
95
|
+
await runner.commitTransaction!.call(runner);
|
|
96
|
+
},
|
|
97
|
+
async rollbackTransaction() {
|
|
98
|
+
if (!supportsTransactions) {
|
|
99
|
+
throw new Error('Transactions are not supported by this executor');
|
|
100
|
+
}
|
|
101
|
+
await runner.rollbackTransaction!.call(runner);
|
|
102
|
+
},
|
|
103
|
+
async dispose() {
|
|
104
|
+
await runner.dispose?.call(runner);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -35,8 +35,15 @@ export function createMysqlExecutor(
|
|
|
35
35
|
const [rows] = await client.query(sql, params);
|
|
36
36
|
|
|
37
37
|
if (!Array.isArray(rows)) {
|
|
38
|
-
|
|
39
|
-
return [{
|
|
38
|
+
const header = rows as Record<string, unknown>;
|
|
39
|
+
return [{
|
|
40
|
+
columns: [],
|
|
41
|
+
values: [],
|
|
42
|
+
meta: {
|
|
43
|
+
insertId: header.insertId as number | undefined,
|
|
44
|
+
rowsAffected: header.affectedRows as number | undefined,
|
|
45
|
+
}
|
|
46
|
+
}];
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
const result = rowsToQueryResult(
|