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
package/dist/index.js
CHANGED
|
@@ -245,6 +245,7 @@ var belongsToMany = (target, pivotTable, options) => ({
|
|
|
245
245
|
|
|
246
246
|
// src/core/ast/expression-nodes.ts
|
|
247
247
|
var operandTypes = /* @__PURE__ */ new Set([
|
|
248
|
+
"AliasRef",
|
|
248
249
|
"Column",
|
|
249
250
|
"Literal",
|
|
250
251
|
"Function",
|
|
@@ -286,11 +287,21 @@ var toOperand = (val) => {
|
|
|
286
287
|
}
|
|
287
288
|
return toNode(val);
|
|
288
289
|
};
|
|
290
|
+
var hasQueryAst = (value) => typeof value.getAST === "function";
|
|
291
|
+
var resolveSelectQueryNode = (query) => hasQueryAst(query) ? query.getAST() : query;
|
|
292
|
+
var toScalarSubqueryNode = (query) => ({
|
|
293
|
+
type: "ScalarSubquery",
|
|
294
|
+
query: resolveSelectQueryNode(query)
|
|
295
|
+
});
|
|
289
296
|
var columnOperand = (col2) => toNode(col2);
|
|
290
297
|
var outerRef = (col2) => ({
|
|
291
298
|
...columnOperand(col2),
|
|
292
299
|
scope: "outer"
|
|
293
300
|
});
|
|
301
|
+
var aliasRef = (name) => ({
|
|
302
|
+
type: "AliasRef",
|
|
303
|
+
name
|
|
304
|
+
});
|
|
294
305
|
var correlateBy = (table, column) => outerRef({ name: column, table });
|
|
295
306
|
var createBinaryExpression = (operator, left2, right2, escape) => {
|
|
296
307
|
const node = {
|
|
@@ -332,14 +343,16 @@ var isNotNull = (left2) => ({
|
|
|
332
343
|
left: toNode(left2),
|
|
333
344
|
operator: "IS NOT NULL"
|
|
334
345
|
});
|
|
335
|
-
var createInExpression = (operator, left2,
|
|
346
|
+
var createInExpression = (operator, left2, right2) => ({
|
|
336
347
|
type: "InExpression",
|
|
337
348
|
left: toNode(left2),
|
|
338
349
|
operator,
|
|
339
|
-
right:
|
|
350
|
+
right: right2
|
|
340
351
|
});
|
|
341
|
-
var inList = (left2, values) => createInExpression("IN", left2, values);
|
|
342
|
-
var notInList = (left2, values) => createInExpression("NOT IN", left2, values);
|
|
352
|
+
var inList = (left2, values) => createInExpression("IN", left2, values.map((v) => toOperand(v)));
|
|
353
|
+
var notInList = (left2, values) => createInExpression("NOT IN", left2, values.map((v) => toOperand(v)));
|
|
354
|
+
var inSubquery = (left2, subquery) => createInExpression("IN", left2, toScalarSubqueryNode(subquery));
|
|
355
|
+
var notInSubquery = (left2, subquery) => createInExpression("NOT IN", left2, toScalarSubqueryNode(subquery));
|
|
343
356
|
var createBetweenExpression = (operator, left2, lower2, upper2) => ({
|
|
344
357
|
type: "BetweenExpression",
|
|
345
358
|
left: toNode(left2),
|
|
@@ -349,6 +362,16 @@ var createBetweenExpression = (operator, left2, lower2, upper2) => ({
|
|
|
349
362
|
});
|
|
350
363
|
var between = (left2, lower2, upper2) => createBetweenExpression("BETWEEN", left2, lower2, upper2);
|
|
351
364
|
var notBetween = (left2, lower2, upper2) => createBetweenExpression("NOT BETWEEN", left2, lower2, upper2);
|
|
365
|
+
var createArithmeticExpression = (operator, left2, right2) => ({
|
|
366
|
+
type: "ArithmeticExpression",
|
|
367
|
+
left: toOperand(left2),
|
|
368
|
+
operator,
|
|
369
|
+
right: toOperand(right2)
|
|
370
|
+
});
|
|
371
|
+
var add = (left2, right2) => createArithmeticExpression("+", left2, right2);
|
|
372
|
+
var sub = (left2, right2) => createArithmeticExpression("-", left2, right2);
|
|
373
|
+
var mul = (left2, right2) => createArithmeticExpression("*", left2, right2);
|
|
374
|
+
var div = (left2, right2) => createArithmeticExpression("/", left2, right2);
|
|
352
375
|
var jsonPath = (col2, path) => ({
|
|
353
376
|
type: "JsonPath",
|
|
354
377
|
column: columnOperand(col2),
|
|
@@ -427,7 +450,7 @@ var windowFunction = (name, args = [], partitionBy, orderBy) => {
|
|
|
427
450
|
const partitionNodes = partitionBy?.map((col2) => columnOperand(col2)) ?? void 0;
|
|
428
451
|
const orderNodes = orderBy?.map((o) => ({
|
|
429
452
|
type: "OrderBy",
|
|
430
|
-
|
|
453
|
+
term: columnOperand(o.column),
|
|
431
454
|
direction: o.direction
|
|
432
455
|
}));
|
|
433
456
|
return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
|
|
@@ -502,7 +525,7 @@ var min = buildAggregate("MIN");
|
|
|
502
525
|
var max = buildAggregate("MAX");
|
|
503
526
|
var toOrderByNode = (order) => ({
|
|
504
527
|
type: "OrderBy",
|
|
505
|
-
|
|
528
|
+
term: columnOperand(order.column),
|
|
506
529
|
direction: order.direction ?? ORDER_DIRECTIONS.ASC
|
|
507
530
|
});
|
|
508
531
|
var groupConcat = (col2, options) => ({
|
|
@@ -552,6 +575,9 @@ var visitExpression = (node, visitor) => {
|
|
|
552
575
|
case "BetweenExpression":
|
|
553
576
|
if (visitor.visitBetweenExpression) return visitor.visitBetweenExpression(node);
|
|
554
577
|
break;
|
|
578
|
+
case "ArithmeticExpression":
|
|
579
|
+
if (visitor.visitArithmeticExpression) return visitor.visitArithmeticExpression(node);
|
|
580
|
+
break;
|
|
555
581
|
default:
|
|
556
582
|
break;
|
|
557
583
|
}
|
|
@@ -583,6 +609,9 @@ var visitOperand = (node, visitor) => {
|
|
|
583
609
|
case "WindowFunction":
|
|
584
610
|
if (visitor.visitWindowFunction) return visitor.visitWindowFunction(node);
|
|
585
611
|
break;
|
|
612
|
+
case "AliasRef":
|
|
613
|
+
if (visitor.visitAliasRef) return visitor.visitAliasRef(node);
|
|
614
|
+
break;
|
|
586
615
|
default:
|
|
587
616
|
break;
|
|
588
617
|
}
|
|
@@ -699,7 +728,14 @@ var StandardFunctionStrategy = class _StandardFunctionStrategy {
|
|
|
699
728
|
if (!orderBy || orderBy.length === 0) {
|
|
700
729
|
return "";
|
|
701
730
|
}
|
|
702
|
-
const parts = orderBy.map((order) =>
|
|
731
|
+
const parts = orderBy.map((order) => {
|
|
732
|
+
const term = isOperandNode(order.term) ? ctx.compileOperand(order.term) : (() => {
|
|
733
|
+
throw new Error("ORDER BY expressions inside functions must be operands");
|
|
734
|
+
})();
|
|
735
|
+
const collation = order.collation ? ` COLLATE ${order.collation}` : "";
|
|
736
|
+
const nulls = order.nulls ? ` NULLS ${order.nulls}` : "";
|
|
737
|
+
return `${term} ${order.direction}${collation}${nulls}`;
|
|
738
|
+
});
|
|
703
739
|
return `ORDER BY ${parts.join(", ")}`;
|
|
704
740
|
}
|
|
705
741
|
formatGroupConcatSeparator(ctx) {
|
|
@@ -964,6 +1000,16 @@ var Dialect = class _Dialect {
|
|
|
964
1000
|
}
|
|
965
1001
|
return compiler(node, ctx);
|
|
966
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Compiles an ordering term (operand, expression, or alias reference).
|
|
1005
|
+
*/
|
|
1006
|
+
compileOrderingTerm(term, ctx) {
|
|
1007
|
+
if (isOperandNode(term)) {
|
|
1008
|
+
return this.compileOperand(term, ctx);
|
|
1009
|
+
}
|
|
1010
|
+
const expr = this.compileExpression(term, ctx);
|
|
1011
|
+
return `(${expr})`;
|
|
1012
|
+
}
|
|
967
1013
|
registerDefaultExpressionCompilers() {
|
|
968
1014
|
this.registerExpressionCompiler("BinaryExpression", (binary, ctx) => {
|
|
969
1015
|
const left2 = this.compileOperand(binary.left, ctx);
|
|
@@ -989,8 +1035,12 @@ var Dialect = class _Dialect {
|
|
|
989
1035
|
});
|
|
990
1036
|
this.registerExpressionCompiler("InExpression", (inExpr, ctx) => {
|
|
991
1037
|
const left2 = this.compileOperand(inExpr.left, ctx);
|
|
992
|
-
|
|
993
|
-
|
|
1038
|
+
if (Array.isArray(inExpr.right)) {
|
|
1039
|
+
const values = inExpr.right.map((v) => this.compileOperand(v, ctx)).join(", ");
|
|
1040
|
+
return `${left2} ${inExpr.operator} (${values})`;
|
|
1041
|
+
}
|
|
1042
|
+
const subquerySql = this.compileSelectAst(inExpr.right.query, ctx).trim().replace(/;$/, "");
|
|
1043
|
+
return `${left2} ${inExpr.operator} (${subquerySql})`;
|
|
994
1044
|
});
|
|
995
1045
|
this.registerExpressionCompiler("ExistsExpression", (existsExpr, ctx) => {
|
|
996
1046
|
const subquerySql = this.compileSelectForExists(existsExpr.subquery, ctx);
|
|
@@ -1002,9 +1052,15 @@ var Dialect = class _Dialect {
|
|
|
1002
1052
|
const upper2 = this.compileOperand(betweenExpr.upper, ctx);
|
|
1003
1053
|
return `${left2} ${betweenExpr.operator} ${lower2} AND ${upper2}`;
|
|
1004
1054
|
});
|
|
1055
|
+
this.registerExpressionCompiler("ArithmeticExpression", (arith, ctx) => {
|
|
1056
|
+
const left2 = this.compileOperand(arith.left, ctx);
|
|
1057
|
+
const right2 = this.compileOperand(arith.right, ctx);
|
|
1058
|
+
return `${left2} ${arith.operator} ${right2}`;
|
|
1059
|
+
});
|
|
1005
1060
|
}
|
|
1006
1061
|
registerDefaultOperandCompilers() {
|
|
1007
1062
|
this.registerOperandCompiler("Literal", (literal, ctx) => ctx.addParameter(literal.value));
|
|
1063
|
+
this.registerOperandCompiler("AliasRef", (alias, _ctx) => this.quoteIdentifier(alias.name));
|
|
1008
1064
|
this.registerOperandCompiler("Column", (column, _ctx) => {
|
|
1009
1065
|
return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
|
|
1010
1066
|
});
|
|
@@ -1042,9 +1098,12 @@ var Dialect = class _Dialect {
|
|
|
1042
1098
|
parts.push(partitionClause);
|
|
1043
1099
|
}
|
|
1044
1100
|
if (node.orderBy && node.orderBy.length > 0) {
|
|
1045
|
-
const orderClause = "ORDER BY " + node.orderBy.map(
|
|
1046
|
-
|
|
1047
|
-
|
|
1101
|
+
const orderClause = "ORDER BY " + node.orderBy.map((o) => {
|
|
1102
|
+
const term = this.compileOrderingTerm(o.term, ctx);
|
|
1103
|
+
const collation = o.collation ? ` COLLATE ${o.collation}` : "";
|
|
1104
|
+
const nulls = o.nulls ? ` NULLS ${o.nulls}` : "";
|
|
1105
|
+
return `${term} ${o.direction}${collation}${nulls}`;
|
|
1106
|
+
}).join(", ");
|
|
1048
1107
|
parts.push(orderClause);
|
|
1049
1108
|
}
|
|
1050
1109
|
result += parts.join(" ");
|
|
@@ -1239,17 +1298,9 @@ var NoReturningStrategy = class {
|
|
|
1239
1298
|
|
|
1240
1299
|
// src/core/dialect/base/join-compiler.ts
|
|
1241
1300
|
var JoinCompiler = class {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
* @param ctx - The compiler context for expression compilation.
|
|
1246
|
-
* @param compileFrom - Function to compile table sources (tables or subqueries).
|
|
1247
|
-
* @param compileExpression - Function to compile join condition expressions.
|
|
1248
|
-
* @returns SQL JOIN clauses (e.g., " LEFT JOIN table ON condition") or empty string if no joins.
|
|
1249
|
-
*/
|
|
1250
|
-
static compileJoins(ast, ctx, compileFrom, compileExpression) {
|
|
1251
|
-
if (!ast.joins || ast.joins.length === 0) return "";
|
|
1252
|
-
const parts = ast.joins.map((j) => {
|
|
1301
|
+
static compileJoins(joins, ctx, compileFrom, compileExpression) {
|
|
1302
|
+
if (!joins || joins.length === 0) return "";
|
|
1303
|
+
const parts = joins.map((j) => {
|
|
1253
1304
|
const table = compileFrom(j.table, ctx);
|
|
1254
1305
|
const cond = compileExpression(j.condition, ctx);
|
|
1255
1306
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
@@ -1263,12 +1314,12 @@ var GroupByCompiler = class {
|
|
|
1263
1314
|
/**
|
|
1264
1315
|
* Compiles GROUP BY clause from a SELECT query AST.
|
|
1265
1316
|
* @param ast - The SELECT query AST containing grouping columns.
|
|
1266
|
-
* @param
|
|
1317
|
+
* @param renderTerm - Function to render a grouping term.
|
|
1267
1318
|
* @returns SQL GROUP BY clause (e.g., " GROUP BY table.col1, table.col2") or empty string if no grouping.
|
|
1268
1319
|
*/
|
|
1269
|
-
static compileGroupBy(ast,
|
|
1320
|
+
static compileGroupBy(ast, renderTerm) {
|
|
1270
1321
|
if (!ast.groupBy || ast.groupBy.length === 0) return "";
|
|
1271
|
-
const cols = ast.groupBy.map(
|
|
1322
|
+
const cols = ast.groupBy.map(renderTerm).join(", ");
|
|
1272
1323
|
return ` GROUP BY ${cols}`;
|
|
1273
1324
|
}
|
|
1274
1325
|
};
|
|
@@ -1278,12 +1329,19 @@ var OrderByCompiler = class {
|
|
|
1278
1329
|
/**
|
|
1279
1330
|
* Compiles ORDER BY clause from a SELECT query AST.
|
|
1280
1331
|
* @param ast - The SELECT query AST containing sort specifications.
|
|
1281
|
-
* @param
|
|
1332
|
+
* @param renderTerm - Function to render an ordering term.
|
|
1333
|
+
* @param renderNulls - Optional function to render NULLS FIRST/LAST.
|
|
1334
|
+
* @param renderCollation - Optional function to render COLLATE clause.
|
|
1282
1335
|
* @returns SQL ORDER BY clause (e.g., " ORDER BY table.col1 ASC, table.col2 DESC") or empty string if no ordering.
|
|
1283
1336
|
*/
|
|
1284
|
-
static compileOrderBy(ast,
|
|
1337
|
+
static compileOrderBy(ast, renderTerm, renderNulls, renderCollation) {
|
|
1285
1338
|
if (!ast.orderBy || ast.orderBy.length === 0) return "";
|
|
1286
|
-
const parts = ast.orderBy.map((o) =>
|
|
1339
|
+
const parts = ast.orderBy.map((o) => {
|
|
1340
|
+
const term = renderTerm(o.term);
|
|
1341
|
+
const collation = renderCollation ? renderCollation(o) : o.collation ? ` COLLATE ${o.collation}` : "";
|
|
1342
|
+
const nulls = renderNulls ? renderNulls(o) : o.nulls ? ` NULLS ${o.nulls}` : "";
|
|
1343
|
+
return `${term} ${o.direction}${collation}${nulls}`;
|
|
1344
|
+
}).join(", ");
|
|
1287
1345
|
return ` ORDER BY ${parts}`;
|
|
1288
1346
|
}
|
|
1289
1347
|
};
|
|
@@ -1314,58 +1372,96 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1314
1372
|
}
|
|
1315
1373
|
compileSelectWithSetOps(ast, baseSelect, ctes, ctx) {
|
|
1316
1374
|
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
1317
|
-
const orderBy = OrderByCompiler.compileOrderBy(
|
|
1375
|
+
const orderBy = OrderByCompiler.compileOrderBy(
|
|
1376
|
+
ast,
|
|
1377
|
+
(term) => this.compileOrderingTerm(term, ctx),
|
|
1378
|
+
this.renderOrderByNulls.bind(this),
|
|
1379
|
+
this.renderOrderByCollation.bind(this)
|
|
1380
|
+
);
|
|
1318
1381
|
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
1319
1382
|
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
1320
1383
|
return `${ctes}${combined}${orderBy}${pagination}`;
|
|
1321
1384
|
}
|
|
1322
1385
|
compileInsertAst(ast, ctx) {
|
|
1386
|
+
if (!ast.columns.length) {
|
|
1387
|
+
throw new Error("INSERT queries must specify columns.");
|
|
1388
|
+
}
|
|
1323
1389
|
const table = this.compileTableName(ast.into);
|
|
1324
1390
|
const columnList = this.compileInsertColumnList(ast.columns);
|
|
1325
|
-
const
|
|
1391
|
+
const source = this.compileInsertSource(ast.source, ctx);
|
|
1326
1392
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1327
|
-
return `INSERT INTO ${table} (${columnList})
|
|
1393
|
+
return `INSERT INTO ${table} (${columnList}) ${source}${returning}`;
|
|
1328
1394
|
}
|
|
1329
1395
|
compileReturning(returning, ctx) {
|
|
1330
1396
|
return this.returningStrategy.compileReturning(returning, ctx);
|
|
1331
1397
|
}
|
|
1398
|
+
compileInsertSource(source, ctx) {
|
|
1399
|
+
if (source.type === "InsertValues") {
|
|
1400
|
+
if (!source.rows.length) {
|
|
1401
|
+
throw new Error("INSERT ... VALUES requires at least one row.");
|
|
1402
|
+
}
|
|
1403
|
+
const values = source.rows.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
1404
|
+
return `VALUES ${values}`;
|
|
1405
|
+
}
|
|
1406
|
+
const normalized = this.normalizeSelectAst(source.query);
|
|
1407
|
+
return this.compileSelectAst(normalized, ctx).trim();
|
|
1408
|
+
}
|
|
1332
1409
|
compileInsertColumnList(columns) {
|
|
1333
1410
|
return columns.map((column) => this.quoteIdentifier(column.name)).join(", ");
|
|
1334
1411
|
}
|
|
1335
|
-
compileInsertValues(values, ctx) {
|
|
1336
|
-
return values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
1337
|
-
}
|
|
1338
1412
|
compileSelectCore(ast, ctx) {
|
|
1339
1413
|
const columns = this.compileSelectColumns(ast, ctx);
|
|
1340
1414
|
const from = this.compileFrom(ast.from, ctx);
|
|
1341
|
-
const joins = JoinCompiler.compileJoins(
|
|
1415
|
+
const joins = JoinCompiler.compileJoins(
|
|
1416
|
+
ast.joins,
|
|
1417
|
+
ctx,
|
|
1418
|
+
this.compileFrom.bind(this),
|
|
1419
|
+
this.compileExpression.bind(this)
|
|
1420
|
+
);
|
|
1342
1421
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1343
|
-
const groupBy = GroupByCompiler.compileGroupBy(ast, this.
|
|
1422
|
+
const groupBy = GroupByCompiler.compileGroupBy(ast, (term) => this.compileOrderingTerm(term, ctx));
|
|
1344
1423
|
const having = this.compileHaving(ast, ctx);
|
|
1345
|
-
const orderBy = OrderByCompiler.compileOrderBy(
|
|
1424
|
+
const orderBy = OrderByCompiler.compileOrderBy(
|
|
1425
|
+
ast,
|
|
1426
|
+
(term) => this.compileOrderingTerm(term, ctx),
|
|
1427
|
+
this.renderOrderByNulls.bind(this),
|
|
1428
|
+
this.renderOrderByCollation.bind(this)
|
|
1429
|
+
);
|
|
1346
1430
|
const pagination = this.paginationStrategy.compilePagination(ast.limit, ast.offset);
|
|
1347
1431
|
return `SELECT ${this.compileDistinct(ast)}${columns} FROM ${from}${joins}${whereClause}${groupBy}${having}${orderBy}${pagination}`;
|
|
1348
1432
|
}
|
|
1349
1433
|
compileUpdateAst(ast, ctx) {
|
|
1350
|
-
const
|
|
1351
|
-
const assignments = this.compileUpdateAssignments(ast.set, ctx);
|
|
1434
|
+
const target = this.compileTableReference(ast.table);
|
|
1435
|
+
const assignments = this.compileUpdateAssignments(ast.set, ast.table, ctx);
|
|
1436
|
+
const fromClause = this.compileUpdateFromClause(ast, ctx);
|
|
1352
1437
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1353
1438
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1354
|
-
return `UPDATE ${
|
|
1439
|
+
return `UPDATE ${target} SET ${assignments}${fromClause}${whereClause}${returning}`;
|
|
1355
1440
|
}
|
|
1356
|
-
compileUpdateAssignments(assignments, ctx) {
|
|
1441
|
+
compileUpdateAssignments(assignments, table, ctx) {
|
|
1357
1442
|
return assignments.map((assignment) => {
|
|
1358
1443
|
const col2 = assignment.column;
|
|
1359
|
-
const target = this.
|
|
1444
|
+
const target = this.compileQualifiedColumn(col2, table);
|
|
1360
1445
|
const value = this.compileOperand(assignment.value, ctx);
|
|
1361
1446
|
return `${target} = ${value}`;
|
|
1362
1447
|
}).join(", ");
|
|
1363
1448
|
}
|
|
1449
|
+
compileQualifiedColumn(column, table) {
|
|
1450
|
+
const baseTableName = table.name;
|
|
1451
|
+
const alias = table.alias;
|
|
1452
|
+
const columnTable = column.table ?? alias ?? baseTableName;
|
|
1453
|
+
const tableQualifier = alias && column.table === baseTableName ? alias : columnTable;
|
|
1454
|
+
if (!tableQualifier) {
|
|
1455
|
+
return this.quoteIdentifier(column.name);
|
|
1456
|
+
}
|
|
1457
|
+
return `${this.quoteIdentifier(tableQualifier)}.${this.quoteIdentifier(column.name)}`;
|
|
1458
|
+
}
|
|
1364
1459
|
compileDeleteAst(ast, ctx) {
|
|
1365
|
-
const
|
|
1460
|
+
const target = this.compileTableReference(ast.from);
|
|
1461
|
+
const usingClause = this.compileDeleteUsingClause(ast, ctx);
|
|
1366
1462
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1367
1463
|
const returning = this.compileReturning(ast.returning, ctx);
|
|
1368
|
-
return `DELETE FROM ${
|
|
1464
|
+
return `DELETE FROM ${target}${usingClause}${whereClause}${returning}`;
|
|
1369
1465
|
}
|
|
1370
1466
|
formatReturningColumns(returning) {
|
|
1371
1467
|
return this.returningStrategy.formatReturningColumns(returning, this.quoteIdentifier.bind(this));
|
|
@@ -1420,6 +1516,38 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1420
1516
|
}
|
|
1421
1517
|
return this.quoteIdentifier(table.name);
|
|
1422
1518
|
}
|
|
1519
|
+
compileTableReference(table) {
|
|
1520
|
+
const base = this.compileTableName(table);
|
|
1521
|
+
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
1522
|
+
}
|
|
1523
|
+
compileUpdateFromClause(ast, ctx) {
|
|
1524
|
+
if (!ast.from && (!ast.joins || ast.joins.length === 0)) return "";
|
|
1525
|
+
if (!ast.from) {
|
|
1526
|
+
throw new Error("UPDATE with JOINs requires an explicit FROM clause.");
|
|
1527
|
+
}
|
|
1528
|
+
const from = this.compileFrom(ast.from, ctx);
|
|
1529
|
+
const joins = JoinCompiler.compileJoins(
|
|
1530
|
+
ast.joins,
|
|
1531
|
+
ctx,
|
|
1532
|
+
this.compileFrom.bind(this),
|
|
1533
|
+
this.compileExpression.bind(this)
|
|
1534
|
+
);
|
|
1535
|
+
return ` FROM ${from}${joins}`;
|
|
1536
|
+
}
|
|
1537
|
+
compileDeleteUsingClause(ast, ctx) {
|
|
1538
|
+
if (!ast.using && (!ast.joins || ast.joins.length === 0)) return "";
|
|
1539
|
+
if (!ast.using) {
|
|
1540
|
+
throw new Error("DELETE with JOINs requires a USING clause.");
|
|
1541
|
+
}
|
|
1542
|
+
const usingTable = this.compileFrom(ast.using, ctx);
|
|
1543
|
+
const joins = JoinCompiler.compileJoins(
|
|
1544
|
+
ast.joins,
|
|
1545
|
+
ctx,
|
|
1546
|
+
this.compileFrom.bind(this),
|
|
1547
|
+
this.compileExpression.bind(this)
|
|
1548
|
+
);
|
|
1549
|
+
return ` USING ${usingTable}${joins}`;
|
|
1550
|
+
}
|
|
1423
1551
|
compileHaving(ast, ctx) {
|
|
1424
1552
|
if (!ast.having) return "";
|
|
1425
1553
|
return ` HAVING ${this.compileExpression(ast.having, ctx)}`;
|
|
@@ -1431,6 +1559,12 @@ var SqlDialectBase = class extends Dialect {
|
|
|
1431
1559
|
const trimmed = this.stripTrailingSemicolon(sql);
|
|
1432
1560
|
return `(${trimmed})`;
|
|
1433
1561
|
}
|
|
1562
|
+
renderOrderByNulls(order) {
|
|
1563
|
+
return order.nulls ? ` NULLS ${order.nulls}` : "";
|
|
1564
|
+
}
|
|
1565
|
+
renderOrderByCollation(order) {
|
|
1566
|
+
return order.collation ? ` COLLATE ${order.collation}` : "";
|
|
1567
|
+
}
|
|
1434
1568
|
};
|
|
1435
1569
|
|
|
1436
1570
|
// src/core/dialect/postgres/functions.ts
|
|
@@ -1794,6 +1928,9 @@ var SqliteDialect = class extends SqlDialectBase {
|
|
|
1794
1928
|
const col2 = `${this.quoteIdentifier(node.column.table)}.${this.quoteIdentifier(node.column.name)}`;
|
|
1795
1929
|
return `json_extract(${col2}, '${node.path}')`;
|
|
1796
1930
|
}
|
|
1931
|
+
compileQualifiedColumn(column, _table) {
|
|
1932
|
+
return this.quoteIdentifier(column.name);
|
|
1933
|
+
}
|
|
1797
1934
|
compileReturning(returning, ctx) {
|
|
1798
1935
|
if (!returning || returning.length === 0) return "";
|
|
1799
1936
|
const columns = this.formatReturningColumns(returning);
|
|
@@ -1899,7 +2036,7 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
|
|
|
1899
2036
|
};
|
|
1900
2037
|
|
|
1901
2038
|
// src/core/dialect/mssql/index.ts
|
|
1902
|
-
var SqlServerDialect = class extends
|
|
2039
|
+
var SqlServerDialect = class extends SqlDialectBase {
|
|
1903
2040
|
/**
|
|
1904
2041
|
* Creates a new SqlServerDialect instance
|
|
1905
2042
|
*/
|
|
@@ -1942,43 +2079,37 @@ var SqlServerDialect = class extends Dialect {
|
|
|
1942
2079
|
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
1943
2080
|
const ctes = this.compileCtes(ast, ctx);
|
|
1944
2081
|
const baseAst = hasSetOps ? { ...ast, setOps: void 0, orderBy: void 0, limit: void 0, offset: void 0 } : ast;
|
|
1945
|
-
const baseSelect = this.
|
|
2082
|
+
const baseSelect = this.compileSelectCoreForMssql(baseAst, ctx);
|
|
1946
2083
|
if (!hasSetOps) {
|
|
1947
2084
|
return `${ctes}${baseSelect}`;
|
|
1948
2085
|
}
|
|
1949
2086
|
const compound = ast.setOps.map((op) => `${op.operator} ${this.wrapSetOperand(this.compileSelectAst(op.query, ctx))}`).join(" ");
|
|
1950
|
-
const orderBy = this.compileOrderBy(ast);
|
|
2087
|
+
const orderBy = this.compileOrderBy(ast, ctx);
|
|
1951
2088
|
const pagination = this.compilePagination(ast, orderBy);
|
|
1952
2089
|
const combined = `${this.wrapSetOperand(baseSelect)} ${compound}`;
|
|
1953
2090
|
const tail = pagination || orderBy;
|
|
1954
2091
|
return `${ctes}${combined}${tail}`;
|
|
1955
2092
|
}
|
|
1956
|
-
compileInsertAst(ast, ctx) {
|
|
1957
|
-
const table = this.quoteIdentifier(ast.into.name);
|
|
1958
|
-
const columnList = ast.columns.map((column) => `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`).join(", ");
|
|
1959
|
-
const values = ast.values.map((row) => `(${row.map((value) => this.compileOperand(value, ctx)).join(", ")})`).join(", ");
|
|
1960
|
-
return `INSERT INTO ${table} (${columnList}) VALUES ${values};`;
|
|
1961
|
-
}
|
|
1962
|
-
compileUpdateAst(ast, ctx) {
|
|
1963
|
-
const table = this.quoteIdentifier(ast.table.name);
|
|
1964
|
-
const assignments = ast.set.map((assignment) => {
|
|
1965
|
-
const col2 = assignment.column;
|
|
1966
|
-
const target = `${this.quoteIdentifier(col2.table)}.${this.quoteIdentifier(col2.name)}`;
|
|
1967
|
-
const value = this.compileOperand(assignment.value, ctx);
|
|
1968
|
-
return `${target} = ${value}`;
|
|
1969
|
-
}).join(", ");
|
|
1970
|
-
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1971
|
-
return `UPDATE ${table} SET ${assignments}${whereClause};`;
|
|
1972
|
-
}
|
|
1973
2093
|
compileDeleteAst(ast, ctx) {
|
|
2094
|
+
if (ast.using) {
|
|
2095
|
+
throw new Error("DELETE ... USING is not supported in the MSSQL dialect; use join() instead.");
|
|
2096
|
+
}
|
|
1974
2097
|
if (ast.from.type !== "Table") {
|
|
1975
2098
|
throw new Error("DELETE only supports base tables in the MSSQL dialect.");
|
|
1976
2099
|
}
|
|
1977
|
-
const
|
|
2100
|
+
const alias = ast.from.alias ?? ast.from.name;
|
|
2101
|
+
const target = this.compileTableReference(ast.from);
|
|
2102
|
+
const joins = JoinCompiler.compileJoins(
|
|
2103
|
+
ast.joins,
|
|
2104
|
+
ctx,
|
|
2105
|
+
this.compileFrom.bind(this),
|
|
2106
|
+
this.compileExpression.bind(this)
|
|
2107
|
+
);
|
|
1978
2108
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
1979
|
-
|
|
2109
|
+
const returning = this.compileReturning(ast.returning, ctx);
|
|
2110
|
+
return `DELETE ${this.quoteIdentifier(alias)} FROM ${target}${joins}${whereClause}${returning}`;
|
|
1980
2111
|
}
|
|
1981
|
-
|
|
2112
|
+
compileSelectCoreForMssql(ast, ctx) {
|
|
1982
2113
|
const columns = ast.columns.map((c) => {
|
|
1983
2114
|
let expr = "";
|
|
1984
2115
|
if (c.type === "Function") {
|
|
@@ -1997,25 +2128,29 @@ var SqlServerDialect = class extends Dialect {
|
|
|
1997
2128
|
return expr;
|
|
1998
2129
|
}).join(", ");
|
|
1999
2130
|
const distinct = ast.distinct ? "DISTINCT " : "";
|
|
2000
|
-
const from = this.compileTableSource(ast.from
|
|
2131
|
+
const from = this.compileTableSource(ast.from);
|
|
2001
2132
|
const joins = ast.joins.map((j) => {
|
|
2002
|
-
const table = this.compileTableSource(j.table
|
|
2133
|
+
const table = this.compileTableSource(j.table);
|
|
2003
2134
|
const cond = this.compileExpression(j.condition, ctx);
|
|
2004
2135
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
2005
2136
|
}).join(" ");
|
|
2006
2137
|
const whereClause = this.compileWhere(ast.where, ctx);
|
|
2007
|
-
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((
|
|
2138
|
+
const groupBy = ast.groupBy && ast.groupBy.length > 0 ? " GROUP BY " + ast.groupBy.map((term) => this.compileOrderingTerm(term, ctx)).join(", ") : "";
|
|
2008
2139
|
const having = ast.having ? ` HAVING ${this.compileExpression(ast.having, ctx)}` : "";
|
|
2009
|
-
const orderBy = this.compileOrderBy(ast);
|
|
2140
|
+
const orderBy = this.compileOrderBy(ast, ctx);
|
|
2010
2141
|
const pagination = this.compilePagination(ast, orderBy);
|
|
2011
2142
|
if (pagination) {
|
|
2012
2143
|
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${pagination}`;
|
|
2013
2144
|
}
|
|
2014
2145
|
return `SELECT ${distinct}${columns} FROM ${from}${joins ? " " + joins : ""}${whereClause}${groupBy}${having}${orderBy}`;
|
|
2015
2146
|
}
|
|
2016
|
-
compileOrderBy(ast) {
|
|
2017
|
-
|
|
2018
|
-
|
|
2147
|
+
compileOrderBy(ast, ctx) {
|
|
2148
|
+
return OrderByCompiler.compileOrderBy(
|
|
2149
|
+
ast,
|
|
2150
|
+
(term) => this.compileOrderingTerm(term, ctx),
|
|
2151
|
+
this.renderOrderByNulls.bind(this),
|
|
2152
|
+
this.renderOrderByCollation.bind(this)
|
|
2153
|
+
);
|
|
2019
2154
|
}
|
|
2020
2155
|
compilePagination(ast, orderBy) {
|
|
2021
2156
|
const hasLimit = ast.limit !== void 0;
|
|
@@ -2029,21 +2164,6 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2029
2164
|
}
|
|
2030
2165
|
return pagination;
|
|
2031
2166
|
}
|
|
2032
|
-
compileTableSource(table, ctx) {
|
|
2033
|
-
if (table.type === "FunctionTable") {
|
|
2034
|
-
return FunctionTableFormatter.format(table, ctx, this);
|
|
2035
|
-
}
|
|
2036
|
-
if (table.type === "DerivedTable") {
|
|
2037
|
-
return this.compileDerivedTable(table, ctx);
|
|
2038
|
-
}
|
|
2039
|
-
const base = table.schema ? `${this.quoteIdentifier(table.schema)}.${this.quoteIdentifier(table.name)}` : this.quoteIdentifier(table.name);
|
|
2040
|
-
return table.alias ? `${base} AS ${this.quoteIdentifier(table.alias)}` : base;
|
|
2041
|
-
}
|
|
2042
|
-
compileDerivedTable(table, ctx) {
|
|
2043
|
-
const sub = this.compileSelectAst(this.normalizeSelectAst(table.query), ctx).trim().replace(/;$/, "");
|
|
2044
|
-
const cols = table.columnAliases?.length ? ` (${table.columnAliases.map((c) => this.quoteIdentifier(c)).join(", ")})` : "";
|
|
2045
|
-
return `(${sub}) AS ${this.quoteIdentifier(table.alias)}${cols}`;
|
|
2046
|
-
}
|
|
2047
2167
|
compileCtes(ast, ctx) {
|
|
2048
2168
|
if (!ast.ctes || ast.ctes.length === 0) return "";
|
|
2049
2169
|
const defs = ast.ctes.map((cte) => {
|
|
@@ -2054,10 +2174,6 @@ var SqlServerDialect = class extends Dialect {
|
|
|
2054
2174
|
}).join(", ");
|
|
2055
2175
|
return `WITH ${defs} `;
|
|
2056
2176
|
}
|
|
2057
|
-
wrapSetOperand(sql) {
|
|
2058
|
-
const trimmed = sql.trim().replace(/;$/, "");
|
|
2059
|
-
return `(${trimmed})`;
|
|
2060
|
-
}
|
|
2061
2177
|
};
|
|
2062
2178
|
|
|
2063
2179
|
// src/core/dialect/dialect-factory.ts
|
|
@@ -2207,7 +2323,7 @@ var SelectQueryState = class _SelectQueryState {
|
|
|
2207
2323
|
}
|
|
2208
2324
|
/**
|
|
2209
2325
|
* Adds GROUP BY columns to the query
|
|
2210
|
-
* @param columns -
|
|
2326
|
+
* @param columns - Terms to group by
|
|
2211
2327
|
* @returns New SelectQueryState with GROUP BY clause
|
|
2212
2328
|
*/
|
|
2213
2329
|
withGroupBy(columns) {
|
|
@@ -2490,31 +2606,38 @@ var HydrationManager = class _HydrationManager {
|
|
|
2490
2606
|
}
|
|
2491
2607
|
const mapped = [];
|
|
2492
2608
|
for (const ob of orderBy) {
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
}
|
|
2496
|
-
const alias = projectionAliases.get(`${ob.column.table}.${ob.column.name}`) ?? ob.column.name;
|
|
2497
|
-
if (!availableColumns.has(alias)) {
|
|
2498
|
-
return null;
|
|
2499
|
-
}
|
|
2500
|
-
mapped.push({
|
|
2501
|
-
type: "OrderBy",
|
|
2502
|
-
column: { type: "Column", table: baseAlias, name: alias },
|
|
2503
|
-
direction: ob.direction
|
|
2504
|
-
});
|
|
2609
|
+
const mappedTerm = this.mapOrderingTerm(ob.term, plan, projectionAliases, baseAlias, availableColumns);
|
|
2610
|
+
if (!mappedTerm) return null;
|
|
2611
|
+
mapped.push({ ...ob, term: mappedTerm });
|
|
2505
2612
|
}
|
|
2506
2613
|
return mapped;
|
|
2507
2614
|
}
|
|
2615
|
+
mapOrderingTerm(term, plan, projectionAliases, baseAlias, availableColumns) {
|
|
2616
|
+
if (term.type === "Column") {
|
|
2617
|
+
const col2 = term;
|
|
2618
|
+
if (col2.table !== plan.rootTable) return null;
|
|
2619
|
+
const alias = projectionAliases.get(`${col2.table}.${col2.name}`) ?? col2.name;
|
|
2620
|
+
if (!availableColumns.has(alias)) return null;
|
|
2621
|
+
return { type: "Column", table: baseAlias, name: alias };
|
|
2622
|
+
}
|
|
2623
|
+
if (term.type === "AliasRef") {
|
|
2624
|
+
const aliasName = term.name;
|
|
2625
|
+
if (!availableColumns.has(aliasName)) return null;
|
|
2626
|
+
return { type: "Column", table: baseAlias, name: aliasName };
|
|
2627
|
+
}
|
|
2628
|
+
return null;
|
|
2629
|
+
}
|
|
2508
2630
|
buildPagingColumns(primaryKey, orderBy, tableAlias) {
|
|
2509
2631
|
const columns = [{ type: "Column", table: tableAlias, name: primaryKey, alias: primaryKey }];
|
|
2510
2632
|
if (!orderBy) return columns;
|
|
2511
2633
|
for (const ob of orderBy) {
|
|
2512
|
-
|
|
2634
|
+
const term = ob.term;
|
|
2635
|
+
if (!columns.some((col2) => col2.name === term.name)) {
|
|
2513
2636
|
columns.push({
|
|
2514
2637
|
type: "Column",
|
|
2515
2638
|
table: tableAlias,
|
|
2516
|
-
name:
|
|
2517
|
-
alias:
|
|
2639
|
+
name: term.name,
|
|
2640
|
+
alias: term.name
|
|
2518
2641
|
});
|
|
2519
2642
|
}
|
|
2520
2643
|
}
|
|
@@ -2820,10 +2943,8 @@ var QueryAstService = class {
|
|
|
2820
2943
|
* @returns Updated query state with GROUP BY clause
|
|
2821
2944
|
*/
|
|
2822
2945
|
withGroupBy(col2) {
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
const node = buildColumnNode(tableRef, col2);
|
|
2826
|
-
return this.state.withGroupBy([node]);
|
|
2946
|
+
const term = this.normalizeOrderingTerm(col2);
|
|
2947
|
+
return this.state.withGroupBy([term]);
|
|
2827
2948
|
}
|
|
2828
2949
|
/**
|
|
2829
2950
|
* Adds a HAVING clause to the query
|
|
@@ -2840,11 +2961,9 @@ var QueryAstService = class {
|
|
|
2840
2961
|
* @param direction - Order direction (ASC/DESC)
|
|
2841
2962
|
* @returns Updated query state with ORDER BY clause
|
|
2842
2963
|
*/
|
|
2843
|
-
withOrderBy(
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2846
|
-
const node = buildColumnNode(tableRef, col2);
|
|
2847
|
-
return this.state.withOrderBy([{ type: "OrderBy", column: node, direction }]);
|
|
2964
|
+
withOrderBy(term, direction, nulls, collation) {
|
|
2965
|
+
const normalized = this.normalizeOrderingTerm(term);
|
|
2966
|
+
return this.state.withOrderBy([{ type: "OrderBy", term: normalized, direction, nulls, collation }]);
|
|
2848
2967
|
}
|
|
2849
2968
|
/**
|
|
2850
2969
|
* Adds a DISTINCT clause to the query
|
|
@@ -2879,6 +2998,24 @@ var QueryAstService = class {
|
|
|
2879
2998
|
combineExpressions(existing, next) {
|
|
2880
2999
|
return existing ? and(existing, next) : next;
|
|
2881
3000
|
}
|
|
3001
|
+
normalizeOrderingTerm(term) {
|
|
3002
|
+
const from = this.state.ast.from;
|
|
3003
|
+
const tableRef = from.type === "Table" && from.alias ? { ...this.table, alias: from.alias } : this.table;
|
|
3004
|
+
const termType = term?.type;
|
|
3005
|
+
if (termType === "Column") {
|
|
3006
|
+
return term;
|
|
3007
|
+
}
|
|
3008
|
+
if (termType === "AliasRef") {
|
|
3009
|
+
return term;
|
|
3010
|
+
}
|
|
3011
|
+
if (isOperandNode(term)) {
|
|
3012
|
+
return term;
|
|
3013
|
+
}
|
|
3014
|
+
if (termType === "BinaryExpression" || termType === "LogicalExpression" || termType === "NullExpression" || termType === "InExpression" || termType === "ExistsExpression" || termType === "BetweenExpression" || termType === "ArithmeticExpression") {
|
|
3015
|
+
return term;
|
|
3016
|
+
}
|
|
3017
|
+
return buildColumnNode(tableRef, term);
|
|
3018
|
+
}
|
|
2882
3019
|
};
|
|
2883
3020
|
|
|
2884
3021
|
// src/query-builder/relation-projection-helper.ts
|
|
@@ -3831,8 +3968,10 @@ var DefaultManyToManyCollection = class {
|
|
|
3831
3968
|
attach(target) {
|
|
3832
3969
|
const entity = this.ensureEntity(target);
|
|
3833
3970
|
const id = this.extractId(entity);
|
|
3834
|
-
if (id
|
|
3835
|
-
|
|
3971
|
+
if (id != null && this.items.some((item) => this.extractId(item) === id)) {
|
|
3972
|
+
return;
|
|
3973
|
+
}
|
|
3974
|
+
if (id == null && this.items.includes(entity)) {
|
|
3836
3975
|
return;
|
|
3837
3976
|
}
|
|
3838
3977
|
this.items.push(entity);
|
|
@@ -4528,8 +4667,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4528
4667
|
* @returns New query builder instance with the subquery selection
|
|
4529
4668
|
|
|
4530
4669
|
*/
|
|
4531
|
-
selectSubquery(alias,
|
|
4532
|
-
const query = this.resolveQueryNode(
|
|
4670
|
+
selectSubquery(alias, sub2) {
|
|
4671
|
+
const query = this.resolveQueryNode(sub2);
|
|
4533
4672
|
return this.clone(this.columnSelector.selectSubquery(this.context, alias, query));
|
|
4534
4673
|
}
|
|
4535
4674
|
/**
|
|
@@ -4711,16 +4850,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4711
4850
|
return this.clone(nextContext);
|
|
4712
4851
|
}
|
|
4713
4852
|
/**
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
*/
|
|
4722
|
-
groupBy(col2) {
|
|
4723
|
-
const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(col2));
|
|
4853
|
+
* Adds a GROUP BY clause to the query
|
|
4854
|
+
* @param term - Column definition or ordering term to group by
|
|
4855
|
+
* @returns New query builder instance with the GROUP BY clause
|
|
4856
|
+
*/
|
|
4857
|
+
groupBy(term) {
|
|
4858
|
+
const nextContext = this.applyAst(this.context, (service) => service.withGroupBy(term));
|
|
4724
4859
|
return this.clone(nextContext);
|
|
4725
4860
|
}
|
|
4726
4861
|
/**
|
|
@@ -4737,18 +4872,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
4737
4872
|
return this.clone(nextContext);
|
|
4738
4873
|
}
|
|
4739
4874
|
/**
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4875
|
+
* Adds an ORDER BY clause to the query
|
|
4876
|
+
* @param term - Column definition or ordering term to order by
|
|
4877
|
+
* @param directionOrOptions - Order direction or options (defaults to ASC)
|
|
4878
|
+
* @returns New query builder instance with the ORDER BY clause
|
|
4879
|
+
*/
|
|
4880
|
+
orderBy(term, directionOrOptions = ORDER_DIRECTIONS.ASC) {
|
|
4881
|
+
const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
|
|
4882
|
+
const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
|
|
4883
|
+
const nextContext = this.applyAst(
|
|
4884
|
+
this.context,
|
|
4885
|
+
(service) => service.withOrderBy(term, dir, options.nulls, options.collation)
|
|
4886
|
+
);
|
|
4752
4887
|
return this.clone(nextContext);
|
|
4753
4888
|
}
|
|
4754
4889
|
/**
|
|
@@ -5159,15 +5294,36 @@ var InsertQueryState = class _InsertQueryState {
|
|
|
5159
5294
|
type: "InsertQuery",
|
|
5160
5295
|
into: createTableNode(table),
|
|
5161
5296
|
columns: [],
|
|
5162
|
-
|
|
5297
|
+
source: {
|
|
5298
|
+
type: "InsertValues",
|
|
5299
|
+
rows: []
|
|
5300
|
+
}
|
|
5163
5301
|
};
|
|
5164
5302
|
}
|
|
5165
5303
|
clone(nextAst) {
|
|
5166
5304
|
return new _InsertQueryState(this.table, nextAst);
|
|
5167
5305
|
}
|
|
5306
|
+
ensureColumnsFromRow(rows) {
|
|
5307
|
+
if (this.ast.columns.length) return this.ast.columns;
|
|
5308
|
+
return buildColumnNodes(this.table, Object.keys(rows[0]));
|
|
5309
|
+
}
|
|
5310
|
+
appendValues(rows) {
|
|
5311
|
+
if (this.ast.source.type === "InsertValues") {
|
|
5312
|
+
return [...this.ast.source.rows, ...rows];
|
|
5313
|
+
}
|
|
5314
|
+
return rows;
|
|
5315
|
+
}
|
|
5316
|
+
getTableColumns() {
|
|
5317
|
+
const names = Object.keys(this.table.columns);
|
|
5318
|
+
if (!names.length) return [];
|
|
5319
|
+
return buildColumnNodes(this.table, names);
|
|
5320
|
+
}
|
|
5168
5321
|
withValues(rows) {
|
|
5169
5322
|
if (!rows.length) return this;
|
|
5170
|
-
|
|
5323
|
+
if (this.ast.source.type === "InsertSelect") {
|
|
5324
|
+
throw new Error("Cannot mix INSERT ... VALUES with INSERT ... SELECT source.");
|
|
5325
|
+
}
|
|
5326
|
+
const definedColumns = this.ensureColumnsFromRow(rows);
|
|
5171
5327
|
const newRows = rows.map(
|
|
5172
5328
|
(row, rowIndex) => definedColumns.map((column) => {
|
|
5173
5329
|
const rawValue = row[column.name];
|
|
@@ -5182,7 +5338,34 @@ var InsertQueryState = class _InsertQueryState {
|
|
|
5182
5338
|
return this.clone({
|
|
5183
5339
|
...this.ast,
|
|
5184
5340
|
columns: definedColumns,
|
|
5185
|
-
|
|
5341
|
+
source: {
|
|
5342
|
+
type: "InsertValues",
|
|
5343
|
+
rows: this.appendValues(newRows)
|
|
5344
|
+
}
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5347
|
+
withColumns(columns) {
|
|
5348
|
+
if (!columns.length) return this;
|
|
5349
|
+
return this.clone({
|
|
5350
|
+
...this.ast,
|
|
5351
|
+
columns: [...columns]
|
|
5352
|
+
});
|
|
5353
|
+
}
|
|
5354
|
+
withSelect(query, columns) {
|
|
5355
|
+
const targetColumns = columns.length ? columns : this.ast.columns.length ? this.ast.columns : this.getTableColumns();
|
|
5356
|
+
if (!targetColumns.length) {
|
|
5357
|
+
throw new Error("INSERT ... SELECT requires specifying destination columns.");
|
|
5358
|
+
}
|
|
5359
|
+
if (this.ast.source.type === "InsertValues" && this.ast.source.rows.length) {
|
|
5360
|
+
throw new Error("Cannot mix INSERT ... SELECT with INSERT ... VALUES source.");
|
|
5361
|
+
}
|
|
5362
|
+
return this.clone({
|
|
5363
|
+
...this.ast,
|
|
5364
|
+
columns: [...targetColumns],
|
|
5365
|
+
source: {
|
|
5366
|
+
type: "InsertSelect",
|
|
5367
|
+
query
|
|
5368
|
+
}
|
|
5186
5369
|
});
|
|
5187
5370
|
}
|
|
5188
5371
|
withReturning(columns) {
|
|
@@ -5207,11 +5390,27 @@ var InsertQueryBuilder = class _InsertQueryBuilder {
|
|
|
5207
5390
|
if (!rows.length) return this;
|
|
5208
5391
|
return this.clone(this.state.withValues(rows));
|
|
5209
5392
|
}
|
|
5393
|
+
columns(...columns) {
|
|
5394
|
+
if (!columns.length) return this;
|
|
5395
|
+
return this.clone(this.state.withColumns(this.resolveColumnNodes(columns)));
|
|
5396
|
+
}
|
|
5397
|
+
fromSelect(query, columns = []) {
|
|
5398
|
+
const ast = this.resolveSelectQuery(query);
|
|
5399
|
+
const nodes = columns.length ? this.resolveColumnNodes(columns) : [];
|
|
5400
|
+
return this.clone(this.state.withSelect(ast, nodes));
|
|
5401
|
+
}
|
|
5210
5402
|
returning(...columns) {
|
|
5211
5403
|
if (!columns.length) return this;
|
|
5212
5404
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5213
5405
|
return this.clone(this.state.withReturning(nodes));
|
|
5214
5406
|
}
|
|
5407
|
+
// Helpers for column/AST resolution
|
|
5408
|
+
resolveColumnNodes(columns) {
|
|
5409
|
+
return columns.map((column) => buildColumnNode(this.table, column));
|
|
5410
|
+
}
|
|
5411
|
+
resolveSelectQuery(query) {
|
|
5412
|
+
return typeof query.getAST === "function" ? query.getAST() : query;
|
|
5413
|
+
}
|
|
5215
5414
|
compile(arg) {
|
|
5216
5415
|
if (typeof arg.compileInsert === "function") {
|
|
5217
5416
|
return arg.compileInsert(this.state.ast);
|
|
@@ -5245,7 +5444,8 @@ var UpdateQueryState = class _UpdateQueryState {
|
|
|
5245
5444
|
this.ast = ast ?? {
|
|
5246
5445
|
type: "UpdateQuery",
|
|
5247
5446
|
table: createTableNode(table),
|
|
5248
|
-
set: []
|
|
5447
|
+
set: [],
|
|
5448
|
+
joins: []
|
|
5249
5449
|
};
|
|
5250
5450
|
}
|
|
5251
5451
|
clone(nextAst) {
|
|
@@ -5284,6 +5484,27 @@ var UpdateQueryState = class _UpdateQueryState {
|
|
|
5284
5484
|
returning: [...columns]
|
|
5285
5485
|
});
|
|
5286
5486
|
}
|
|
5487
|
+
withFrom(from) {
|
|
5488
|
+
return this.clone({
|
|
5489
|
+
...this.ast,
|
|
5490
|
+
from
|
|
5491
|
+
});
|
|
5492
|
+
}
|
|
5493
|
+
withJoin(join) {
|
|
5494
|
+
return this.clone({
|
|
5495
|
+
...this.ast,
|
|
5496
|
+
joins: [...this.ast.joins ?? [], join]
|
|
5497
|
+
});
|
|
5498
|
+
}
|
|
5499
|
+
withTableAlias(alias) {
|
|
5500
|
+
return this.clone({
|
|
5501
|
+
...this.ast,
|
|
5502
|
+
table: {
|
|
5503
|
+
...this.ast.table,
|
|
5504
|
+
alias
|
|
5505
|
+
}
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5287
5508
|
};
|
|
5288
5509
|
|
|
5289
5510
|
// src/query-builder/update.ts
|
|
@@ -5295,6 +5516,18 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5295
5516
|
clone(state) {
|
|
5296
5517
|
return new _UpdateQueryBuilder(this.table, state);
|
|
5297
5518
|
}
|
|
5519
|
+
as(alias) {
|
|
5520
|
+
return this.clone(this.state.withTableAlias(alias));
|
|
5521
|
+
}
|
|
5522
|
+
from(source) {
|
|
5523
|
+
const tableSource = this.resolveTableSource(source);
|
|
5524
|
+
return this.clone(this.state.withFrom(tableSource));
|
|
5525
|
+
}
|
|
5526
|
+
join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
|
|
5527
|
+
const joinTarget = this.resolveJoinTarget(table);
|
|
5528
|
+
const joinNode = createJoinNode(kind, joinTarget, condition, relationName);
|
|
5529
|
+
return this.clone(this.state.withJoin(joinNode));
|
|
5530
|
+
}
|
|
5298
5531
|
set(values) {
|
|
5299
5532
|
return this.clone(this.state.withSet(values));
|
|
5300
5533
|
}
|
|
@@ -5306,6 +5539,16 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5306
5539
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5307
5540
|
return this.clone(this.state.withReturning(nodes));
|
|
5308
5541
|
}
|
|
5542
|
+
resolveTableSource(source) {
|
|
5543
|
+
if (isTableSourceNode(source)) {
|
|
5544
|
+
return source;
|
|
5545
|
+
}
|
|
5546
|
+
return { type: "Table", name: source.name, schema: source.schema };
|
|
5547
|
+
}
|
|
5548
|
+
resolveJoinTarget(table) {
|
|
5549
|
+
if (typeof table === "string") return table;
|
|
5550
|
+
return this.resolveTableSource(table);
|
|
5551
|
+
}
|
|
5309
5552
|
compile(arg) {
|
|
5310
5553
|
if (typeof arg.compileUpdate === "function") {
|
|
5311
5554
|
return arg.compileUpdate(this.state.ast);
|
|
@@ -5320,6 +5563,7 @@ var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
|
5320
5563
|
return this.state.ast;
|
|
5321
5564
|
}
|
|
5322
5565
|
};
|
|
5566
|
+
var isTableSourceNode = (source) => typeof source.type === "string";
|
|
5323
5567
|
|
|
5324
5568
|
// src/query-builder/delete-query-state.ts
|
|
5325
5569
|
var DeleteQueryState = class _DeleteQueryState {
|
|
@@ -5327,7 +5571,8 @@ var DeleteQueryState = class _DeleteQueryState {
|
|
|
5327
5571
|
this.table = table;
|
|
5328
5572
|
this.ast = ast ?? {
|
|
5329
5573
|
type: "DeleteQuery",
|
|
5330
|
-
from: createTableNode(table)
|
|
5574
|
+
from: createTableNode(table),
|
|
5575
|
+
joins: []
|
|
5331
5576
|
};
|
|
5332
5577
|
}
|
|
5333
5578
|
clone(nextAst) {
|
|
@@ -5345,6 +5590,27 @@ var DeleteQueryState = class _DeleteQueryState {
|
|
|
5345
5590
|
returning: [...columns]
|
|
5346
5591
|
});
|
|
5347
5592
|
}
|
|
5593
|
+
withUsing(source) {
|
|
5594
|
+
return this.clone({
|
|
5595
|
+
...this.ast,
|
|
5596
|
+
using: source
|
|
5597
|
+
});
|
|
5598
|
+
}
|
|
5599
|
+
withJoin(join) {
|
|
5600
|
+
return this.clone({
|
|
5601
|
+
...this.ast,
|
|
5602
|
+
joins: [...this.ast.joins ?? [], join]
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
withTableAlias(alias) {
|
|
5606
|
+
return this.clone({
|
|
5607
|
+
...this.ast,
|
|
5608
|
+
from: {
|
|
5609
|
+
...this.ast.from,
|
|
5610
|
+
alias
|
|
5611
|
+
}
|
|
5612
|
+
});
|
|
5613
|
+
}
|
|
5348
5614
|
};
|
|
5349
5615
|
|
|
5350
5616
|
// src/query-builder/delete.ts
|
|
@@ -5359,11 +5625,32 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
|
5359
5625
|
where(expr) {
|
|
5360
5626
|
return this.clone(this.state.withWhere(expr));
|
|
5361
5627
|
}
|
|
5628
|
+
as(alias) {
|
|
5629
|
+
return this.clone(this.state.withTableAlias(alias));
|
|
5630
|
+
}
|
|
5631
|
+
using(source) {
|
|
5632
|
+
return this.clone(this.state.withUsing(this.resolveTableSource(source)));
|
|
5633
|
+
}
|
|
5634
|
+
join(table, condition, kind = JOIN_KINDS.INNER, relationName) {
|
|
5635
|
+
const target = this.resolveJoinTarget(table);
|
|
5636
|
+
const joinNode = createJoinNode(kind, target, condition, relationName);
|
|
5637
|
+
return this.clone(this.state.withJoin(joinNode));
|
|
5638
|
+
}
|
|
5362
5639
|
returning(...columns) {
|
|
5363
5640
|
if (!columns.length) return this;
|
|
5364
5641
|
const nodes = columns.map((column) => buildColumnNode(this.table, column));
|
|
5365
5642
|
return this.clone(this.state.withReturning(nodes));
|
|
5366
5643
|
}
|
|
5644
|
+
resolveTableSource(source) {
|
|
5645
|
+
if (isTableSourceNode2(source)) {
|
|
5646
|
+
return source;
|
|
5647
|
+
}
|
|
5648
|
+
return { type: "Table", name: source.name, schema: source.schema };
|
|
5649
|
+
}
|
|
5650
|
+
resolveJoinTarget(table) {
|
|
5651
|
+
if (typeof table === "string") return table;
|
|
5652
|
+
return this.resolveTableSource(table);
|
|
5653
|
+
}
|
|
5367
5654
|
compile(arg) {
|
|
5368
5655
|
if (typeof arg.compileDelete === "function") {
|
|
5369
5656
|
return arg.compileDelete(this.state.ast);
|
|
@@ -5378,6 +5665,7 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
|
5378
5665
|
return this.state.ast;
|
|
5379
5666
|
}
|
|
5380
5667
|
};
|
|
5668
|
+
var isTableSourceNode2 = (source) => typeof source.type === "string";
|
|
5381
5669
|
|
|
5382
5670
|
// src/core/ddl/sql-writing.ts
|
|
5383
5671
|
var resolvePrimaryKey = (table) => {
|
|
@@ -6576,7 +6864,7 @@ var TypeScriptGenerator = class {
|
|
|
6576
6864
|
lines.push(`.where(${this.printExpression(ast.where)})`);
|
|
6577
6865
|
}
|
|
6578
6866
|
if (ast.groupBy && ast.groupBy.length) {
|
|
6579
|
-
const cols = ast.groupBy.map((
|
|
6867
|
+
const cols = ast.groupBy.map((term) => this.printOrderingTerm(term)).join(", ");
|
|
6580
6868
|
lines.push(`.groupBy(${cols})`);
|
|
6581
6869
|
}
|
|
6582
6870
|
if (ast.having) {
|
|
@@ -6584,7 +6872,16 @@ var TypeScriptGenerator = class {
|
|
|
6584
6872
|
}
|
|
6585
6873
|
if (ast.orderBy && ast.orderBy.length) {
|
|
6586
6874
|
ast.orderBy.forEach((o) => {
|
|
6587
|
-
|
|
6875
|
+
const term = this.printOrderingTerm(o.term);
|
|
6876
|
+
const opts = [`direction: '${o.direction}'`];
|
|
6877
|
+
if (o.nulls) opts.push(`nulls: '${o.nulls}'`);
|
|
6878
|
+
if (o.collation) opts.push(`collation: '${o.collation}'`);
|
|
6879
|
+
const hasOpts = opts.length > 1;
|
|
6880
|
+
if (hasOpts) {
|
|
6881
|
+
lines.push(`.orderBy(${term}, { ${opts.join(", ")} })`);
|
|
6882
|
+
} else {
|
|
6883
|
+
lines.push(`.orderBy(${term}, '${o.direction}')`);
|
|
6884
|
+
}
|
|
6588
6885
|
});
|
|
6589
6886
|
}
|
|
6590
6887
|
if (ast.limit) lines.push(`.limit(${ast.limit})`);
|
|
@@ -6607,6 +6904,29 @@ var TypeScriptGenerator = class {
|
|
|
6607
6904
|
printOperand(node) {
|
|
6608
6905
|
return visitOperand(node, this);
|
|
6609
6906
|
}
|
|
6907
|
+
/**
|
|
6908
|
+
* Prints an ordering term (operand/expression/alias) to TypeScript code.
|
|
6909
|
+
*/
|
|
6910
|
+
printOrderingTerm(term) {
|
|
6911
|
+
if (!term || !term.type) {
|
|
6912
|
+
throw new Error("Unsupported ordering term");
|
|
6913
|
+
}
|
|
6914
|
+
switch (term.type) {
|
|
6915
|
+
case "Column":
|
|
6916
|
+
return `${this.namingStrategy.tableToSymbol(term.table)}.${term.name}`;
|
|
6917
|
+
case "AliasRef":
|
|
6918
|
+
return this.visitAliasRef(term);
|
|
6919
|
+
case "Literal":
|
|
6920
|
+
case "Function":
|
|
6921
|
+
case "JsonPath":
|
|
6922
|
+
case "ScalarSubquery":
|
|
6923
|
+
case "CaseExpression":
|
|
6924
|
+
case "WindowFunction":
|
|
6925
|
+
return this.printOperand(term);
|
|
6926
|
+
default:
|
|
6927
|
+
return this.printExpression(term);
|
|
6928
|
+
}
|
|
6929
|
+
}
|
|
6610
6930
|
visitBinaryExpression(binary) {
|
|
6611
6931
|
return this.printBinaryExpression(binary);
|
|
6612
6932
|
}
|
|
@@ -6625,6 +6945,9 @@ var TypeScriptGenerator = class {
|
|
|
6625
6945
|
visitBetweenExpression(betweenExpr) {
|
|
6626
6946
|
return this.printBetweenExpression(betweenExpr);
|
|
6627
6947
|
}
|
|
6948
|
+
visitArithmeticExpression(arithExpr) {
|
|
6949
|
+
return this.printArithmeticExpression(arithExpr);
|
|
6950
|
+
}
|
|
6628
6951
|
visitColumn(node) {
|
|
6629
6952
|
return this.printColumnOperand(node);
|
|
6630
6953
|
}
|
|
@@ -6646,6 +6969,9 @@ var TypeScriptGenerator = class {
|
|
|
6646
6969
|
visitWindowFunction(node) {
|
|
6647
6970
|
return this.printWindowFunctionOperand(node);
|
|
6648
6971
|
}
|
|
6972
|
+
visitAliasRef(node) {
|
|
6973
|
+
return `aliasRef('${node.name}')`;
|
|
6974
|
+
}
|
|
6649
6975
|
/**
|
|
6650
6976
|
* Prints a binary expression to TypeScript code
|
|
6651
6977
|
* @param binary - Binary expression node
|
|
@@ -6676,6 +7002,11 @@ var TypeScriptGenerator = class {
|
|
|
6676
7002
|
${parts.join(",\n ")}
|
|
6677
7003
|
)`;
|
|
6678
7004
|
}
|
|
7005
|
+
printArithmeticExpression(expr) {
|
|
7006
|
+
const left2 = this.printOperand(expr.left);
|
|
7007
|
+
const right2 = this.printOperand(expr.right);
|
|
7008
|
+
return `${left2} ${expr.operator} ${right2}`;
|
|
7009
|
+
}
|
|
6679
7010
|
/**
|
|
6680
7011
|
* Prints an IN expression to TypeScript code
|
|
6681
7012
|
* @param inExpr - IN expression node
|
|
@@ -6683,9 +7014,13 @@ var TypeScriptGenerator = class {
|
|
|
6683
7014
|
*/
|
|
6684
7015
|
printInExpression(inExpr) {
|
|
6685
7016
|
const left2 = this.printOperand(inExpr.left);
|
|
6686
|
-
const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
|
|
6687
7017
|
const fn4 = this.mapOp(inExpr.operator);
|
|
6688
|
-
|
|
7018
|
+
if (Array.isArray(inExpr.right)) {
|
|
7019
|
+
const values = inExpr.right.map((v) => this.printOperand(v)).join(", ");
|
|
7020
|
+
return `${fn4}(${left2}, [${values}])`;
|
|
7021
|
+
}
|
|
7022
|
+
const subquery = this.inlineChain(this.buildSelectLines(inExpr.right.query));
|
|
7023
|
+
return `${fn4}(${left2}, (${subquery}))`;
|
|
6689
7024
|
}
|
|
6690
7025
|
/**
|
|
6691
7026
|
* Prints a null expression to TypeScript code
|
|
@@ -6789,7 +7124,12 @@ var TypeScriptGenerator = class {
|
|
|
6789
7124
|
parts.push(partitionClause);
|
|
6790
7125
|
}
|
|
6791
7126
|
if (node.orderBy && node.orderBy.length > 0) {
|
|
6792
|
-
const orderClause = "ORDER BY " + node.orderBy.map((o) =>
|
|
7127
|
+
const orderClause = "ORDER BY " + node.orderBy.map((o) => {
|
|
7128
|
+
const term = this.printOrderingTerm(o.term);
|
|
7129
|
+
const collation = o.collation ? ` COLLATE ${o.collation}` : "";
|
|
7130
|
+
const nulls = o.nulls ? ` NULLS ${o.nulls}` : "";
|
|
7131
|
+
return `${term} ${o.direction}${collation}${nulls}`;
|
|
7132
|
+
}).join(", ");
|
|
6793
7133
|
parts.push(orderClause);
|
|
6794
7134
|
}
|
|
6795
7135
|
result += parts.join(" ");
|
|
@@ -6865,6 +7205,13 @@ var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
|
|
|
6865
7205
|
|
|
6866
7206
|
// src/orm/unit-of-work.ts
|
|
6867
7207
|
var UnitOfWork = class {
|
|
7208
|
+
/**
|
|
7209
|
+
* Creates a new UnitOfWork instance.
|
|
7210
|
+
* @param dialect - The database dialect
|
|
7211
|
+
* @param executor - The database executor
|
|
7212
|
+
* @param identityMap - The identity map
|
|
7213
|
+
* @param hookContext - Function to get the hook context
|
|
7214
|
+
*/
|
|
6868
7215
|
constructor(dialect, executor, identityMap, hookContext) {
|
|
6869
7216
|
this.dialect = dialect;
|
|
6870
7217
|
this.executor = executor;
|
|
@@ -6872,21 +7219,50 @@ var UnitOfWork = class {
|
|
|
6872
7219
|
this.hookContext = hookContext;
|
|
6873
7220
|
this.trackedEntities = /* @__PURE__ */ new Map();
|
|
6874
7221
|
}
|
|
7222
|
+
/**
|
|
7223
|
+
* Gets the identity buckets map.
|
|
7224
|
+
*/
|
|
6875
7225
|
get identityBuckets() {
|
|
6876
7226
|
return this.identityMap.bucketsMap;
|
|
6877
7227
|
}
|
|
7228
|
+
/**
|
|
7229
|
+
* Gets all tracked entities.
|
|
7230
|
+
* @returns Array of tracked entities
|
|
7231
|
+
*/
|
|
6878
7232
|
getTracked() {
|
|
6879
7233
|
return Array.from(this.trackedEntities.values());
|
|
6880
7234
|
}
|
|
7235
|
+
/**
|
|
7236
|
+
* Gets an entity by table and primary key.
|
|
7237
|
+
* @param table - The table definition
|
|
7238
|
+
* @param pk - The primary key value
|
|
7239
|
+
* @returns The entity or undefined if not found
|
|
7240
|
+
*/
|
|
6881
7241
|
getEntity(table, pk) {
|
|
6882
7242
|
return this.identityMap.getEntity(table, pk);
|
|
6883
7243
|
}
|
|
7244
|
+
/**
|
|
7245
|
+
* Gets all tracked entities for a specific table.
|
|
7246
|
+
* @param table - The table definition
|
|
7247
|
+
* @returns Array of tracked entities
|
|
7248
|
+
*/
|
|
6884
7249
|
getEntitiesForTable(table) {
|
|
6885
7250
|
return this.identityMap.getEntitiesForTable(table);
|
|
6886
7251
|
}
|
|
7252
|
+
/**
|
|
7253
|
+
* Finds a tracked entity.
|
|
7254
|
+
* @param entity - The entity to find
|
|
7255
|
+
* @returns The tracked entity or undefined if not found
|
|
7256
|
+
*/
|
|
6887
7257
|
findTracked(entity) {
|
|
6888
7258
|
return this.trackedEntities.get(entity);
|
|
6889
7259
|
}
|
|
7260
|
+
/**
|
|
7261
|
+
* Sets an entity in the identity map.
|
|
7262
|
+
* @param table - The table definition
|
|
7263
|
+
* @param pk - The primary key value
|
|
7264
|
+
* @param entity - The entity instance
|
|
7265
|
+
*/
|
|
6890
7266
|
setEntity(table, pk, entity) {
|
|
6891
7267
|
if (pk === null || pk === void 0) return;
|
|
6892
7268
|
let tracked = this.trackedEntities.get(entity);
|
|
@@ -6904,6 +7280,12 @@ var UnitOfWork = class {
|
|
|
6904
7280
|
}
|
|
6905
7281
|
this.registerIdentity(tracked);
|
|
6906
7282
|
}
|
|
7283
|
+
/**
|
|
7284
|
+
* Tracks a new entity.
|
|
7285
|
+
* @param table - The table definition
|
|
7286
|
+
* @param entity - The entity instance
|
|
7287
|
+
* @param pk - Optional primary key value
|
|
7288
|
+
*/
|
|
6907
7289
|
trackNew(table, entity, pk) {
|
|
6908
7290
|
const tracked = {
|
|
6909
7291
|
table,
|
|
@@ -6917,6 +7299,12 @@ var UnitOfWork = class {
|
|
|
6917
7299
|
this.registerIdentity(tracked);
|
|
6918
7300
|
}
|
|
6919
7301
|
}
|
|
7302
|
+
/**
|
|
7303
|
+
* Tracks a managed entity.
|
|
7304
|
+
* @param table - The table definition
|
|
7305
|
+
* @param pk - The primary key value
|
|
7306
|
+
* @param entity - The entity instance
|
|
7307
|
+
*/
|
|
6920
7308
|
trackManaged(table, pk, entity) {
|
|
6921
7309
|
const tracked = {
|
|
6922
7310
|
table,
|
|
@@ -6928,17 +7316,28 @@ var UnitOfWork = class {
|
|
|
6928
7316
|
this.trackedEntities.set(entity, tracked);
|
|
6929
7317
|
this.registerIdentity(tracked);
|
|
6930
7318
|
}
|
|
7319
|
+
/**
|
|
7320
|
+
* Marks an entity as dirty (modified).
|
|
7321
|
+
* @param entity - The entity to mark as dirty
|
|
7322
|
+
*/
|
|
6931
7323
|
markDirty(entity) {
|
|
6932
7324
|
const tracked = this.trackedEntities.get(entity);
|
|
6933
7325
|
if (!tracked) return;
|
|
6934
7326
|
if (tracked.status === "new" /* New */ || tracked.status === "removed" /* Removed */) return;
|
|
6935
7327
|
tracked.status = "dirty" /* Dirty */;
|
|
6936
7328
|
}
|
|
7329
|
+
/**
|
|
7330
|
+
* Marks an entity as removed.
|
|
7331
|
+
* @param entity - The entity to mark as removed
|
|
7332
|
+
*/
|
|
6937
7333
|
markRemoved(entity) {
|
|
6938
7334
|
const tracked = this.trackedEntities.get(entity);
|
|
6939
7335
|
if (!tracked) return;
|
|
6940
7336
|
tracked.status = "removed" /* Removed */;
|
|
6941
7337
|
}
|
|
7338
|
+
/**
|
|
7339
|
+
* Flushes pending changes to the database.
|
|
7340
|
+
*/
|
|
6942
7341
|
async flush() {
|
|
6943
7342
|
const toFlush = Array.from(this.trackedEntities.values());
|
|
6944
7343
|
for (const tracked of toFlush) {
|
|
@@ -6957,10 +7356,17 @@ var UnitOfWork = class {
|
|
|
6957
7356
|
}
|
|
6958
7357
|
}
|
|
6959
7358
|
}
|
|
7359
|
+
/**
|
|
7360
|
+
* Resets the unit of work by clearing all tracked entities and identity map.
|
|
7361
|
+
*/
|
|
6960
7362
|
reset() {
|
|
6961
7363
|
this.trackedEntities.clear();
|
|
6962
7364
|
this.identityMap.clear();
|
|
6963
7365
|
}
|
|
7366
|
+
/**
|
|
7367
|
+
* Flushes an insert operation for a new entity.
|
|
7368
|
+
* @param tracked - The tracked entity to insert
|
|
7369
|
+
*/
|
|
6964
7370
|
async flushInsert(tracked) {
|
|
6965
7371
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
6966
7372
|
const payload = this.extractColumns(tracked.table, tracked.entity);
|
|
@@ -6977,6 +7383,10 @@ var UnitOfWork = class {
|
|
|
6977
7383
|
this.registerIdentity(tracked);
|
|
6978
7384
|
await this.runHook(tracked.table.hooks?.afterInsert, tracked);
|
|
6979
7385
|
}
|
|
7386
|
+
/**
|
|
7387
|
+
* Flushes an update operation for a modified entity.
|
|
7388
|
+
* @param tracked - The tracked entity to update
|
|
7389
|
+
*/
|
|
6980
7390
|
async flushUpdate(tracked) {
|
|
6981
7391
|
if (tracked.pk == null) return;
|
|
6982
7392
|
const changes = this.computeChanges(tracked);
|
|
@@ -6999,6 +7409,10 @@ var UnitOfWork = class {
|
|
|
6999
7409
|
this.registerIdentity(tracked);
|
|
7000
7410
|
await this.runHook(tracked.table.hooks?.afterUpdate, tracked);
|
|
7001
7411
|
}
|
|
7412
|
+
/**
|
|
7413
|
+
* Flushes a delete operation for a removed entity.
|
|
7414
|
+
* @param tracked - The tracked entity to delete
|
|
7415
|
+
*/
|
|
7002
7416
|
async flushDelete(tracked) {
|
|
7003
7417
|
if (tracked.pk == null) return;
|
|
7004
7418
|
await this.runHook(tracked.table.hooks?.beforeDelete, tracked);
|
|
@@ -7012,10 +7426,20 @@ var UnitOfWork = class {
|
|
|
7012
7426
|
this.identityMap.remove(tracked);
|
|
7013
7427
|
await this.runHook(tracked.table.hooks?.afterDelete, tracked);
|
|
7014
7428
|
}
|
|
7429
|
+
/**
|
|
7430
|
+
* Runs a table hook if defined.
|
|
7431
|
+
* @param hook - The hook function
|
|
7432
|
+
* @param tracked - The tracked entity
|
|
7433
|
+
*/
|
|
7015
7434
|
async runHook(hook, tracked) {
|
|
7016
7435
|
if (!hook) return;
|
|
7017
7436
|
await hook(this.hookContext(), tracked.entity);
|
|
7018
7437
|
}
|
|
7438
|
+
/**
|
|
7439
|
+
* Computes changes between current entity state and original snapshot.
|
|
7440
|
+
* @param tracked - The tracked entity
|
|
7441
|
+
* @returns Object with changed column values
|
|
7442
|
+
*/
|
|
7019
7443
|
computeChanges(tracked) {
|
|
7020
7444
|
const snapshot = tracked.original ?? {};
|
|
7021
7445
|
const changes = {};
|
|
@@ -7027,6 +7451,12 @@ var UnitOfWork = class {
|
|
|
7027
7451
|
}
|
|
7028
7452
|
return changes;
|
|
7029
7453
|
}
|
|
7454
|
+
/**
|
|
7455
|
+
* Extracts column values from an entity.
|
|
7456
|
+
* @param table - The table definition
|
|
7457
|
+
* @param entity - The entity instance
|
|
7458
|
+
* @returns Object with column values
|
|
7459
|
+
*/
|
|
7030
7460
|
extractColumns(table, entity) {
|
|
7031
7461
|
const payload = {};
|
|
7032
7462
|
for (const column of Object.keys(table.columns)) {
|
|
@@ -7035,9 +7465,19 @@ var UnitOfWork = class {
|
|
|
7035
7465
|
}
|
|
7036
7466
|
return payload;
|
|
7037
7467
|
}
|
|
7468
|
+
/**
|
|
7469
|
+
* Executes a compiled query.
|
|
7470
|
+
* @param compiled - The compiled query
|
|
7471
|
+
* @returns Query results
|
|
7472
|
+
*/
|
|
7038
7473
|
async executeCompiled(compiled) {
|
|
7039
7474
|
return this.executor.executeSql(compiled.sql, compiled.params);
|
|
7040
7475
|
}
|
|
7476
|
+
/**
|
|
7477
|
+
* Gets columns for RETURNING clause.
|
|
7478
|
+
* @param table - The table definition
|
|
7479
|
+
* @returns Array of column nodes
|
|
7480
|
+
*/
|
|
7041
7481
|
getReturningColumns(table) {
|
|
7042
7482
|
return Object.values(table.columns).map((column) => ({
|
|
7043
7483
|
type: "Column",
|
|
@@ -7046,6 +7486,11 @@ var UnitOfWork = class {
|
|
|
7046
7486
|
alias: column.name
|
|
7047
7487
|
}));
|
|
7048
7488
|
}
|
|
7489
|
+
/**
|
|
7490
|
+
* Applies RETURNING clause results to the tracked entity.
|
|
7491
|
+
* @param tracked - The tracked entity
|
|
7492
|
+
* @param results - Query results
|
|
7493
|
+
*/
|
|
7049
7494
|
applyReturningResults(tracked, results) {
|
|
7050
7495
|
if (!this.dialect.supportsReturning()) return;
|
|
7051
7496
|
const first = results[0];
|
|
@@ -7057,15 +7502,30 @@ var UnitOfWork = class {
|
|
|
7057
7502
|
tracked.entity[columnName] = row[i];
|
|
7058
7503
|
}
|
|
7059
7504
|
}
|
|
7505
|
+
/**
|
|
7506
|
+
* Normalizes a column name by removing quotes and table prefixes.
|
|
7507
|
+
* @param column - The column name to normalize
|
|
7508
|
+
* @returns Normalized column name
|
|
7509
|
+
*/
|
|
7060
7510
|
normalizeColumnName(column) {
|
|
7061
7511
|
const parts = column.split(".");
|
|
7062
7512
|
const candidate = parts[parts.length - 1];
|
|
7063
7513
|
return candidate.replace(/^["`[\]]+|["`[\]]+$/g, "");
|
|
7064
7514
|
}
|
|
7515
|
+
/**
|
|
7516
|
+
* Registers an entity in the identity map.
|
|
7517
|
+
* @param tracked - The tracked entity to register
|
|
7518
|
+
*/
|
|
7065
7519
|
registerIdentity(tracked) {
|
|
7066
7520
|
if (tracked.pk == null) return;
|
|
7067
7521
|
this.identityMap.register(tracked);
|
|
7068
7522
|
}
|
|
7523
|
+
/**
|
|
7524
|
+
* Creates a snapshot of an entity's current state.
|
|
7525
|
+
* @param table - The table definition
|
|
7526
|
+
* @param entity - The entity instance
|
|
7527
|
+
* @returns Object with entity state
|
|
7528
|
+
*/
|
|
7069
7529
|
createSnapshot(table, entity) {
|
|
7070
7530
|
const snapshot = {};
|
|
7071
7531
|
for (const column of Object.keys(table.columns)) {
|
|
@@ -7073,6 +7533,11 @@ var UnitOfWork = class {
|
|
|
7073
7533
|
}
|
|
7074
7534
|
return snapshot;
|
|
7075
7535
|
}
|
|
7536
|
+
/**
|
|
7537
|
+
* Gets the primary key value from a tracked entity.
|
|
7538
|
+
* @param tracked - The tracked entity
|
|
7539
|
+
* @returns Primary key value or null
|
|
7540
|
+
*/
|
|
7076
7541
|
getPrimaryKeyValue(tracked) {
|
|
7077
7542
|
const key = findPrimaryKey(tracked.table);
|
|
7078
7543
|
const val = tracked.entity[key];
|
|
@@ -7083,6 +7548,10 @@ var UnitOfWork = class {
|
|
|
7083
7548
|
|
|
7084
7549
|
// src/orm/domain-event-bus.ts
|
|
7085
7550
|
var DomainEventBus = class {
|
|
7551
|
+
/**
|
|
7552
|
+
* Creates a new DomainEventBus instance.
|
|
7553
|
+
* @param initialHandlers - Optional initial event handlers
|
|
7554
|
+
*/
|
|
7086
7555
|
constructor(initialHandlers) {
|
|
7087
7556
|
this.handlers = /* @__PURE__ */ new Map();
|
|
7088
7557
|
if (initialHandlers) {
|
|
@@ -7093,15 +7562,32 @@ var DomainEventBus = class {
|
|
|
7093
7562
|
}
|
|
7094
7563
|
}
|
|
7095
7564
|
}
|
|
7565
|
+
/**
|
|
7566
|
+
* Registers an event handler for a specific event type.
|
|
7567
|
+
* @template TType - The event type
|
|
7568
|
+
* @param type - The event type
|
|
7569
|
+
* @param handler - The event handler
|
|
7570
|
+
*/
|
|
7096
7571
|
on(type, handler) {
|
|
7097
7572
|
const key = type;
|
|
7098
7573
|
const existing = this.handlers.get(key) ?? [];
|
|
7099
7574
|
existing.push(handler);
|
|
7100
7575
|
this.handlers.set(key, existing);
|
|
7101
7576
|
}
|
|
7577
|
+
/**
|
|
7578
|
+
* Registers an event handler for a specific event type (alias for on).
|
|
7579
|
+
* @template TType - The event type
|
|
7580
|
+
* @param type - The event type
|
|
7581
|
+
* @param handler - The event handler
|
|
7582
|
+
*/
|
|
7102
7583
|
register(type, handler) {
|
|
7103
7584
|
this.on(type, handler);
|
|
7104
7585
|
}
|
|
7586
|
+
/**
|
|
7587
|
+
* Dispatches domain events for tracked entities.
|
|
7588
|
+
* @param trackedEntities - Iterable of tracked entities
|
|
7589
|
+
* @param ctx - The context to pass to handlers
|
|
7590
|
+
*/
|
|
7105
7591
|
async dispatch(trackedEntities, ctx) {
|
|
7106
7592
|
for (const tracked of trackedEntities) {
|
|
7107
7593
|
const entity = tracked.entity;
|
|
@@ -7126,18 +7612,34 @@ var addDomainEvent = (entity, event) => {
|
|
|
7126
7612
|
|
|
7127
7613
|
// src/orm/relation-change-processor.ts
|
|
7128
7614
|
var RelationChangeProcessor = class {
|
|
7615
|
+
/**
|
|
7616
|
+
* Creates a new RelationChangeProcessor instance.
|
|
7617
|
+
* @param unitOfWork - The unit of work instance
|
|
7618
|
+
* @param dialect - The database dialect
|
|
7619
|
+
* @param executor - The database executor
|
|
7620
|
+
*/
|
|
7129
7621
|
constructor(unitOfWork, dialect, executor) {
|
|
7130
7622
|
this.unitOfWork = unitOfWork;
|
|
7131
7623
|
this.dialect = dialect;
|
|
7132
7624
|
this.executor = executor;
|
|
7133
7625
|
this.relationChanges = [];
|
|
7134
7626
|
}
|
|
7627
|
+
/**
|
|
7628
|
+
* Registers a relation change for processing.
|
|
7629
|
+
* @param entry - The relation change entry
|
|
7630
|
+
*/
|
|
7135
7631
|
registerChange(entry) {
|
|
7136
7632
|
this.relationChanges.push(entry);
|
|
7137
7633
|
}
|
|
7634
|
+
/**
|
|
7635
|
+
* Resets the relation change processor by clearing all pending changes.
|
|
7636
|
+
*/
|
|
7138
7637
|
reset() {
|
|
7139
7638
|
this.relationChanges.length = 0;
|
|
7140
7639
|
}
|
|
7640
|
+
/**
|
|
7641
|
+
* Processes all pending relation changes.
|
|
7642
|
+
*/
|
|
7141
7643
|
async process() {
|
|
7142
7644
|
if (!this.relationChanges.length) return;
|
|
7143
7645
|
const entries = [...this.relationChanges];
|
|
@@ -7159,6 +7661,10 @@ var RelationChangeProcessor = class {
|
|
|
7159
7661
|
}
|
|
7160
7662
|
}
|
|
7161
7663
|
}
|
|
7664
|
+
/**
|
|
7665
|
+
* Handles changes for has-many relations.
|
|
7666
|
+
* @param entry - The relation change entry
|
|
7667
|
+
*/
|
|
7162
7668
|
async handleHasManyChange(entry) {
|
|
7163
7669
|
const relation = entry.relation;
|
|
7164
7670
|
const target = entry.change.entity;
|
|
@@ -7177,6 +7683,10 @@ var RelationChangeProcessor = class {
|
|
|
7177
7683
|
this.detachHasManyChild(tracked.entity, relation);
|
|
7178
7684
|
}
|
|
7179
7685
|
}
|
|
7686
|
+
/**
|
|
7687
|
+
* Handles changes for has-one relations.
|
|
7688
|
+
* @param entry - The relation change entry
|
|
7689
|
+
*/
|
|
7180
7690
|
async handleHasOneChange(entry) {
|
|
7181
7691
|
const relation = entry.relation;
|
|
7182
7692
|
const target = entry.change.entity;
|
|
@@ -7195,8 +7705,16 @@ var RelationChangeProcessor = class {
|
|
|
7195
7705
|
this.detachHasOneChild(tracked.entity, relation);
|
|
7196
7706
|
}
|
|
7197
7707
|
}
|
|
7708
|
+
/**
|
|
7709
|
+
* Handles changes for belongs-to relations.
|
|
7710
|
+
* @param _entry - The relation change entry (reserved for future use)
|
|
7711
|
+
*/
|
|
7198
7712
|
async handleBelongsToChange(_entry) {
|
|
7199
7713
|
}
|
|
7714
|
+
/**
|
|
7715
|
+
* Handles changes for belongs-to-many relations.
|
|
7716
|
+
* @param entry - The relation change entry
|
|
7717
|
+
*/
|
|
7200
7718
|
async handleBelongsToManyChange(entry) {
|
|
7201
7719
|
const relation = entry.relation;
|
|
7202
7720
|
const rootKey = relation.localKey || findPrimaryKey(entry.rootTable);
|
|
@@ -7215,11 +7733,22 @@ var RelationChangeProcessor = class {
|
|
|
7215
7733
|
}
|
|
7216
7734
|
}
|
|
7217
7735
|
}
|
|
7736
|
+
/**
|
|
7737
|
+
* Assigns a foreign key for has-many relations.
|
|
7738
|
+
* @param child - The child entity
|
|
7739
|
+
* @param relation - The has-many relation
|
|
7740
|
+
* @param rootValue - The root entity's primary key value
|
|
7741
|
+
*/
|
|
7218
7742
|
assignHasManyForeignKey(child, relation, rootValue) {
|
|
7219
7743
|
const current = child[relation.foreignKey];
|
|
7220
7744
|
if (current === rootValue) return;
|
|
7221
7745
|
child[relation.foreignKey] = rootValue;
|
|
7222
7746
|
}
|
|
7747
|
+
/**
|
|
7748
|
+
* Detaches a child entity from has-many relations.
|
|
7749
|
+
* @param child - The child entity
|
|
7750
|
+
* @param relation - The has-many relation
|
|
7751
|
+
*/
|
|
7223
7752
|
detachHasManyChild(child, relation) {
|
|
7224
7753
|
if (relation.cascade === "all" || relation.cascade === "remove") {
|
|
7225
7754
|
this.unitOfWork.markRemoved(child);
|
|
@@ -7228,11 +7757,22 @@ var RelationChangeProcessor = class {
|
|
|
7228
7757
|
child[relation.foreignKey] = null;
|
|
7229
7758
|
this.unitOfWork.markDirty(child);
|
|
7230
7759
|
}
|
|
7760
|
+
/**
|
|
7761
|
+
* Assigns a foreign key for has-one relations.
|
|
7762
|
+
* @param child - The child entity
|
|
7763
|
+
* @param relation - The has-one relation
|
|
7764
|
+
* @param rootValue - The root entity's primary key value
|
|
7765
|
+
*/
|
|
7231
7766
|
assignHasOneForeignKey(child, relation, rootValue) {
|
|
7232
7767
|
const current = child[relation.foreignKey];
|
|
7233
7768
|
if (current === rootValue) return;
|
|
7234
7769
|
child[relation.foreignKey] = rootValue;
|
|
7235
7770
|
}
|
|
7771
|
+
/**
|
|
7772
|
+
* Detaches a child entity from has-one relations.
|
|
7773
|
+
* @param child - The child entity
|
|
7774
|
+
* @param relation - The has-one relation
|
|
7775
|
+
*/
|
|
7236
7776
|
detachHasOneChild(child, relation) {
|
|
7237
7777
|
if (relation.cascade === "all" || relation.cascade === "remove") {
|
|
7238
7778
|
this.unitOfWork.markRemoved(child);
|
|
@@ -7241,6 +7781,12 @@ var RelationChangeProcessor = class {
|
|
|
7241
7781
|
child[relation.foreignKey] = null;
|
|
7242
7782
|
this.unitOfWork.markDirty(child);
|
|
7243
7783
|
}
|
|
7784
|
+
/**
|
|
7785
|
+
* Inserts a pivot row for belongs-to-many relations.
|
|
7786
|
+
* @param relation - The belongs-to-many relation
|
|
7787
|
+
* @param rootId - The root entity's primary key value
|
|
7788
|
+
* @param targetId - The target entity's primary key value
|
|
7789
|
+
*/
|
|
7244
7790
|
async insertPivotRow(relation, rootId, targetId) {
|
|
7245
7791
|
const payload = {
|
|
7246
7792
|
[relation.pivotForeignKeyToRoot]: rootId,
|
|
@@ -7250,6 +7796,12 @@ var RelationChangeProcessor = class {
|
|
|
7250
7796
|
const compiled = builder.compile(this.dialect);
|
|
7251
7797
|
await this.executor.executeSql(compiled.sql, compiled.params);
|
|
7252
7798
|
}
|
|
7799
|
+
/**
|
|
7800
|
+
* Deletes a pivot row for belongs-to-many relations.
|
|
7801
|
+
* @param relation - The belongs-to-many relation
|
|
7802
|
+
* @param rootId - The root entity's primary key value
|
|
7803
|
+
* @param targetId - The target entity's primary key value
|
|
7804
|
+
*/
|
|
7253
7805
|
async deletePivotRow(relation, rootId, targetId) {
|
|
7254
7806
|
const rootCol = relation.pivotTable.columns[relation.pivotForeignKeyToRoot];
|
|
7255
7807
|
const targetCol = relation.pivotTable.columns[relation.pivotForeignKeyToTarget];
|
|
@@ -7260,6 +7812,12 @@ var RelationChangeProcessor = class {
|
|
|
7260
7812
|
const compiled = builder.compile(this.dialect);
|
|
7261
7813
|
await this.executor.executeSql(compiled.sql, compiled.params);
|
|
7262
7814
|
}
|
|
7815
|
+
/**
|
|
7816
|
+
* Resolves the primary key value from an entity.
|
|
7817
|
+
* @param entity - The entity
|
|
7818
|
+
* @param table - The table definition
|
|
7819
|
+
* @returns The primary key value or null
|
|
7820
|
+
*/
|
|
7263
7821
|
resolvePrimaryKeyValue(entity, table) {
|
|
7264
7822
|
if (!entity) return null;
|
|
7265
7823
|
const key = findPrimaryKey(table);
|
|
@@ -7275,42 +7833,231 @@ var createQueryLoggingExecutor = (executor, logger) => {
|
|
|
7275
7833
|
return executor;
|
|
7276
7834
|
}
|
|
7277
7835
|
const wrapped = {
|
|
7836
|
+
capabilities: executor.capabilities,
|
|
7278
7837
|
async executeSql(sql, params) {
|
|
7279
7838
|
logger({ sql, params });
|
|
7280
7839
|
return executor.executeSql(sql, params);
|
|
7281
|
-
}
|
|
7840
|
+
},
|
|
7841
|
+
beginTransaction: () => executor.beginTransaction(),
|
|
7842
|
+
commitTransaction: () => executor.commitTransaction(),
|
|
7843
|
+
rollbackTransaction: () => executor.rollbackTransaction(),
|
|
7844
|
+
dispose: () => executor.dispose()
|
|
7282
7845
|
};
|
|
7283
|
-
if (executor.beginTransaction) {
|
|
7284
|
-
wrapped.beginTransaction = executor.beginTransaction.bind(executor);
|
|
7285
|
-
}
|
|
7286
|
-
if (executor.commitTransaction) {
|
|
7287
|
-
wrapped.commitTransaction = executor.commitTransaction.bind(executor);
|
|
7288
|
-
}
|
|
7289
|
-
if (executor.rollbackTransaction) {
|
|
7290
|
-
wrapped.rollbackTransaction = executor.rollbackTransaction.bind(executor);
|
|
7291
|
-
}
|
|
7292
7846
|
return wrapped;
|
|
7293
7847
|
};
|
|
7294
7848
|
|
|
7295
7849
|
// src/orm/transaction-runner.ts
|
|
7296
7850
|
var runInTransaction = async (executor, action) => {
|
|
7297
|
-
if (!executor.
|
|
7851
|
+
if (!executor.capabilities.transactions) {
|
|
7298
7852
|
await action();
|
|
7299
7853
|
return;
|
|
7300
7854
|
}
|
|
7301
7855
|
await executor.beginTransaction();
|
|
7302
7856
|
try {
|
|
7303
7857
|
await action();
|
|
7304
|
-
await executor.commitTransaction
|
|
7858
|
+
await executor.commitTransaction();
|
|
7305
7859
|
} catch (error) {
|
|
7306
|
-
await executor.rollbackTransaction
|
|
7860
|
+
await executor.rollbackTransaction();
|
|
7307
7861
|
throw error;
|
|
7308
7862
|
}
|
|
7309
7863
|
};
|
|
7310
7864
|
|
|
7865
|
+
// src/orm/save-graph.ts
|
|
7866
|
+
var toKey8 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
7867
|
+
var pickColumns = (table, payload) => {
|
|
7868
|
+
const columns = {};
|
|
7869
|
+
for (const key of Object.keys(table.columns)) {
|
|
7870
|
+
if (payload[key] !== void 0) {
|
|
7871
|
+
columns[key] = payload[key];
|
|
7872
|
+
}
|
|
7873
|
+
}
|
|
7874
|
+
return columns;
|
|
7875
|
+
};
|
|
7876
|
+
var ensureEntity = (session, table, payload) => {
|
|
7877
|
+
const pk = findPrimaryKey(table);
|
|
7878
|
+
const row = pickColumns(table, payload);
|
|
7879
|
+
const pkValue = payload[pk];
|
|
7880
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
7881
|
+
const tracked = session.getEntity(table, pkValue);
|
|
7882
|
+
if (tracked) {
|
|
7883
|
+
return tracked;
|
|
7884
|
+
}
|
|
7885
|
+
if (row[pk] === void 0) {
|
|
7886
|
+
row[pk] = pkValue;
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
return createEntityFromRow(session, table, row);
|
|
7890
|
+
};
|
|
7891
|
+
var assignColumns = (table, entity, payload) => {
|
|
7892
|
+
for (const key of Object.keys(table.columns)) {
|
|
7893
|
+
if (payload[key] !== void 0) {
|
|
7894
|
+
entity[key] = payload[key];
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
};
|
|
7898
|
+
var isEntityInCollection = (items, pkName, entity) => {
|
|
7899
|
+
if (items.includes(entity)) return true;
|
|
7900
|
+
const entityPk = entity[pkName];
|
|
7901
|
+
if (entityPk === void 0 || entityPk === null) return false;
|
|
7902
|
+
return items.some((item) => toKey8(item[pkName]) === toKey8(entityPk));
|
|
7903
|
+
};
|
|
7904
|
+
var findInCollectionByPk = (items, pkName, pkValue) => {
|
|
7905
|
+
if (pkValue === void 0 || pkValue === null) return void 0;
|
|
7906
|
+
return items.find((item) => toKey8(item[pkName]) === toKey8(pkValue));
|
|
7907
|
+
};
|
|
7908
|
+
var handleHasMany = async (session, root, relationName, relation, payload, options) => {
|
|
7909
|
+
if (!Array.isArray(payload)) return;
|
|
7910
|
+
const collection = root[relationName];
|
|
7911
|
+
await collection.load();
|
|
7912
|
+
const targetTable = relation.target;
|
|
7913
|
+
const targetPk = findPrimaryKey(targetTable);
|
|
7914
|
+
const existing = collection.getItems();
|
|
7915
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7916
|
+
for (const item of payload) {
|
|
7917
|
+
if (item === null || item === void 0) continue;
|
|
7918
|
+
const asObj = typeof item === "object" ? item : { [targetPk]: item };
|
|
7919
|
+
const pkValue = asObj[targetPk];
|
|
7920
|
+
const current = findInCollectionByPk(existing, targetPk, pkValue) ?? (pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) : void 0);
|
|
7921
|
+
const entity = current ?? ensureEntity(session, targetTable, asObj);
|
|
7922
|
+
assignColumns(targetTable, entity, asObj);
|
|
7923
|
+
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
7924
|
+
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
7925
|
+
collection.attach(entity);
|
|
7926
|
+
}
|
|
7927
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
7928
|
+
seen.add(toKey8(pkValue));
|
|
7929
|
+
}
|
|
7930
|
+
}
|
|
7931
|
+
if (options.pruneMissing) {
|
|
7932
|
+
for (const item of [...collection.getItems()]) {
|
|
7933
|
+
const pkValue = item[targetPk];
|
|
7934
|
+
if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
|
|
7935
|
+
collection.remove(item);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
};
|
|
7940
|
+
var handleHasOne = async (session, root, relationName, relation, payload, options) => {
|
|
7941
|
+
const ref = root[relationName];
|
|
7942
|
+
if (payload === void 0) return;
|
|
7943
|
+
if (payload === null) {
|
|
7944
|
+
ref.set(null);
|
|
7945
|
+
return;
|
|
7946
|
+
}
|
|
7947
|
+
const pk = findPrimaryKey(relation.target);
|
|
7948
|
+
if (typeof payload === "number" || typeof payload === "string") {
|
|
7949
|
+
const entity = ref.set({ [pk]: payload });
|
|
7950
|
+
if (entity) {
|
|
7951
|
+
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
7952
|
+
}
|
|
7953
|
+
return;
|
|
7954
|
+
}
|
|
7955
|
+
const attached = ref.set(payload);
|
|
7956
|
+
if (attached) {
|
|
7957
|
+
await applyGraphToEntity(session, relation.target, attached, payload, options);
|
|
7958
|
+
}
|
|
7959
|
+
};
|
|
7960
|
+
var handleBelongsTo = async (session, root, relationName, relation, payload, options) => {
|
|
7961
|
+
const ref = root[relationName];
|
|
7962
|
+
if (payload === void 0) return;
|
|
7963
|
+
if (payload === null) {
|
|
7964
|
+
ref.set(null);
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
const pk = relation.localKey || findPrimaryKey(relation.target);
|
|
7968
|
+
if (typeof payload === "number" || typeof payload === "string") {
|
|
7969
|
+
const entity = ref.set({ [pk]: payload });
|
|
7970
|
+
if (entity) {
|
|
7971
|
+
await applyGraphToEntity(session, relation.target, entity, { [pk]: payload }, options);
|
|
7972
|
+
}
|
|
7973
|
+
return;
|
|
7974
|
+
}
|
|
7975
|
+
const attached = ref.set(payload);
|
|
7976
|
+
if (attached) {
|
|
7977
|
+
await applyGraphToEntity(session, relation.target, attached, payload, options);
|
|
7978
|
+
}
|
|
7979
|
+
};
|
|
7980
|
+
var handleBelongsToMany = async (session, root, relationName, relation, payload, options) => {
|
|
7981
|
+
if (!Array.isArray(payload)) return;
|
|
7982
|
+
const collection = root[relationName];
|
|
7983
|
+
await collection.load();
|
|
7984
|
+
const targetTable = relation.target;
|
|
7985
|
+
const targetPk = relation.targetKey || findPrimaryKey(targetTable);
|
|
7986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7987
|
+
for (const item of payload) {
|
|
7988
|
+
if (item === null || item === void 0) continue;
|
|
7989
|
+
if (typeof item === "number" || typeof item === "string") {
|
|
7990
|
+
const id = item;
|
|
7991
|
+
collection.attach(id);
|
|
7992
|
+
seen.add(toKey8(id));
|
|
7993
|
+
continue;
|
|
7994
|
+
}
|
|
7995
|
+
const asObj = item;
|
|
7996
|
+
const pkValue = asObj[targetPk];
|
|
7997
|
+
const entity = pkValue !== void 0 && pkValue !== null ? session.getEntity(targetTable, pkValue) ?? ensureEntity(session, targetTable, asObj) : ensureEntity(session, targetTable, asObj);
|
|
7998
|
+
assignColumns(targetTable, entity, asObj);
|
|
7999
|
+
await applyGraphToEntity(session, targetTable, entity, asObj, options);
|
|
8000
|
+
if (!isEntityInCollection(collection.getItems(), targetPk, entity)) {
|
|
8001
|
+
collection.attach(entity);
|
|
8002
|
+
}
|
|
8003
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
8004
|
+
seen.add(toKey8(pkValue));
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
if (options.pruneMissing) {
|
|
8008
|
+
for (const item of [...collection.getItems()]) {
|
|
8009
|
+
const pkValue = item[targetPk];
|
|
8010
|
+
if (pkValue !== void 0 && pkValue !== null && !seen.has(toKey8(pkValue))) {
|
|
8011
|
+
collection.detach(item);
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
};
|
|
8016
|
+
var applyRelation = async (session, table, entity, relationName, relation, payload, options) => {
|
|
8017
|
+
switch (relation.type) {
|
|
8018
|
+
case RelationKinds.HasMany:
|
|
8019
|
+
return handleHasMany(session, entity, relationName, relation, payload, options);
|
|
8020
|
+
case RelationKinds.HasOne:
|
|
8021
|
+
return handleHasOne(session, entity, relationName, relation, payload, options);
|
|
8022
|
+
case RelationKinds.BelongsTo:
|
|
8023
|
+
return handleBelongsTo(session, entity, relationName, relation, payload, options);
|
|
8024
|
+
case RelationKinds.BelongsToMany:
|
|
8025
|
+
return handleBelongsToMany(session, entity, relationName, relation, payload, options);
|
|
8026
|
+
}
|
|
8027
|
+
};
|
|
8028
|
+
var applyGraphToEntity = async (session, table, entity, payload, options) => {
|
|
8029
|
+
assignColumns(table, entity, payload);
|
|
8030
|
+
for (const [relationName, relation] of Object.entries(table.relations)) {
|
|
8031
|
+
if (!(relationName in payload)) continue;
|
|
8032
|
+
await applyRelation(session, table, entity, relationName, relation, payload[relationName], options);
|
|
8033
|
+
}
|
|
8034
|
+
};
|
|
8035
|
+
var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
|
|
8036
|
+
const table = getTableDefFromEntity(entityClass);
|
|
8037
|
+
if (!table) {
|
|
8038
|
+
throw new Error("Entity metadata has not been bootstrapped");
|
|
8039
|
+
}
|
|
8040
|
+
const root = ensureEntity(session, table, payload);
|
|
8041
|
+
await applyGraphToEntity(session, table, root, payload, options);
|
|
8042
|
+
return root;
|
|
8043
|
+
};
|
|
8044
|
+
|
|
7311
8045
|
// src/orm/orm-session.ts
|
|
7312
8046
|
var OrmSession = class {
|
|
8047
|
+
/**
|
|
8048
|
+
* Creates a new OrmSession instance.
|
|
8049
|
+
* @param opts - Session options
|
|
8050
|
+
*/
|
|
7313
8051
|
constructor(opts) {
|
|
8052
|
+
/**
|
|
8053
|
+
* Registers a relation change.
|
|
8054
|
+
* @param root - The root entity
|
|
8055
|
+
* @param relationKey - The relation key
|
|
8056
|
+
* @param rootTable - The root table definition
|
|
8057
|
+
* @param relationName - The relation name
|
|
8058
|
+
* @param relation - The relation definition
|
|
8059
|
+
* @param change - The relation change
|
|
8060
|
+
*/
|
|
7314
8061
|
this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
|
|
7315
8062
|
this.relationChanges.registerChange(
|
|
7316
8063
|
buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
|
|
@@ -7324,42 +8071,117 @@ var OrmSession = class {
|
|
|
7324
8071
|
this.relationChanges = new RelationChangeProcessor(this.unitOfWork, this.orm.dialect, this.executor);
|
|
7325
8072
|
this.domainEvents = new DomainEventBus(opts.domainEventHandlers);
|
|
7326
8073
|
}
|
|
8074
|
+
/**
|
|
8075
|
+
* Releases resources associated with this session (executor/pool leases) and resets tracking.
|
|
8076
|
+
* Must be safe to call multiple times.
|
|
8077
|
+
*/
|
|
8078
|
+
async dispose() {
|
|
8079
|
+
try {
|
|
8080
|
+
await this.executor.dispose();
|
|
8081
|
+
} finally {
|
|
8082
|
+
this.unitOfWork.reset();
|
|
8083
|
+
this.relationChanges.reset();
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
8086
|
+
/**
|
|
8087
|
+
* Gets the database dialect.
|
|
8088
|
+
*/
|
|
7327
8089
|
get dialect() {
|
|
7328
8090
|
return this.orm.dialect;
|
|
7329
8091
|
}
|
|
8092
|
+
/**
|
|
8093
|
+
* Gets the identity buckets map.
|
|
8094
|
+
*/
|
|
7330
8095
|
get identityBuckets() {
|
|
7331
8096
|
return this.unitOfWork.identityBuckets;
|
|
7332
8097
|
}
|
|
8098
|
+
/**
|
|
8099
|
+
* Gets all tracked entities.
|
|
8100
|
+
*/
|
|
7333
8101
|
get tracked() {
|
|
7334
8102
|
return this.unitOfWork.getTracked();
|
|
7335
8103
|
}
|
|
8104
|
+
/**
|
|
8105
|
+
* Gets an entity by table and primary key.
|
|
8106
|
+
* @param table - The table definition
|
|
8107
|
+
* @param pk - The primary key value
|
|
8108
|
+
* @returns The entity or undefined if not found
|
|
8109
|
+
*/
|
|
7336
8110
|
getEntity(table, pk) {
|
|
7337
8111
|
return this.unitOfWork.getEntity(table, pk);
|
|
7338
8112
|
}
|
|
8113
|
+
/**
|
|
8114
|
+
* Sets an entity in the identity map.
|
|
8115
|
+
* @param table - The table definition
|
|
8116
|
+
* @param pk - The primary key value
|
|
8117
|
+
* @param entity - The entity instance
|
|
8118
|
+
*/
|
|
7339
8119
|
setEntity(table, pk, entity) {
|
|
7340
8120
|
this.unitOfWork.setEntity(table, pk, entity);
|
|
7341
8121
|
}
|
|
8122
|
+
/**
|
|
8123
|
+
* Tracks a new entity.
|
|
8124
|
+
* @param table - The table definition
|
|
8125
|
+
* @param entity - The entity instance
|
|
8126
|
+
* @param pk - Optional primary key value
|
|
8127
|
+
*/
|
|
7342
8128
|
trackNew(table, entity, pk) {
|
|
7343
8129
|
this.unitOfWork.trackNew(table, entity, pk);
|
|
7344
8130
|
}
|
|
8131
|
+
/**
|
|
8132
|
+
* Tracks a managed entity.
|
|
8133
|
+
* @param table - The table definition
|
|
8134
|
+
* @param pk - The primary key value
|
|
8135
|
+
* @param entity - The entity instance
|
|
8136
|
+
*/
|
|
7345
8137
|
trackManaged(table, pk, entity) {
|
|
7346
8138
|
this.unitOfWork.trackManaged(table, pk, entity);
|
|
7347
8139
|
}
|
|
8140
|
+
/**
|
|
8141
|
+
* Marks an entity as dirty (modified).
|
|
8142
|
+
* @param entity - The entity to mark as dirty
|
|
8143
|
+
*/
|
|
7348
8144
|
markDirty(entity) {
|
|
7349
8145
|
this.unitOfWork.markDirty(entity);
|
|
7350
8146
|
}
|
|
8147
|
+
/**
|
|
8148
|
+
* Marks an entity as removed.
|
|
8149
|
+
* @param entity - The entity to mark as removed
|
|
8150
|
+
*/
|
|
7351
8151
|
markRemoved(entity) {
|
|
7352
8152
|
this.unitOfWork.markRemoved(entity);
|
|
7353
8153
|
}
|
|
8154
|
+
/**
|
|
8155
|
+
* Gets all tracked entities for a specific table.
|
|
8156
|
+
* @param table - The table definition
|
|
8157
|
+
* @returns Array of tracked entities
|
|
8158
|
+
*/
|
|
7354
8159
|
getEntitiesForTable(table) {
|
|
7355
8160
|
return this.unitOfWork.getEntitiesForTable(table);
|
|
7356
8161
|
}
|
|
8162
|
+
/**
|
|
8163
|
+
* Registers an interceptor for flush lifecycle hooks.
|
|
8164
|
+
* @param interceptor - The interceptor to register
|
|
8165
|
+
*/
|
|
7357
8166
|
registerInterceptor(interceptor) {
|
|
7358
8167
|
this.interceptors.push(interceptor);
|
|
7359
8168
|
}
|
|
8169
|
+
/**
|
|
8170
|
+
* Registers a domain event handler.
|
|
8171
|
+
* @param type - The event type
|
|
8172
|
+
* @param handler - The event handler
|
|
8173
|
+
*/
|
|
7360
8174
|
registerDomainEventHandler(type, handler) {
|
|
7361
8175
|
this.domainEvents.on(type, handler);
|
|
7362
8176
|
}
|
|
8177
|
+
/**
|
|
8178
|
+
* Finds an entity by its primary key.
|
|
8179
|
+
* @template TCtor - The entity constructor type
|
|
8180
|
+
* @param entityClass - The entity constructor
|
|
8181
|
+
* @param id - The primary key value
|
|
8182
|
+
* @returns The entity instance or null if not found
|
|
8183
|
+
* @throws If entity metadata is not bootstrapped or table has no primary key
|
|
8184
|
+
*/
|
|
7363
8185
|
async find(entityClass, id) {
|
|
7364
8186
|
const table = getTableDefFromEntity(entityClass);
|
|
7365
8187
|
if (!table) {
|
|
@@ -7378,14 +8200,46 @@ var OrmSession = class {
|
|
|
7378
8200
|
const rows = await executeHydrated(this, qb);
|
|
7379
8201
|
return rows[0] ?? null;
|
|
7380
8202
|
}
|
|
8203
|
+
/**
|
|
8204
|
+
* Finds a single entity using a query builder.
|
|
8205
|
+
* @template TTable - The table type
|
|
8206
|
+
* @param qb - The query builder
|
|
8207
|
+
* @returns The first entity instance or null if not found
|
|
8208
|
+
*/
|
|
7381
8209
|
async findOne(qb) {
|
|
7382
8210
|
const limited = qb.limit(1);
|
|
7383
8211
|
const rows = await executeHydrated(this, limited);
|
|
7384
8212
|
return rows[0] ?? null;
|
|
7385
8213
|
}
|
|
8214
|
+
/**
|
|
8215
|
+
* Finds multiple entities using a query builder.
|
|
8216
|
+
* @template TTable - The table type
|
|
8217
|
+
* @param qb - The query builder
|
|
8218
|
+
* @returns Array of entity instances
|
|
8219
|
+
*/
|
|
7386
8220
|
async findMany(qb) {
|
|
7387
8221
|
return executeHydrated(this, qb);
|
|
7388
8222
|
}
|
|
8223
|
+
/**
|
|
8224
|
+
* Saves an entity graph (root + nested relations) based on a DTO-like payload.
|
|
8225
|
+
* @param entityClass - Root entity constructor
|
|
8226
|
+
* @param payload - DTO payload containing column values and nested relations
|
|
8227
|
+
* @param options - Graph save options
|
|
8228
|
+
* @returns The root entity instance
|
|
8229
|
+
*/
|
|
8230
|
+
async saveGraph(entityClass, payload, options) {
|
|
8231
|
+
const { transactional = true, ...graphOptions } = options ?? {};
|
|
8232
|
+
const execute = () => saveGraphInternal(this, entityClass, payload, graphOptions);
|
|
8233
|
+
if (!transactional) {
|
|
8234
|
+
return execute();
|
|
8235
|
+
}
|
|
8236
|
+
return this.transaction(() => execute());
|
|
8237
|
+
}
|
|
8238
|
+
/**
|
|
8239
|
+
* Persists an entity (either inserts or updates).
|
|
8240
|
+
* @param entity - The entity to persist
|
|
8241
|
+
* @throws If entity metadata is not bootstrapped
|
|
8242
|
+
*/
|
|
7389
8243
|
async persist(entity) {
|
|
7390
8244
|
if (this.unitOfWork.findTracked(entity)) {
|
|
7391
8245
|
return;
|
|
@@ -7402,12 +8256,22 @@ var OrmSession = class {
|
|
|
7402
8256
|
this.trackNew(table, entity);
|
|
7403
8257
|
}
|
|
7404
8258
|
}
|
|
8259
|
+
/**
|
|
8260
|
+
* Marks an entity for removal.
|
|
8261
|
+
* @param entity - The entity to remove
|
|
8262
|
+
*/
|
|
7405
8263
|
async remove(entity) {
|
|
7406
8264
|
this.markRemoved(entity);
|
|
7407
8265
|
}
|
|
8266
|
+
/**
|
|
8267
|
+
* Flushes pending changes to the database.
|
|
8268
|
+
*/
|
|
7408
8269
|
async flush() {
|
|
7409
8270
|
await this.unitOfWork.flush();
|
|
7410
8271
|
}
|
|
8272
|
+
/**
|
|
8273
|
+
* Flushes pending changes with interceptors and relation processing.
|
|
8274
|
+
*/
|
|
7411
8275
|
async flushWithHooks() {
|
|
7412
8276
|
for (const interceptor of this.interceptors) {
|
|
7413
8277
|
await interceptor.beforeFlush?.(this);
|
|
@@ -7419,14 +8283,24 @@ var OrmSession = class {
|
|
|
7419
8283
|
await interceptor.afterFlush?.(this);
|
|
7420
8284
|
}
|
|
7421
8285
|
}
|
|
8286
|
+
/**
|
|
8287
|
+
* Commits the current transaction.
|
|
8288
|
+
*/
|
|
7422
8289
|
async commit() {
|
|
7423
8290
|
await runInTransaction(this.executor, async () => {
|
|
7424
8291
|
await this.flushWithHooks();
|
|
7425
8292
|
});
|
|
7426
8293
|
await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
|
|
7427
8294
|
}
|
|
8295
|
+
/**
|
|
8296
|
+
* Executes a function within a transaction.
|
|
8297
|
+
* @template T - The return type
|
|
8298
|
+
* @param fn - The function to execute
|
|
8299
|
+
* @returns The result of the function
|
|
8300
|
+
* @throws If the transaction fails
|
|
8301
|
+
*/
|
|
7428
8302
|
async transaction(fn4) {
|
|
7429
|
-
if (!this.executor.
|
|
8303
|
+
if (!this.executor.capabilities.transactions) {
|
|
7430
8304
|
const result = await fn4(this);
|
|
7431
8305
|
await this.commit();
|
|
7432
8306
|
return result;
|
|
@@ -7435,7 +8309,7 @@ var OrmSession = class {
|
|
|
7435
8309
|
try {
|
|
7436
8310
|
const result = await fn4(this);
|
|
7437
8311
|
await this.flushWithHooks();
|
|
7438
|
-
await this.executor.commitTransaction
|
|
8312
|
+
await this.executor.commitTransaction();
|
|
7439
8313
|
await this.domainEvents.dispatch(this.unitOfWork.getTracked(), this);
|
|
7440
8314
|
return result;
|
|
7441
8315
|
} catch (err) {
|
|
@@ -7443,11 +8317,20 @@ var OrmSession = class {
|
|
|
7443
8317
|
throw err;
|
|
7444
8318
|
}
|
|
7445
8319
|
}
|
|
8320
|
+
/**
|
|
8321
|
+
* Rolls back the current transaction.
|
|
8322
|
+
*/
|
|
7446
8323
|
async rollback() {
|
|
7447
|
-
|
|
8324
|
+
if (this.executor.capabilities.transactions) {
|
|
8325
|
+
await this.executor.rollbackTransaction();
|
|
8326
|
+
}
|
|
7448
8327
|
this.unitOfWork.reset();
|
|
7449
8328
|
this.relationChanges.reset();
|
|
7450
8329
|
}
|
|
8330
|
+
/**
|
|
8331
|
+
* Gets the execution context.
|
|
8332
|
+
* @returns The execution context
|
|
8333
|
+
*/
|
|
7451
8334
|
getExecutionContext() {
|
|
7452
8335
|
return {
|
|
7453
8336
|
dialect: this.orm.dialect,
|
|
@@ -7455,6 +8338,10 @@ var OrmSession = class {
|
|
|
7455
8338
|
interceptors: this.orm.interceptors
|
|
7456
8339
|
};
|
|
7457
8340
|
}
|
|
8341
|
+
/**
|
|
8342
|
+
* Gets the hydration context.
|
|
8343
|
+
* @returns The hydration context
|
|
8344
|
+
*/
|
|
7458
8345
|
getHydrationContext() {
|
|
7459
8346
|
return {
|
|
7460
8347
|
identityMap: this.identityMap,
|
|
@@ -7497,29 +8384,49 @@ var InterceptorPipeline = class {
|
|
|
7497
8384
|
|
|
7498
8385
|
// src/orm/orm.ts
|
|
7499
8386
|
var Orm = class {
|
|
8387
|
+
/**
|
|
8388
|
+
* Creates a new ORM instance.
|
|
8389
|
+
* @param opts - ORM options
|
|
8390
|
+
*/
|
|
7500
8391
|
constructor(opts) {
|
|
7501
8392
|
this.dialect = opts.dialect;
|
|
7502
8393
|
this.interceptors = opts.interceptors ?? new InterceptorPipeline();
|
|
7503
8394
|
this.namingStrategy = opts.namingStrategy ?? new DefaultNamingStrategy();
|
|
7504
8395
|
this.executorFactory = opts.executorFactory;
|
|
7505
8396
|
}
|
|
7506
|
-
|
|
7507
|
-
|
|
8397
|
+
/**
|
|
8398
|
+
* Creates a new ORM session.
|
|
8399
|
+
* @param options - Optional session options
|
|
8400
|
+
* @returns The ORM session
|
|
8401
|
+
*/
|
|
8402
|
+
createSession() {
|
|
8403
|
+
const executor = this.executorFactory.createExecutor();
|
|
7508
8404
|
return new OrmSession({ orm: this, executor });
|
|
7509
8405
|
}
|
|
8406
|
+
/**
|
|
8407
|
+
* Executes a function within a transaction.
|
|
8408
|
+
* @template T - The return type
|
|
8409
|
+
* @param fn - The function to execute
|
|
8410
|
+
* @returns The result of the function
|
|
8411
|
+
* @throws If the transaction fails
|
|
8412
|
+
*/
|
|
7510
8413
|
async transaction(fn4) {
|
|
7511
8414
|
const executor = this.executorFactory.createTransactionalExecutor();
|
|
7512
8415
|
const session = new OrmSession({ orm: this, executor });
|
|
7513
8416
|
try {
|
|
7514
|
-
|
|
7515
|
-
await session.commit();
|
|
7516
|
-
return result;
|
|
8417
|
+
return await session.transaction(() => fn4(session));
|
|
7517
8418
|
} catch (err) {
|
|
7518
|
-
await session.rollback();
|
|
7519
8419
|
throw err;
|
|
7520
8420
|
} finally {
|
|
8421
|
+
await session.dispose();
|
|
7521
8422
|
}
|
|
7522
8423
|
}
|
|
8424
|
+
/**
|
|
8425
|
+
* Shuts down the ORM and releases underlying resources (pools, timers).
|
|
8426
|
+
*/
|
|
8427
|
+
async dispose() {
|
|
8428
|
+
await this.executorFactory.dispose();
|
|
8429
|
+
}
|
|
7523
8430
|
};
|
|
7524
8431
|
|
|
7525
8432
|
// src/decorators/decorator-metadata.ts
|
|
@@ -7778,18 +8685,245 @@ function rowsToQueryResult(rows) {
|
|
|
7778
8685
|
return { columns, values };
|
|
7779
8686
|
}
|
|
7780
8687
|
function createExecutorFromQueryRunner(runner) {
|
|
8688
|
+
const supportsTransactions = typeof runner.beginTransaction === "function" && typeof runner.commitTransaction === "function" && typeof runner.rollbackTransaction === "function";
|
|
7781
8689
|
return {
|
|
8690
|
+
capabilities: {
|
|
8691
|
+
transactions: supportsTransactions
|
|
8692
|
+
},
|
|
7782
8693
|
async executeSql(sql, params) {
|
|
7783
8694
|
const rows = await runner.query(sql, params);
|
|
7784
8695
|
const result = rowsToQueryResult(rows);
|
|
7785
8696
|
return [result];
|
|
7786
8697
|
},
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
8698
|
+
async beginTransaction() {
|
|
8699
|
+
if (!supportsTransactions) {
|
|
8700
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8701
|
+
}
|
|
8702
|
+
await runner.beginTransaction.call(runner);
|
|
8703
|
+
},
|
|
8704
|
+
async commitTransaction() {
|
|
8705
|
+
if (!supportsTransactions) {
|
|
8706
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8707
|
+
}
|
|
8708
|
+
await runner.commitTransaction.call(runner);
|
|
8709
|
+
},
|
|
8710
|
+
async rollbackTransaction() {
|
|
8711
|
+
if (!supportsTransactions) {
|
|
8712
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8713
|
+
}
|
|
8714
|
+
await runner.rollbackTransaction.call(runner);
|
|
8715
|
+
},
|
|
8716
|
+
async dispose() {
|
|
8717
|
+
await runner.dispose?.call(runner);
|
|
8718
|
+
}
|
|
7790
8719
|
};
|
|
7791
8720
|
}
|
|
7792
8721
|
|
|
8722
|
+
// src/core/execution/pooling/pool.ts
|
|
8723
|
+
var deferred = () => {
|
|
8724
|
+
let resolve;
|
|
8725
|
+
let reject;
|
|
8726
|
+
const promise = new Promise((res, rej) => {
|
|
8727
|
+
resolve = res;
|
|
8728
|
+
reject = rej;
|
|
8729
|
+
});
|
|
8730
|
+
return { promise, resolve, reject };
|
|
8731
|
+
};
|
|
8732
|
+
var Pool = class {
|
|
8733
|
+
constructor(adapter, options) {
|
|
8734
|
+
this.destroyed = false;
|
|
8735
|
+
this.creating = 0;
|
|
8736
|
+
this.leased = 0;
|
|
8737
|
+
this.idle = [];
|
|
8738
|
+
this.waiters = [];
|
|
8739
|
+
this.reapTimer = null;
|
|
8740
|
+
if (!Number.isFinite(options.max) || options.max <= 0) {
|
|
8741
|
+
throw new Error("Pool options.max must be a positive number");
|
|
8742
|
+
}
|
|
8743
|
+
this.adapter = adapter;
|
|
8744
|
+
this.options = { max: options.max, ...options };
|
|
8745
|
+
const idleTimeout = this.options.idleTimeoutMillis;
|
|
8746
|
+
if (idleTimeout && idleTimeout > 0) {
|
|
8747
|
+
const interval = this.options.reapIntervalMillis ?? Math.max(1e3, Math.floor(idleTimeout / 2));
|
|
8748
|
+
this.reapTimer = setInterval(() => {
|
|
8749
|
+
void this.reapIdle();
|
|
8750
|
+
}, interval);
|
|
8751
|
+
this.reapTimer.unref?.();
|
|
8752
|
+
}
|
|
8753
|
+
const min2 = this.options.min ?? 0;
|
|
8754
|
+
if (min2 > 0) {
|
|
8755
|
+
void this.warm(min2);
|
|
8756
|
+
}
|
|
8757
|
+
}
|
|
8758
|
+
/**
|
|
8759
|
+
* Acquire a resource lease.
|
|
8760
|
+
* The returned lease MUST be released or destroyed.
|
|
8761
|
+
*/
|
|
8762
|
+
async acquire() {
|
|
8763
|
+
if (this.destroyed) {
|
|
8764
|
+
throw new Error("Pool is destroyed");
|
|
8765
|
+
}
|
|
8766
|
+
const idle = await this.takeIdleValidated();
|
|
8767
|
+
if (idle) {
|
|
8768
|
+
this.leased++;
|
|
8769
|
+
return this.makeLease(idle);
|
|
8770
|
+
}
|
|
8771
|
+
if (this.totalLive() < this.options.max) {
|
|
8772
|
+
this.creating++;
|
|
8773
|
+
try {
|
|
8774
|
+
const created = await this.adapter.create();
|
|
8775
|
+
this.leased++;
|
|
8776
|
+
return this.makeLease(created);
|
|
8777
|
+
} finally {
|
|
8778
|
+
this.creating--;
|
|
8779
|
+
}
|
|
8780
|
+
}
|
|
8781
|
+
const waiter = deferred();
|
|
8782
|
+
this.waiters.push(waiter);
|
|
8783
|
+
const timeout = this.options.acquireTimeoutMillis;
|
|
8784
|
+
let timer = null;
|
|
8785
|
+
if (timeout && timeout > 0) {
|
|
8786
|
+
timer = setTimeout(() => {
|
|
8787
|
+
const idx = this.waiters.indexOf(waiter);
|
|
8788
|
+
if (idx >= 0) this.waiters.splice(idx, 1);
|
|
8789
|
+
waiter.reject(new Error("Pool acquire timeout"));
|
|
8790
|
+
}, timeout);
|
|
8791
|
+
timer.unref?.();
|
|
8792
|
+
}
|
|
8793
|
+
try {
|
|
8794
|
+
return await waiter.promise;
|
|
8795
|
+
} finally {
|
|
8796
|
+
if (timer) clearTimeout(timer);
|
|
8797
|
+
}
|
|
8798
|
+
}
|
|
8799
|
+
/** Destroy pool and all idle resources; waits for in-flight creations to settle. */
|
|
8800
|
+
async destroy() {
|
|
8801
|
+
if (this.destroyed) return;
|
|
8802
|
+
this.destroyed = true;
|
|
8803
|
+
if (this.reapTimer) {
|
|
8804
|
+
clearInterval(this.reapTimer);
|
|
8805
|
+
this.reapTimer = null;
|
|
8806
|
+
}
|
|
8807
|
+
while (this.waiters.length) {
|
|
8808
|
+
this.waiters.shift().reject(new Error("Pool destroyed"));
|
|
8809
|
+
}
|
|
8810
|
+
while (this.idle.length) {
|
|
8811
|
+
const entry = this.idle.shift();
|
|
8812
|
+
await this.adapter.destroy(entry.resource);
|
|
8813
|
+
}
|
|
8814
|
+
}
|
|
8815
|
+
totalLive() {
|
|
8816
|
+
return this.idle.length + this.leased + this.creating;
|
|
8817
|
+
}
|
|
8818
|
+
makeLease(resource) {
|
|
8819
|
+
let done = false;
|
|
8820
|
+
return {
|
|
8821
|
+
resource,
|
|
8822
|
+
release: async () => {
|
|
8823
|
+
if (done) return;
|
|
8824
|
+
done = true;
|
|
8825
|
+
await this.releaseResource(resource);
|
|
8826
|
+
},
|
|
8827
|
+
destroy: async () => {
|
|
8828
|
+
if (done) return;
|
|
8829
|
+
done = true;
|
|
8830
|
+
await this.destroyResource(resource);
|
|
8831
|
+
}
|
|
8832
|
+
};
|
|
8833
|
+
}
|
|
8834
|
+
async releaseResource(resource) {
|
|
8835
|
+
this.leased = Math.max(0, this.leased - 1);
|
|
8836
|
+
if (this.destroyed) {
|
|
8837
|
+
await this.adapter.destroy(resource);
|
|
8838
|
+
return;
|
|
8839
|
+
}
|
|
8840
|
+
const next = this.waiters.shift();
|
|
8841
|
+
if (next) {
|
|
8842
|
+
this.leased++;
|
|
8843
|
+
next.resolve(this.makeLease(resource));
|
|
8844
|
+
return;
|
|
8845
|
+
}
|
|
8846
|
+
this.idle.push({ resource, lastUsedAt: Date.now() });
|
|
8847
|
+
await this.trimToMinMax();
|
|
8848
|
+
}
|
|
8849
|
+
async destroyResource(resource) {
|
|
8850
|
+
this.leased = Math.max(0, this.leased - 1);
|
|
8851
|
+
await this.adapter.destroy(resource);
|
|
8852
|
+
if (!this.destroyed && this.waiters.length && this.totalLive() < this.options.max) {
|
|
8853
|
+
const waiter = this.waiters.shift();
|
|
8854
|
+
this.creating++;
|
|
8855
|
+
try {
|
|
8856
|
+
const created = await this.adapter.create();
|
|
8857
|
+
this.leased++;
|
|
8858
|
+
waiter.resolve(this.makeLease(created));
|
|
8859
|
+
} catch (err) {
|
|
8860
|
+
waiter.reject(err);
|
|
8861
|
+
} finally {
|
|
8862
|
+
this.creating--;
|
|
8863
|
+
}
|
|
8864
|
+
}
|
|
8865
|
+
}
|
|
8866
|
+
async takeIdleValidated() {
|
|
8867
|
+
while (this.idle.length) {
|
|
8868
|
+
const entry = this.idle.pop();
|
|
8869
|
+
if (!this.adapter.validate) {
|
|
8870
|
+
return entry.resource;
|
|
8871
|
+
}
|
|
8872
|
+
const ok = await this.adapter.validate(entry.resource);
|
|
8873
|
+
if (ok) {
|
|
8874
|
+
return entry.resource;
|
|
8875
|
+
}
|
|
8876
|
+
await this.adapter.destroy(entry.resource);
|
|
8877
|
+
}
|
|
8878
|
+
return null;
|
|
8879
|
+
}
|
|
8880
|
+
async reapIdle() {
|
|
8881
|
+
if (this.destroyed) return;
|
|
8882
|
+
const idleTimeout = this.options.idleTimeoutMillis;
|
|
8883
|
+
if (!idleTimeout || idleTimeout <= 0) return;
|
|
8884
|
+
const now2 = Date.now();
|
|
8885
|
+
const min2 = this.options.min ?? 0;
|
|
8886
|
+
const keep = [];
|
|
8887
|
+
const kill = [];
|
|
8888
|
+
for (const entry of this.idle) {
|
|
8889
|
+
const expired = now2 - entry.lastUsedAt >= idleTimeout;
|
|
8890
|
+
if (expired) kill.push(entry);
|
|
8891
|
+
else keep.push(entry);
|
|
8892
|
+
}
|
|
8893
|
+
while (keep.length < min2 && kill.length) {
|
|
8894
|
+
keep.push(kill.pop());
|
|
8895
|
+
}
|
|
8896
|
+
this.idle.length = 0;
|
|
8897
|
+
this.idle.push(...keep);
|
|
8898
|
+
for (const entry of kill) {
|
|
8899
|
+
await this.adapter.destroy(entry.resource);
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
async warm(targetMin) {
|
|
8903
|
+
const min2 = Math.max(0, targetMin);
|
|
8904
|
+
while (!this.destroyed && this.idle.length < min2 && this.totalLive() < this.options.max) {
|
|
8905
|
+
this.creating++;
|
|
8906
|
+
try {
|
|
8907
|
+
const created = await this.adapter.create();
|
|
8908
|
+
this.idle.push({ resource: created, lastUsedAt: Date.now() });
|
|
8909
|
+
} catch {
|
|
8910
|
+
break;
|
|
8911
|
+
} finally {
|
|
8912
|
+
this.creating--;
|
|
8913
|
+
}
|
|
8914
|
+
}
|
|
8915
|
+
}
|
|
8916
|
+
async trimToMinMax() {
|
|
8917
|
+
const max2 = this.options.max;
|
|
8918
|
+
const min2 = this.options.min ?? 0;
|
|
8919
|
+
while (this.totalLive() > max2 && this.idle.length > min2) {
|
|
8920
|
+
const entry = this.idle.shift();
|
|
8921
|
+
if (!entry) break;
|
|
8922
|
+
await this.adapter.destroy(entry.resource);
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
};
|
|
8926
|
+
|
|
7793
8927
|
// src/core/execution/executors/postgres-executor.ts
|
|
7794
8928
|
function createPostgresExecutor(client) {
|
|
7795
8929
|
return createExecutorFromQueryRunner({
|
|
@@ -7811,7 +8945,11 @@ function createPostgresExecutor(client) {
|
|
|
7811
8945
|
|
|
7812
8946
|
// src/core/execution/executors/mysql-executor.ts
|
|
7813
8947
|
function createMysqlExecutor(client) {
|
|
8948
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
|
|
7814
8949
|
return {
|
|
8950
|
+
capabilities: {
|
|
8951
|
+
transactions: supportsTransactions
|
|
8952
|
+
},
|
|
7815
8953
|
async executeSql(sql, params) {
|
|
7816
8954
|
const [rows] = await client.query(sql, params);
|
|
7817
8955
|
if (!Array.isArray(rows)) {
|
|
@@ -7823,53 +8961,94 @@ function createMysqlExecutor(client) {
|
|
|
7823
8961
|
return [result];
|
|
7824
8962
|
},
|
|
7825
8963
|
async beginTransaction() {
|
|
7826
|
-
if (!
|
|
8964
|
+
if (!supportsTransactions) {
|
|
8965
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8966
|
+
}
|
|
7827
8967
|
await client.beginTransaction();
|
|
7828
8968
|
},
|
|
7829
8969
|
async commitTransaction() {
|
|
7830
|
-
if (!
|
|
8970
|
+
if (!supportsTransactions) {
|
|
8971
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8972
|
+
}
|
|
7831
8973
|
await client.commit();
|
|
7832
8974
|
},
|
|
7833
8975
|
async rollbackTransaction() {
|
|
7834
|
-
if (!
|
|
8976
|
+
if (!supportsTransactions) {
|
|
8977
|
+
throw new Error("Transactions are not supported by this executor");
|
|
8978
|
+
}
|
|
7835
8979
|
await client.rollback();
|
|
8980
|
+
},
|
|
8981
|
+
async dispose() {
|
|
7836
8982
|
}
|
|
7837
8983
|
};
|
|
7838
8984
|
}
|
|
7839
8985
|
|
|
7840
8986
|
// src/core/execution/executors/sqlite-executor.ts
|
|
7841
8987
|
function createSqliteExecutor(client) {
|
|
8988
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commitTransaction === "function" && typeof client.rollbackTransaction === "function";
|
|
7842
8989
|
return {
|
|
8990
|
+
capabilities: {
|
|
8991
|
+
transactions: supportsTransactions
|
|
8992
|
+
},
|
|
7843
8993
|
async executeSql(sql, params) {
|
|
7844
8994
|
const rows = await client.all(sql, params);
|
|
7845
8995
|
const result = rowsToQueryResult(rows);
|
|
7846
8996
|
return [result];
|
|
7847
8997
|
},
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
8998
|
+
async beginTransaction() {
|
|
8999
|
+
if (!supportsTransactions) {
|
|
9000
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9001
|
+
}
|
|
9002
|
+
await client.beginTransaction();
|
|
9003
|
+
},
|
|
9004
|
+
async commitTransaction() {
|
|
9005
|
+
if (!supportsTransactions) {
|
|
9006
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9007
|
+
}
|
|
9008
|
+
await client.commitTransaction();
|
|
9009
|
+
},
|
|
9010
|
+
async rollbackTransaction() {
|
|
9011
|
+
if (!supportsTransactions) {
|
|
9012
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9013
|
+
}
|
|
9014
|
+
await client.rollbackTransaction();
|
|
9015
|
+
},
|
|
9016
|
+
async dispose() {
|
|
9017
|
+
}
|
|
7851
9018
|
};
|
|
7852
9019
|
}
|
|
7853
9020
|
|
|
7854
9021
|
// src/core/execution/executors/mssql-executor.ts
|
|
7855
9022
|
function createMssqlExecutor(client) {
|
|
9023
|
+
const supportsTransactions = typeof client.beginTransaction === "function" && typeof client.commit === "function" && typeof client.rollback === "function";
|
|
7856
9024
|
return {
|
|
9025
|
+
capabilities: {
|
|
9026
|
+
transactions: supportsTransactions
|
|
9027
|
+
},
|
|
7857
9028
|
async executeSql(sql, params) {
|
|
7858
9029
|
const { recordset } = await client.query(sql, params);
|
|
7859
9030
|
const result = rowsToQueryResult(recordset ?? []);
|
|
7860
9031
|
return [result];
|
|
7861
9032
|
},
|
|
7862
9033
|
async beginTransaction() {
|
|
7863
|
-
if (!
|
|
9034
|
+
if (!supportsTransactions) {
|
|
9035
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9036
|
+
}
|
|
7864
9037
|
await client.beginTransaction();
|
|
7865
9038
|
},
|
|
7866
9039
|
async commitTransaction() {
|
|
7867
|
-
if (!
|
|
9040
|
+
if (!supportsTransactions) {
|
|
9041
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9042
|
+
}
|
|
7868
9043
|
await client.commit();
|
|
7869
9044
|
},
|
|
7870
9045
|
async rollbackTransaction() {
|
|
7871
|
-
if (!
|
|
9046
|
+
if (!supportsTransactions) {
|
|
9047
|
+
throw new Error("Transactions are not supported by this executor");
|
|
9048
|
+
}
|
|
7872
9049
|
await client.rollback();
|
|
9050
|
+
},
|
|
9051
|
+
async dispose() {
|
|
7873
9052
|
}
|
|
7874
9053
|
};
|
|
7875
9054
|
}
|
|
@@ -7938,6 +9117,86 @@ function createTediousExecutor(connection, module, options) {
|
|
|
7938
9117
|
const client = createTediousMssqlClient(connection, module, options);
|
|
7939
9118
|
return createMssqlExecutor(client);
|
|
7940
9119
|
}
|
|
9120
|
+
|
|
9121
|
+
// src/orm/pooled-executor-factory.ts
|
|
9122
|
+
function createPooledExecutorFactory(opts) {
|
|
9123
|
+
const { pool, adapter } = opts;
|
|
9124
|
+
const makeExecutor = (mode) => {
|
|
9125
|
+
let lease = null;
|
|
9126
|
+
const getLease = async () => {
|
|
9127
|
+
if (lease) return lease;
|
|
9128
|
+
lease = await pool.acquire();
|
|
9129
|
+
return lease;
|
|
9130
|
+
};
|
|
9131
|
+
const executeWithConn = async (conn, sql, params) => {
|
|
9132
|
+
const rows = await adapter.query(conn, sql, params);
|
|
9133
|
+
return [rowsToQueryResult(rows)];
|
|
9134
|
+
};
|
|
9135
|
+
return {
|
|
9136
|
+
capabilities: { transactions: true },
|
|
9137
|
+
async executeSql(sql, params) {
|
|
9138
|
+
if (mode === "sticky") {
|
|
9139
|
+
const l2 = await getLease();
|
|
9140
|
+
return executeWithConn(l2.resource, sql, params);
|
|
9141
|
+
}
|
|
9142
|
+
if (lease) {
|
|
9143
|
+
return executeWithConn(lease.resource, sql, params);
|
|
9144
|
+
}
|
|
9145
|
+
const l = await pool.acquire();
|
|
9146
|
+
try {
|
|
9147
|
+
return await executeWithConn(l.resource, sql, params);
|
|
9148
|
+
} finally {
|
|
9149
|
+
await l.release();
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9152
|
+
async beginTransaction() {
|
|
9153
|
+
const l = await getLease();
|
|
9154
|
+
await adapter.beginTransaction(l.resource);
|
|
9155
|
+
},
|
|
9156
|
+
async commitTransaction() {
|
|
9157
|
+
if (!lease) {
|
|
9158
|
+
throw new Error("commitTransaction called without an active transaction");
|
|
9159
|
+
}
|
|
9160
|
+
const l = lease;
|
|
9161
|
+
try {
|
|
9162
|
+
await adapter.commitTransaction(l.resource);
|
|
9163
|
+
} finally {
|
|
9164
|
+
lease = null;
|
|
9165
|
+
await l.release();
|
|
9166
|
+
}
|
|
9167
|
+
},
|
|
9168
|
+
async rollbackTransaction() {
|
|
9169
|
+
if (!lease) {
|
|
9170
|
+
return;
|
|
9171
|
+
}
|
|
9172
|
+
const l = lease;
|
|
9173
|
+
try {
|
|
9174
|
+
await adapter.rollbackTransaction(l.resource);
|
|
9175
|
+
} finally {
|
|
9176
|
+
lease = null;
|
|
9177
|
+
await l.release();
|
|
9178
|
+
}
|
|
9179
|
+
},
|
|
9180
|
+
async dispose() {
|
|
9181
|
+
if (!lease) return;
|
|
9182
|
+
const l = lease;
|
|
9183
|
+
lease = null;
|
|
9184
|
+
await l.release();
|
|
9185
|
+
}
|
|
9186
|
+
};
|
|
9187
|
+
};
|
|
9188
|
+
return {
|
|
9189
|
+
createExecutor() {
|
|
9190
|
+
return makeExecutor("session");
|
|
9191
|
+
},
|
|
9192
|
+
createTransactionalExecutor() {
|
|
9193
|
+
return makeExecutor("sticky");
|
|
9194
|
+
},
|
|
9195
|
+
async dispose() {
|
|
9196
|
+
await pool.destroy();
|
|
9197
|
+
}
|
|
9198
|
+
};
|
|
9199
|
+
}
|
|
7941
9200
|
export {
|
|
7942
9201
|
AsyncLocalStorage,
|
|
7943
9202
|
BelongsTo,
|
|
@@ -7956,6 +9215,7 @@ export {
|
|
|
7956
9215
|
MySqlDialect,
|
|
7957
9216
|
Orm,
|
|
7958
9217
|
OrmSession,
|
|
9218
|
+
Pool,
|
|
7959
9219
|
PostgresDialect,
|
|
7960
9220
|
PrimaryKey,
|
|
7961
9221
|
RelationKinds,
|
|
@@ -7966,7 +9226,9 @@ export {
|
|
|
7966
9226
|
UpdateQueryBuilder,
|
|
7967
9227
|
abs,
|
|
7968
9228
|
acos,
|
|
9229
|
+
add,
|
|
7969
9230
|
addDomainEvent,
|
|
9231
|
+
aliasRef,
|
|
7970
9232
|
and,
|
|
7971
9233
|
ascii,
|
|
7972
9234
|
asin,
|
|
@@ -7999,6 +9261,7 @@ export {
|
|
|
7999
9261
|
createLiteral,
|
|
8000
9262
|
createMssqlExecutor,
|
|
8001
9263
|
createMysqlExecutor,
|
|
9264
|
+
createPooledExecutorFactory,
|
|
8002
9265
|
createPostgresExecutor,
|
|
8003
9266
|
createQueryLoggingExecutor,
|
|
8004
9267
|
createSqliteExecutor,
|
|
@@ -8017,6 +9280,7 @@ export {
|
|
|
8017
9280
|
degrees,
|
|
8018
9281
|
denseRank,
|
|
8019
9282
|
diffSchema,
|
|
9283
|
+
div,
|
|
8020
9284
|
endOfMonth,
|
|
8021
9285
|
eq,
|
|
8022
9286
|
esel,
|
|
@@ -8039,6 +9303,7 @@ export {
|
|
|
8039
9303
|
hasOne,
|
|
8040
9304
|
hydrateRows,
|
|
8041
9305
|
inList,
|
|
9306
|
+
inSubquery,
|
|
8042
9307
|
instr,
|
|
8043
9308
|
introspectSchema,
|
|
8044
9309
|
isCaseExpressionNode,
|
|
@@ -8074,10 +9339,12 @@ export {
|
|
|
8074
9339
|
min,
|
|
8075
9340
|
mod,
|
|
8076
9341
|
month,
|
|
9342
|
+
mul,
|
|
8077
9343
|
neq,
|
|
8078
9344
|
notBetween,
|
|
8079
9345
|
notExists,
|
|
8080
9346
|
notInList,
|
|
9347
|
+
notInSubquery,
|
|
8081
9348
|
notLike,
|
|
8082
9349
|
now,
|
|
8083
9350
|
ntile,
|
|
@@ -8109,6 +9376,7 @@ export {
|
|
|
8109
9376
|
sin,
|
|
8110
9377
|
space,
|
|
8111
9378
|
sqrt,
|
|
9379
|
+
sub,
|
|
8112
9380
|
substr,
|
|
8113
9381
|
sum,
|
|
8114
9382
|
synchronizeSchema,
|