metal-orm 1.0.58 → 1.0.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -31
- package/dist/index.cjs +1583 -901
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +400 -129
- package/dist/index.d.ts +400 -129
- package/dist/index.js +1575 -901
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ddl/schema-generator.ts +44 -1
- package/src/decorators/bootstrap.ts +183 -146
- package/src/decorators/column-decorator.ts +8 -49
- package/src/decorators/decorator-metadata.ts +10 -46
- package/src/decorators/entity.ts +30 -40
- package/src/decorators/relations.ts +30 -56
- package/src/index.ts +7 -7
- package/src/orm/entity-hydration.ts +72 -0
- package/src/orm/entity-meta.ts +13 -11
- package/src/orm/entity-metadata.ts +240 -238
- package/src/orm/entity-relation-cache.ts +39 -0
- package/src/orm/entity-relations.ts +207 -0
- package/src/orm/entity.ts +124 -410
- package/src/orm/execute.ts +4 -4
- package/src/orm/lazy-batch/belongs-to-many.ts +134 -0
- package/src/orm/lazy-batch/belongs-to.ts +108 -0
- package/src/orm/lazy-batch/has-many.ts +69 -0
- package/src/orm/lazy-batch/has-one.ts +68 -0
- package/src/orm/lazy-batch/shared.ts +125 -0
- package/src/orm/lazy-batch.ts +4 -492
- package/src/orm/relations/many-to-many.ts +2 -1
- package/src/query-builder/relation-cte-builder.ts +63 -0
- package/src/query-builder/relation-filter-utils.ts +159 -0
- package/src/query-builder/relation-include-strategies.ts +177 -0
- package/src/query-builder/relation-join-planner.ts +80 -0
- package/src/query-builder/relation-service.ts +119 -479
- package/src/query-builder/relation-types.ts +41 -10
- package/src/query-builder/select/projection-facet.ts +23 -23
- package/src/query-builder/select/select-operations.ts +145 -0
- package/src/query-builder/select.ts +329 -221
- package/src/schema/relation.ts +22 -18
- package/src/schema/table.ts +22 -9
- package/src/schema/types.ts +14 -12
package/dist/index.cjs
CHANGED
|
@@ -134,6 +134,7 @@ __export(index_exports, {
|
|
|
134
134
|
dayOfWeek: () => dayOfWeek,
|
|
135
135
|
defineTable: () => defineTable,
|
|
136
136
|
degrees: () => degrees,
|
|
137
|
+
deleteFrom: () => deleteFrom,
|
|
137
138
|
denseRank: () => denseRank,
|
|
138
139
|
diffSchema: () => diffSchema,
|
|
139
140
|
div: () => div,
|
|
@@ -143,6 +144,8 @@ __export(index_exports, {
|
|
|
143
144
|
esel: () => esel,
|
|
144
145
|
executeHydrated: () => executeHydrated,
|
|
145
146
|
executeHydratedWithContexts: () => executeHydratedWithContexts,
|
|
147
|
+
executeSchemaSql: () => executeSchemaSql,
|
|
148
|
+
executeSchemaSqlFor: () => executeSchemaSqlFor,
|
|
146
149
|
exists: () => exists,
|
|
147
150
|
exp: () => exp,
|
|
148
151
|
extract: () => extract,
|
|
@@ -151,6 +154,7 @@ __export(index_exports, {
|
|
|
151
154
|
fromUnixTime: () => fromUnixTime,
|
|
152
155
|
generateCreateTableSql: () => generateCreateTableSql,
|
|
153
156
|
generateSchemaSql: () => generateSchemaSql,
|
|
157
|
+
generateSchemaSqlFor: () => generateSchemaSqlFor,
|
|
154
158
|
getColumn: () => getColumn,
|
|
155
159
|
getDecoratorMetadata: () => getDecoratorMetadata,
|
|
156
160
|
getSchemaIntrospector: () => getSchemaIntrospector,
|
|
@@ -167,6 +171,7 @@ __export(index_exports, {
|
|
|
167
171
|
inList: () => inList,
|
|
168
172
|
inSubquery: () => inSubquery,
|
|
169
173
|
initcap: () => initcap,
|
|
174
|
+
insertInto: () => insertInto,
|
|
170
175
|
instr: () => instr,
|
|
171
176
|
introspectSchema: () => introspectSchema,
|
|
172
177
|
isCaseExpressionNode: () => isCaseExpressionNode,
|
|
@@ -255,7 +260,9 @@ __export(index_exports, {
|
|
|
255
260
|
rtrim: () => rtrim,
|
|
256
261
|
second: () => second,
|
|
257
262
|
sel: () => sel,
|
|
263
|
+
selectFrom: () => selectFrom,
|
|
258
264
|
selectFromEntity: () => selectFromEntity,
|
|
265
|
+
setRelations: () => setRelations,
|
|
259
266
|
sha1: () => sha1,
|
|
260
267
|
sha2: () => sha2,
|
|
261
268
|
shiftLeft: () => shiftLeft,
|
|
@@ -277,6 +284,7 @@ __export(index_exports, {
|
|
|
277
284
|
trunc: () => trunc,
|
|
278
285
|
truncate: () => truncate,
|
|
279
286
|
unixTimestamp: () => unixTimestamp,
|
|
287
|
+
update: () => update,
|
|
280
288
|
upper: () => upper,
|
|
281
289
|
utcNow: () => utcNow,
|
|
282
290
|
valueToOperand: () => valueToOperand,
|
|
@@ -311,6 +319,9 @@ var defineTable = (name, columns, relations = {}, hooks, options = {}) => {
|
|
|
311
319
|
collation: options.collation
|
|
312
320
|
};
|
|
313
321
|
};
|
|
322
|
+
function setRelations(table, relations) {
|
|
323
|
+
table.relations = relations;
|
|
324
|
+
}
|
|
314
325
|
var TABLE_REF_CACHE = /* @__PURE__ */ new WeakMap();
|
|
315
326
|
var withColumnProps = (table) => {
|
|
316
327
|
const cached = TABLE_REF_CACHE.get(table);
|
|
@@ -954,6 +965,7 @@ var variance = buildAggregate("VARIANCE");
|
|
|
954
965
|
|
|
955
966
|
// src/core/ast/expression-visitor.ts
|
|
956
967
|
var DispatcherRegistry = class _DispatcherRegistry {
|
|
968
|
+
dispatchers;
|
|
957
969
|
constructor(dispatchers = /* @__PURE__ */ new Map()) {
|
|
958
970
|
this.dispatchers = dispatchers;
|
|
959
971
|
}
|
|
@@ -1087,61 +1099,9 @@ var toTableRef = (table) => ({
|
|
|
1087
1099
|
alias: hasAlias(table) ? table.alias : void 0
|
|
1088
1100
|
});
|
|
1089
1101
|
|
|
1090
|
-
// src/core/ast/builders.ts
|
|
1091
|
-
var isColumnNode = (col2) => "type" in col2 && col2.type === "Column";
|
|
1092
|
-
var resolveTableName = (def, table) => {
|
|
1093
|
-
if (!def.table) {
|
|
1094
|
-
return table.alias || table.name;
|
|
1095
|
-
}
|
|
1096
|
-
if (table.alias && def.table === table.name) {
|
|
1097
|
-
return table.alias;
|
|
1098
|
-
}
|
|
1099
|
-
return def.table;
|
|
1100
|
-
};
|
|
1101
|
-
var buildColumnNode = (table, column) => {
|
|
1102
|
-
if (isColumnNode(column)) {
|
|
1103
|
-
return column;
|
|
1104
|
-
}
|
|
1105
|
-
const def = column;
|
|
1106
|
-
const baseTable = resolveTableName(def, table);
|
|
1107
|
-
return {
|
|
1108
|
-
type: "Column",
|
|
1109
|
-
table: baseTable,
|
|
1110
|
-
name: def.name
|
|
1111
|
-
};
|
|
1112
|
-
};
|
|
1113
|
-
var buildColumnNodes = (table, names) => names.map((name) => ({
|
|
1114
|
-
type: "Column",
|
|
1115
|
-
table: table.alias || table.name,
|
|
1116
|
-
name
|
|
1117
|
-
}));
|
|
1118
|
-
var createTableNode = (table) => ({
|
|
1119
|
-
type: "Table",
|
|
1120
|
-
name: table.name,
|
|
1121
|
-
schema: table.schema
|
|
1122
|
-
});
|
|
1123
|
-
var fnTable = (name, args = [], alias, opts) => ({
|
|
1124
|
-
type: "FunctionTable",
|
|
1125
|
-
name,
|
|
1126
|
-
args,
|
|
1127
|
-
alias,
|
|
1128
|
-
lateral: opts?.lateral,
|
|
1129
|
-
withOrdinality: opts?.withOrdinality,
|
|
1130
|
-
columnAliases: opts?.columnAliases,
|
|
1131
|
-
schema: opts?.schema
|
|
1132
|
-
});
|
|
1133
|
-
var derivedTable = (query, alias, columnAliases) => ({
|
|
1134
|
-
type: "DerivedTable",
|
|
1135
|
-
query,
|
|
1136
|
-
alias,
|
|
1137
|
-
columnAliases
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
1102
|
// src/core/functions/function-registry.ts
|
|
1141
1103
|
var FunctionRegistry = class {
|
|
1142
|
-
|
|
1143
|
-
this.renderers = /* @__PURE__ */ new Map();
|
|
1144
|
-
}
|
|
1104
|
+
renderers = /* @__PURE__ */ new Map();
|
|
1145
1105
|
/**
|
|
1146
1106
|
* Registers or overrides a renderer for the given function name.
|
|
1147
1107
|
*/
|
|
@@ -1436,6 +1396,7 @@ function renderStandardGroupConcat(ctx) {
|
|
|
1436
1396
|
|
|
1437
1397
|
// src/core/functions/standard-strategy.ts
|
|
1438
1398
|
var StandardFunctionStrategy = class {
|
|
1399
|
+
registry;
|
|
1439
1400
|
/**
|
|
1440
1401
|
* Creates a new StandardFunctionStrategy and registers standard functions.
|
|
1441
1402
|
*/
|
|
@@ -1501,17 +1462,13 @@ var StandardFunctionStrategy = class {
|
|
|
1501
1462
|
getGroupConcatSeparatorOperand(ctx) {
|
|
1502
1463
|
return getGroupConcatSeparatorOperand(ctx);
|
|
1503
1464
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
this.DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
|
|
1507
|
-
}
|
|
1465
|
+
/** Default separator for GROUP_CONCAT, a comma. */
|
|
1466
|
+
static DEFAULT_GROUP_CONCAT_SEPARATOR = DEFAULT_GROUP_CONCAT_SEPARATOR;
|
|
1508
1467
|
};
|
|
1509
1468
|
|
|
1510
1469
|
// src/core/functions/standard-table-strategy.ts
|
|
1511
1470
|
var StandardTableFunctionStrategy = class {
|
|
1512
|
-
|
|
1513
|
-
this.renderers = /* @__PURE__ */ new Map();
|
|
1514
|
-
}
|
|
1471
|
+
renderers = /* @__PURE__ */ new Map();
|
|
1515
1472
|
add(key, renderer) {
|
|
1516
1473
|
this.renderers.set(key, renderer);
|
|
1517
1474
|
}
|
|
@@ -1690,6 +1647,10 @@ var Dialect = class _Dialect {
|
|
|
1690
1647
|
const combinedCtes = [...normalized.ctes ?? [], ...hoistedCtes];
|
|
1691
1648
|
return combinedCtes.length ? { ...normalized, ctes: combinedCtes } : normalized;
|
|
1692
1649
|
}
|
|
1650
|
+
expressionCompilers;
|
|
1651
|
+
operandCompilers;
|
|
1652
|
+
functionStrategy;
|
|
1653
|
+
tableFunctionStrategy;
|
|
1693
1654
|
constructor(functionStrategy, tableFunctionStrategy) {
|
|
1694
1655
|
this.expressionCompilers = /* @__PURE__ */ new Map();
|
|
1695
1656
|
this.operandCompilers = /* @__PURE__ */ new Map();
|
|
@@ -1705,10 +1666,7 @@ var Dialect = class _Dialect {
|
|
|
1705
1666
|
*/
|
|
1706
1667
|
static create(functionStrategy, tableFunctionStrategy) {
|
|
1707
1668
|
class TestDialect extends _Dialect {
|
|
1708
|
-
|
|
1709
|
-
super(...arguments);
|
|
1710
|
-
this.dialect = "sqlite";
|
|
1711
|
-
}
|
|
1669
|
+
dialect = "sqlite";
|
|
1712
1670
|
quoteIdentifier(id) {
|
|
1713
1671
|
return `"${id}"`;
|
|
1714
1672
|
}
|
|
@@ -2148,11 +2106,8 @@ var OrderByCompiler = class {
|
|
|
2148
2106
|
|
|
2149
2107
|
// src/core/dialect/base/sql-dialect.ts
|
|
2150
2108
|
var SqlDialectBase = class extends Dialect {
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
this.paginationStrategy = new StandardLimitOffsetPagination();
|
|
2154
|
-
this.returningStrategy = new NoReturningStrategy();
|
|
2155
|
-
}
|
|
2109
|
+
paginationStrategy = new StandardLimitOffsetPagination();
|
|
2110
|
+
returningStrategy = new NoReturningStrategy();
|
|
2156
2111
|
compileSelectAst(ast, ctx) {
|
|
2157
2112
|
const hasSetOps = !!(ast.setOps && ast.setOps.length);
|
|
2158
2113
|
const ctes = CteCompiler.compileCtes(
|
|
@@ -2540,12 +2495,12 @@ var PostgresTableFunctionStrategy = class extends StandardTableFunctionStrategy
|
|
|
2540
2495
|
|
|
2541
2496
|
// src/core/dialect/postgres/index.ts
|
|
2542
2497
|
var PostgresDialect = class extends SqlDialectBase {
|
|
2498
|
+
dialect = "postgres";
|
|
2543
2499
|
/**
|
|
2544
2500
|
* Creates a new PostgresDialect instance
|
|
2545
2501
|
*/
|
|
2546
2502
|
constructor() {
|
|
2547
2503
|
super(new PostgresFunctionStrategy(), new PostgresTableFunctionStrategy());
|
|
2548
|
-
this.dialect = "postgres";
|
|
2549
2504
|
this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
|
|
2550
2505
|
const left2 = this.compileOperand(node.left, ctx);
|
|
2551
2506
|
const right2 = this.compileOperand(node.right, ctx);
|
|
@@ -2679,12 +2634,12 @@ var MysqlFunctionStrategy = class extends StandardFunctionStrategy {
|
|
|
2679
2634
|
|
|
2680
2635
|
// src/core/dialect/mysql/index.ts
|
|
2681
2636
|
var MySqlDialect = class extends SqlDialectBase {
|
|
2637
|
+
dialect = "mysql";
|
|
2682
2638
|
/**
|
|
2683
2639
|
* Creates a new MySqlDialect instance
|
|
2684
2640
|
*/
|
|
2685
2641
|
constructor() {
|
|
2686
2642
|
super(new MysqlFunctionStrategy());
|
|
2687
|
-
this.dialect = "mysql";
|
|
2688
2643
|
}
|
|
2689
2644
|
/**
|
|
2690
2645
|
* Quotes an identifier using MySQL backtick syntax
|
|
@@ -2835,12 +2790,12 @@ var SqliteFunctionStrategy = class extends StandardFunctionStrategy {
|
|
|
2835
2790
|
|
|
2836
2791
|
// src/core/dialect/sqlite/index.ts
|
|
2837
2792
|
var SqliteDialect = class extends SqlDialectBase {
|
|
2793
|
+
dialect = "sqlite";
|
|
2838
2794
|
/**
|
|
2839
2795
|
* Creates a new SqliteDialect instance
|
|
2840
2796
|
*/
|
|
2841
2797
|
constructor() {
|
|
2842
2798
|
super(new SqliteFunctionStrategy());
|
|
2843
|
-
this.dialect = "sqlite";
|
|
2844
2799
|
this.registerExpressionCompiler("BitwiseExpression", (node, ctx) => {
|
|
2845
2800
|
const left2 = this.compileOperand(node.left, ctx);
|
|
2846
2801
|
const right2 = this.compileOperand(node.right, ctx);
|
|
@@ -3013,12 +2968,12 @@ var MssqlFunctionStrategy = class extends StandardFunctionStrategy {
|
|
|
3013
2968
|
|
|
3014
2969
|
// src/core/dialect/mssql/index.ts
|
|
3015
2970
|
var SqlServerDialect = class extends SqlDialectBase {
|
|
2971
|
+
dialect = "mssql";
|
|
3016
2972
|
/**
|
|
3017
2973
|
* Creates a new SqlServerDialect instance
|
|
3018
2974
|
*/
|
|
3019
2975
|
constructor() {
|
|
3020
2976
|
super(new MssqlFunctionStrategy());
|
|
3021
|
-
this.dialect = "mssql";
|
|
3022
2977
|
}
|
|
3023
2978
|
/**
|
|
3024
2979
|
* Quotes an identifier using SQL Server bracket syntax
|
|
@@ -3145,12 +3100,8 @@ var SqlServerDialect = class extends SqlDialectBase {
|
|
|
3145
3100
|
|
|
3146
3101
|
// src/core/dialect/dialect-factory.ts
|
|
3147
3102
|
var DialectFactory = class {
|
|
3148
|
-
static
|
|
3149
|
-
|
|
3150
|
-
}
|
|
3151
|
-
static {
|
|
3152
|
-
this.defaultsInitialized = false;
|
|
3153
|
-
}
|
|
3103
|
+
static registry = /* @__PURE__ */ new Map();
|
|
3104
|
+
static defaultsInitialized = false;
|
|
3154
3105
|
static ensureDefaults() {
|
|
3155
3106
|
if (this.defaultsInitialized) return;
|
|
3156
3107
|
this.defaultsInitialized = true;
|
|
@@ -3209,8 +3160,66 @@ var resolveDialectInput = (dialect) => {
|
|
|
3209
3160
|
return dialect;
|
|
3210
3161
|
};
|
|
3211
3162
|
|
|
3163
|
+
// src/core/ast/builders.ts
|
|
3164
|
+
var isColumnNode = (col2) => "type" in col2 && col2.type === "Column";
|
|
3165
|
+
var resolveTableName = (def, table) => {
|
|
3166
|
+
if (!def.table) {
|
|
3167
|
+
return table.alias || table.name;
|
|
3168
|
+
}
|
|
3169
|
+
if (table.alias && def.table === table.name) {
|
|
3170
|
+
return table.alias;
|
|
3171
|
+
}
|
|
3172
|
+
return def.table;
|
|
3173
|
+
};
|
|
3174
|
+
var buildColumnNode = (table, column) => {
|
|
3175
|
+
if (isColumnNode(column)) {
|
|
3176
|
+
return column;
|
|
3177
|
+
}
|
|
3178
|
+
const def = column;
|
|
3179
|
+
const baseTable = resolveTableName(def, table);
|
|
3180
|
+
return {
|
|
3181
|
+
type: "Column",
|
|
3182
|
+
table: baseTable,
|
|
3183
|
+
name: def.name
|
|
3184
|
+
};
|
|
3185
|
+
};
|
|
3186
|
+
var buildColumnNodes = (table, names) => names.map((name) => ({
|
|
3187
|
+
type: "Column",
|
|
3188
|
+
table: table.alias || table.name,
|
|
3189
|
+
name
|
|
3190
|
+
}));
|
|
3191
|
+
var createTableNode = (table) => ({
|
|
3192
|
+
type: "Table",
|
|
3193
|
+
name: table.name,
|
|
3194
|
+
schema: table.schema
|
|
3195
|
+
});
|
|
3196
|
+
var fnTable = (name, args = [], alias, opts) => ({
|
|
3197
|
+
type: "FunctionTable",
|
|
3198
|
+
name,
|
|
3199
|
+
args,
|
|
3200
|
+
alias,
|
|
3201
|
+
lateral: opts?.lateral,
|
|
3202
|
+
withOrdinality: opts?.withOrdinality,
|
|
3203
|
+
columnAliases: opts?.columnAliases,
|
|
3204
|
+
schema: opts?.schema
|
|
3205
|
+
});
|
|
3206
|
+
var derivedTable = (query, alias, columnAliases) => ({
|
|
3207
|
+
type: "DerivedTable",
|
|
3208
|
+
query,
|
|
3209
|
+
alias,
|
|
3210
|
+
columnAliases
|
|
3211
|
+
});
|
|
3212
|
+
|
|
3212
3213
|
// src/query-builder/select-query-state.ts
|
|
3213
3214
|
var SelectQueryState = class _SelectQueryState {
|
|
3215
|
+
/**
|
|
3216
|
+
* Table definition for the query
|
|
3217
|
+
*/
|
|
3218
|
+
table;
|
|
3219
|
+
/**
|
|
3220
|
+
* Abstract Syntax Tree (AST) representation of the query
|
|
3221
|
+
*/
|
|
3222
|
+
ast;
|
|
3214
3223
|
/**
|
|
3215
3224
|
* Creates a new SelectQueryState instance
|
|
3216
3225
|
* @param table - Table definition
|
|
@@ -4180,199 +4189,150 @@ var buildRelationCorrelation = (root, relation, rootAlias, targetTableName) => {
|
|
|
4180
4189
|
// src/core/ast/join-metadata.ts
|
|
4181
4190
|
var getJoinRelationName = (join) => join.meta?.relationName;
|
|
4182
4191
|
|
|
4183
|
-
// src/query-builder/relation-
|
|
4184
|
-
var
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
this.state = state;
|
|
4195
|
-
this.hydration = hydration;
|
|
4196
|
-
this.createQueryAstService = createQueryAstService;
|
|
4197
|
-
this.projectionHelper = new RelationProjectionHelper(
|
|
4198
|
-
table,
|
|
4199
|
-
(state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
|
|
4200
|
-
);
|
|
4201
|
-
}
|
|
4202
|
-
/**
|
|
4203
|
-
* Joins a relation to the query
|
|
4204
|
-
* @param relationName - Name of the relation to join
|
|
4205
|
-
* @param joinKind - Type of join to use
|
|
4206
|
-
* @param extraCondition - Additional join condition
|
|
4207
|
-
* @returns Relation result with updated state and hydration
|
|
4208
|
-
*/
|
|
4209
|
-
joinRelation(relationName, joinKind, extraCondition, tableSource) {
|
|
4210
|
-
const nextState = this.withJoin(this.state, relationName, joinKind, extraCondition, tableSource);
|
|
4211
|
-
return { state: nextState, hydration: this.hydration };
|
|
4192
|
+
// src/query-builder/relation-filter-utils.ts
|
|
4193
|
+
var splitFilterExpressions = (filter, allowedTables) => {
|
|
4194
|
+
const terms = flattenAnd(filter);
|
|
4195
|
+
const selfFilters = [];
|
|
4196
|
+
const crossFilters = [];
|
|
4197
|
+
for (const term of terms) {
|
|
4198
|
+
if (isExpressionSelfContained(term, allowedTables)) {
|
|
4199
|
+
selfFilters.push(term);
|
|
4200
|
+
} else {
|
|
4201
|
+
crossFilters.push(term);
|
|
4202
|
+
}
|
|
4212
4203
|
}
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
match(relationName, predicate) {
|
|
4220
|
-
const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
|
|
4221
|
-
const pk = findPrimaryKey(this.table);
|
|
4222
|
-
const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
|
|
4223
|
-
const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
|
|
4224
|
-
const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
|
|
4225
|
-
return { state: nextState, hydration: joined.hydration };
|
|
4204
|
+
return { selfFilters, crossFilters };
|
|
4205
|
+
};
|
|
4206
|
+
var flattenAnd = (node) => {
|
|
4207
|
+
if (!node) return [];
|
|
4208
|
+
if (node.type === "LogicalExpression" && node.operator === "AND") {
|
|
4209
|
+
return node.operands.flatMap((operand) => flattenAnd(operand));
|
|
4226
4210
|
}
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
const relation = this.getRelation(relationName);
|
|
4237
|
-
const aliasPrefix = options?.aliasPrefix ?? relationName;
|
|
4238
|
-
const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
|
|
4239
|
-
const { selfFilters, crossFilters } = this.splitFilterExpressions(
|
|
4240
|
-
options?.filter,
|
|
4241
|
-
/* @__PURE__ */ new Set([relation.target.name])
|
|
4242
|
-
);
|
|
4243
|
-
const canUseCte = !alreadyJoined && selfFilters.length > 0;
|
|
4244
|
-
const joinFilters = [...crossFilters];
|
|
4245
|
-
if (!canUseCte) {
|
|
4246
|
-
joinFilters.push(...selfFilters);
|
|
4247
|
-
}
|
|
4248
|
-
const joinCondition = this.combineWithAnd(joinFilters);
|
|
4249
|
-
let tableSourceOverride;
|
|
4250
|
-
if (canUseCte) {
|
|
4251
|
-
const cteInfo = this.createFilteredRelationCte(state, relationName, relation, selfFilters);
|
|
4252
|
-
state = cteInfo.state;
|
|
4253
|
-
tableSourceOverride = cteInfo.table;
|
|
4254
|
-
}
|
|
4255
|
-
if (!alreadyJoined) {
|
|
4256
|
-
state = this.withJoin(
|
|
4257
|
-
state,
|
|
4258
|
-
relationName,
|
|
4259
|
-
options?.joinKind ?? JOIN_KINDS.LEFT,
|
|
4260
|
-
joinCondition,
|
|
4261
|
-
tableSourceOverride
|
|
4262
|
-
);
|
|
4211
|
+
return [node];
|
|
4212
|
+
};
|
|
4213
|
+
var isExpressionSelfContained = (expr, allowedTables) => {
|
|
4214
|
+
const collector = collectReferencedTables(expr);
|
|
4215
|
+
if (collector.hasSubquery) return false;
|
|
4216
|
+
if (collector.tables.size === 0) return true;
|
|
4217
|
+
for (const table of collector.tables) {
|
|
4218
|
+
if (!allowedTables.has(table)) {
|
|
4219
|
+
return false;
|
|
4263
4220
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4221
|
+
}
|
|
4222
|
+
return true;
|
|
4223
|
+
};
|
|
4224
|
+
var collectReferencedTables = (expr) => {
|
|
4225
|
+
const collector = {
|
|
4226
|
+
tables: /* @__PURE__ */ new Set(),
|
|
4227
|
+
hasSubquery: false
|
|
4228
|
+
};
|
|
4229
|
+
collectFromExpression(expr, collector);
|
|
4230
|
+
return collector;
|
|
4231
|
+
};
|
|
4232
|
+
var collectFromExpression = (expr, collector) => {
|
|
4233
|
+
switch (expr.type) {
|
|
4234
|
+
case "BinaryExpression":
|
|
4235
|
+
collectFromOperand(expr.left, collector);
|
|
4236
|
+
collectFromOperand(expr.right, collector);
|
|
4237
|
+
break;
|
|
4238
|
+
case "LogicalExpression":
|
|
4239
|
+
expr.operands.forEach((operand) => collectFromExpression(operand, collector));
|
|
4240
|
+
break;
|
|
4241
|
+
case "NullExpression":
|
|
4242
|
+
collectFromOperand(expr.left, collector);
|
|
4243
|
+
break;
|
|
4244
|
+
case "InExpression":
|
|
4245
|
+
collectFromOperand(expr.left, collector);
|
|
4246
|
+
if (Array.isArray(expr.right)) {
|
|
4247
|
+
expr.right.forEach((value) => collectFromOperand(value, collector));
|
|
4248
|
+
} else {
|
|
4249
|
+
collector.hasSubquery = true;
|
|
4283
4250
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
};
|
|
4301
|
-
const targetSelection = buildTypedSelection(
|
|
4302
|
-
relation.target.columns,
|
|
4303
|
-
aliasPrefix,
|
|
4304
|
-
targetColumns,
|
|
4305
|
-
(key) => `Column '${key}' not found on relation '${relationName}'`
|
|
4306
|
-
);
|
|
4307
|
-
if (relation.type !== RelationKinds.BelongsToMany) {
|
|
4308
|
-
const relationSelectionResult2 = this.selectColumns(state, hydration, targetSelection);
|
|
4309
|
-
state = relationSelectionResult2.state;
|
|
4310
|
-
hydration = relationSelectionResult2.hydration;
|
|
4311
|
-
hydration = hydration.onRelationIncluded(
|
|
4312
|
-
state,
|
|
4313
|
-
relation,
|
|
4314
|
-
relationName,
|
|
4315
|
-
aliasPrefix,
|
|
4316
|
-
targetColumns
|
|
4317
|
-
);
|
|
4318
|
-
return { state, hydration };
|
|
4319
|
-
}
|
|
4320
|
-
const many = relation;
|
|
4321
|
-
const pivotAliasPrefix = options?.pivot?.aliasPrefix ?? `${aliasPrefix}_pivot`;
|
|
4322
|
-
const pivotPk = many.pivotPrimaryKey || findPrimaryKey(many.pivotTable);
|
|
4323
|
-
const pivotColumns = options?.pivot?.columns ?? many.defaultPivotColumns ?? buildDefaultPivotColumns(many, pivotPk);
|
|
4324
|
-
const pivotSelection = buildTypedSelection(
|
|
4325
|
-
many.pivotTable.columns,
|
|
4326
|
-
pivotAliasPrefix,
|
|
4327
|
-
pivotColumns,
|
|
4328
|
-
(key) => `Column '${key}' not found on pivot table '${many.pivotTable.name}'`
|
|
4329
|
-
);
|
|
4330
|
-
const combinedSelection = {
|
|
4331
|
-
...targetSelection,
|
|
4332
|
-
...pivotSelection
|
|
4333
|
-
};
|
|
4334
|
-
const relationSelectionResult = this.selectColumns(state, hydration, combinedSelection);
|
|
4335
|
-
state = relationSelectionResult.state;
|
|
4336
|
-
hydration = relationSelectionResult.hydration;
|
|
4337
|
-
hydration = hydration.onRelationIncluded(
|
|
4338
|
-
state,
|
|
4339
|
-
relation,
|
|
4340
|
-
relationName,
|
|
4341
|
-
aliasPrefix,
|
|
4342
|
-
targetColumns,
|
|
4343
|
-
{ aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
|
|
4344
|
-
);
|
|
4345
|
-
return { state, hydration };
|
|
4251
|
+
break;
|
|
4252
|
+
case "ExistsExpression":
|
|
4253
|
+
collector.hasSubquery = true;
|
|
4254
|
+
break;
|
|
4255
|
+
case "BetweenExpression":
|
|
4256
|
+
collectFromOperand(expr.left, collector);
|
|
4257
|
+
collectFromOperand(expr.lower, collector);
|
|
4258
|
+
collectFromOperand(expr.upper, collector);
|
|
4259
|
+
break;
|
|
4260
|
+
case "ArithmeticExpression":
|
|
4261
|
+
case "BitwiseExpression":
|
|
4262
|
+
collectFromOperand(expr.left, collector);
|
|
4263
|
+
collectFromOperand(expr.right, collector);
|
|
4264
|
+
break;
|
|
4265
|
+
default:
|
|
4266
|
+
break;
|
|
4346
4267
|
}
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4268
|
+
};
|
|
4269
|
+
var collectFromOperand = (node, collector) => {
|
|
4270
|
+
switch (node.type) {
|
|
4271
|
+
case "Column":
|
|
4272
|
+
collector.tables.add(node.table);
|
|
4273
|
+
break;
|
|
4274
|
+
case "Function":
|
|
4275
|
+
node.args.forEach((arg) => collectFromOperand(arg, collector));
|
|
4276
|
+
if (node.separator) {
|
|
4277
|
+
collectFromOperand(node.separator, collector);
|
|
4278
|
+
}
|
|
4279
|
+
if (node.orderBy) {
|
|
4280
|
+
node.orderBy.forEach((order) => collectFromOrderingTerm(order.term, collector));
|
|
4281
|
+
}
|
|
4282
|
+
break;
|
|
4283
|
+
case "JsonPath":
|
|
4284
|
+
collectFromOperand(node.column, collector);
|
|
4285
|
+
break;
|
|
4286
|
+
case "ScalarSubquery":
|
|
4287
|
+
collector.hasSubquery = true;
|
|
4288
|
+
break;
|
|
4289
|
+
case "CaseExpression":
|
|
4290
|
+
node.conditions.forEach(({ when, then }) => {
|
|
4291
|
+
collectFromExpression(when, collector);
|
|
4292
|
+
collectFromOperand(then, collector);
|
|
4293
|
+
});
|
|
4294
|
+
if (node.else) {
|
|
4295
|
+
collectFromOperand(node.else, collector);
|
|
4296
|
+
}
|
|
4297
|
+
break;
|
|
4298
|
+
case "Cast":
|
|
4299
|
+
collectFromOperand(node.expression, collector);
|
|
4300
|
+
break;
|
|
4301
|
+
case "WindowFunction":
|
|
4302
|
+
node.args.forEach((arg) => collectFromOperand(arg, collector));
|
|
4303
|
+
node.partitionBy?.forEach((part) => collectFromOperand(part, collector));
|
|
4304
|
+
node.orderBy?.forEach((order) => collectFromOrderingTerm(order.term, collector));
|
|
4305
|
+
break;
|
|
4306
|
+
case "Collate":
|
|
4307
|
+
collectFromOperand(node.expression, collector);
|
|
4308
|
+
break;
|
|
4309
|
+
case "ArithmeticExpression":
|
|
4310
|
+
case "BitwiseExpression":
|
|
4311
|
+
collectFromOperand(node.left, collector);
|
|
4312
|
+
collectFromOperand(node.right, collector);
|
|
4313
|
+
break;
|
|
4314
|
+
case "Literal":
|
|
4315
|
+
case "AliasRef":
|
|
4316
|
+
break;
|
|
4317
|
+
default:
|
|
4318
|
+
break;
|
|
4365
4319
|
}
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4320
|
+
};
|
|
4321
|
+
var collectFromOrderingTerm = (term, collector) => {
|
|
4322
|
+
if (isOperandNode(term)) {
|
|
4323
|
+
collectFromOperand(term, collector);
|
|
4324
|
+
return;
|
|
4325
|
+
}
|
|
4326
|
+
collectFromExpression(term, collector);
|
|
4327
|
+
};
|
|
4328
|
+
|
|
4329
|
+
// src/query-builder/relation-join-planner.ts
|
|
4330
|
+
var RelationJoinPlanner = class {
|
|
4331
|
+
constructor(table, createQueryAstService) {
|
|
4332
|
+
this.table = table;
|
|
4333
|
+
this.createQueryAstService = createQueryAstService;
|
|
4334
|
+
}
|
|
4335
|
+
withJoin(state, relationName, relation, joinKind, extraCondition, tableSource) {
|
|
4376
4336
|
const rootAlias = state.ast.from.type === "Table" ? state.ast.from.alias : void 0;
|
|
4377
4337
|
if (relation.type === RelationKinds.BelongsToMany) {
|
|
4378
4338
|
const targetTableSource = tableSource ?? {
|
|
@@ -4409,167 +4369,31 @@ var RelationService = class {
|
|
|
4409
4369
|
const joinNode = createJoinNode(joinKind, targetTable, condition, relationName);
|
|
4410
4370
|
return this.astService(state).withJoin(joinNode);
|
|
4411
4371
|
}
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
* @param state - Current query state
|
|
4415
|
-
* @param hydration - Hydration manager
|
|
4416
|
-
* @param columns - Columns to select
|
|
4417
|
-
* @returns Relation result with updated state and hydration
|
|
4418
|
-
*/
|
|
4419
|
-
selectColumns(state, hydration, columns) {
|
|
4420
|
-
const { state: nextState, addedColumns } = this.astService(state).select(columns);
|
|
4421
|
-
return {
|
|
4422
|
-
state: nextState,
|
|
4423
|
-
hydration: hydration.onColumnsSelected(nextState, addedColumns)
|
|
4424
|
-
};
|
|
4425
|
-
}
|
|
4426
|
-
combineWithAnd(expressions) {
|
|
4427
|
-
if (expressions.length === 0) return void 0;
|
|
4428
|
-
if (expressions.length === 1) return expressions[0];
|
|
4429
|
-
return {
|
|
4430
|
-
type: "LogicalExpression",
|
|
4431
|
-
operator: "AND",
|
|
4432
|
-
operands: expressions
|
|
4433
|
-
};
|
|
4434
|
-
}
|
|
4435
|
-
splitFilterExpressions(filter, allowedTables) {
|
|
4436
|
-
const terms = this.flattenAnd(filter);
|
|
4437
|
-
const selfFilters = [];
|
|
4438
|
-
const crossFilters = [];
|
|
4439
|
-
for (const term of terms) {
|
|
4440
|
-
if (this.isExpressionSelfContained(term, allowedTables)) {
|
|
4441
|
-
selfFilters.push(term);
|
|
4442
|
-
} else {
|
|
4443
|
-
crossFilters.push(term);
|
|
4444
|
-
}
|
|
4445
|
-
}
|
|
4446
|
-
return { selfFilters, crossFilters };
|
|
4447
|
-
}
|
|
4448
|
-
flattenAnd(node) {
|
|
4449
|
-
if (!node) return [];
|
|
4450
|
-
if (node.type === "LogicalExpression" && node.operator === "AND") {
|
|
4451
|
-
return node.operands.flatMap((operand) => this.flattenAnd(operand));
|
|
4452
|
-
}
|
|
4453
|
-
return [node];
|
|
4372
|
+
astService(state) {
|
|
4373
|
+
return this.createQueryAstService(this.table, state);
|
|
4454
4374
|
}
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
if (collector.tables.size === 0) return true;
|
|
4459
|
-
for (const table of collector.tables) {
|
|
4460
|
-
if (!allowedTables.has(table)) {
|
|
4461
|
-
return false;
|
|
4462
|
-
}
|
|
4375
|
+
resolveTargetTableName(target, relation) {
|
|
4376
|
+
if (target.type === "Table") {
|
|
4377
|
+
return target.alias ?? target.name;
|
|
4463
4378
|
}
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
collectReferencedTables(expr) {
|
|
4467
|
-
const collector = {
|
|
4468
|
-
tables: /* @__PURE__ */ new Set(),
|
|
4469
|
-
hasSubquery: false
|
|
4470
|
-
};
|
|
4471
|
-
this.collectFromExpression(expr, collector);
|
|
4472
|
-
return collector;
|
|
4473
|
-
}
|
|
4474
|
-
collectFromExpression(expr, collector) {
|
|
4475
|
-
switch (expr.type) {
|
|
4476
|
-
case "BinaryExpression":
|
|
4477
|
-
this.collectFromOperand(expr.left, collector);
|
|
4478
|
-
this.collectFromOperand(expr.right, collector);
|
|
4479
|
-
break;
|
|
4480
|
-
case "LogicalExpression":
|
|
4481
|
-
expr.operands.forEach((operand) => this.collectFromExpression(operand, collector));
|
|
4482
|
-
break;
|
|
4483
|
-
case "NullExpression":
|
|
4484
|
-
this.collectFromOperand(expr.left, collector);
|
|
4485
|
-
break;
|
|
4486
|
-
case "InExpression":
|
|
4487
|
-
this.collectFromOperand(expr.left, collector);
|
|
4488
|
-
if (Array.isArray(expr.right)) {
|
|
4489
|
-
expr.right.forEach((value) => this.collectFromOperand(value, collector));
|
|
4490
|
-
} else {
|
|
4491
|
-
collector.hasSubquery = true;
|
|
4492
|
-
}
|
|
4493
|
-
break;
|
|
4494
|
-
case "ExistsExpression":
|
|
4495
|
-
collector.hasSubquery = true;
|
|
4496
|
-
break;
|
|
4497
|
-
case "BetweenExpression":
|
|
4498
|
-
this.collectFromOperand(expr.left, collector);
|
|
4499
|
-
this.collectFromOperand(expr.lower, collector);
|
|
4500
|
-
this.collectFromOperand(expr.upper, collector);
|
|
4501
|
-
break;
|
|
4502
|
-
case "ArithmeticExpression":
|
|
4503
|
-
case "BitwiseExpression":
|
|
4504
|
-
this.collectFromOperand(expr.left, collector);
|
|
4505
|
-
this.collectFromOperand(expr.right, collector);
|
|
4506
|
-
break;
|
|
4507
|
-
default:
|
|
4508
|
-
break;
|
|
4379
|
+
if (target.type === "DerivedTable") {
|
|
4380
|
+
return target.alias;
|
|
4509
4381
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
switch (node.type) {
|
|
4513
|
-
case "Column":
|
|
4514
|
-
collector.tables.add(node.table);
|
|
4515
|
-
break;
|
|
4516
|
-
case "Function":
|
|
4517
|
-
node.args.forEach((arg) => this.collectFromOperand(arg, collector));
|
|
4518
|
-
if (node.separator) {
|
|
4519
|
-
this.collectFromOperand(node.separator, collector);
|
|
4520
|
-
}
|
|
4521
|
-
if (node.orderBy) {
|
|
4522
|
-
node.orderBy.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
|
|
4523
|
-
}
|
|
4524
|
-
break;
|
|
4525
|
-
case "JsonPath":
|
|
4526
|
-
this.collectFromOperand(node.column, collector);
|
|
4527
|
-
break;
|
|
4528
|
-
case "ScalarSubquery":
|
|
4529
|
-
collector.hasSubquery = true;
|
|
4530
|
-
break;
|
|
4531
|
-
case "CaseExpression":
|
|
4532
|
-
node.conditions.forEach(({ when, then }) => {
|
|
4533
|
-
this.collectFromExpression(when, collector);
|
|
4534
|
-
this.collectFromOperand(then, collector);
|
|
4535
|
-
});
|
|
4536
|
-
if (node.else) {
|
|
4537
|
-
this.collectFromOperand(node.else, collector);
|
|
4538
|
-
}
|
|
4539
|
-
break;
|
|
4540
|
-
case "Cast":
|
|
4541
|
-
this.collectFromOperand(node.expression, collector);
|
|
4542
|
-
break;
|
|
4543
|
-
case "WindowFunction":
|
|
4544
|
-
node.args.forEach((arg) => this.collectFromOperand(arg, collector));
|
|
4545
|
-
node.partitionBy?.forEach((part) => this.collectFromOperand(part, collector));
|
|
4546
|
-
node.orderBy?.forEach((order) => this.collectFromOrderingTerm(order.term, collector));
|
|
4547
|
-
break;
|
|
4548
|
-
case "Collate":
|
|
4549
|
-
this.collectFromOperand(node.expression, collector);
|
|
4550
|
-
break;
|
|
4551
|
-
case "ArithmeticExpression":
|
|
4552
|
-
case "BitwiseExpression":
|
|
4553
|
-
this.collectFromOperand(node.left, collector);
|
|
4554
|
-
this.collectFromOperand(node.right, collector);
|
|
4555
|
-
break;
|
|
4556
|
-
case "Literal":
|
|
4557
|
-
case "AliasRef":
|
|
4558
|
-
break;
|
|
4559
|
-
default:
|
|
4560
|
-
break;
|
|
4382
|
+
if (target.type === "FunctionTable") {
|
|
4383
|
+
return target.alias ?? relation.target.name;
|
|
4561
4384
|
}
|
|
4385
|
+
return relation.target.name;
|
|
4562
4386
|
}
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
this.
|
|
4387
|
+
};
|
|
4388
|
+
|
|
4389
|
+
// src/query-builder/relation-cte-builder.ts
|
|
4390
|
+
var RelationCteBuilder = class {
|
|
4391
|
+
constructor(table, createQueryAstService) {
|
|
4392
|
+
this.table = table;
|
|
4393
|
+
this.createQueryAstService = createQueryAstService;
|
|
4569
4394
|
}
|
|
4570
|
-
createFilteredRelationCte(state, relationName, relation,
|
|
4395
|
+
createFilteredRelationCte(state, relationName, relation, predicate) {
|
|
4571
4396
|
const cteName = this.generateUniqueCteName(state, relationName);
|
|
4572
|
-
const predicate = this.combineWithAnd(filters);
|
|
4573
4397
|
if (!predicate) {
|
|
4574
4398
|
throw new Error("Unable to build filter CTE without predicates.");
|
|
4575
4399
|
}
|
|
@@ -4603,17 +4427,274 @@ var RelationService = class {
|
|
|
4603
4427
|
}
|
|
4604
4428
|
return candidate;
|
|
4605
4429
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4430
|
+
astService(state) {
|
|
4431
|
+
return this.createQueryAstService(this.table, state);
|
|
4432
|
+
}
|
|
4433
|
+
};
|
|
4434
|
+
|
|
4435
|
+
// src/query-builder/relation-include-strategies.ts
|
|
4436
|
+
var buildTypedSelection = (columns, prefix, keys, missingMsg) => {
|
|
4437
|
+
return keys.reduce((acc, key) => {
|
|
4438
|
+
const def = columns[key];
|
|
4439
|
+
if (!def) {
|
|
4440
|
+
throw new Error(missingMsg(key));
|
|
4609
4441
|
}
|
|
4610
|
-
|
|
4611
|
-
|
|
4442
|
+
acc[makeRelationAlias(prefix, key)] = def;
|
|
4443
|
+
return acc;
|
|
4444
|
+
}, {});
|
|
4445
|
+
};
|
|
4446
|
+
var resolveTargetColumns = (relation, options) => {
|
|
4447
|
+
const requestedColumns = options?.columns?.length ? [...options.columns] : Object.keys(relation.target.columns);
|
|
4448
|
+
const targetPrimaryKey = findPrimaryKey(relation.target);
|
|
4449
|
+
if (!requestedColumns.includes(targetPrimaryKey)) {
|
|
4450
|
+
requestedColumns.push(targetPrimaryKey);
|
|
4451
|
+
}
|
|
4452
|
+
return requestedColumns;
|
|
4453
|
+
};
|
|
4454
|
+
var ensureRootForeignKeySelected = (context, relation) => {
|
|
4455
|
+
const fkColumn = context.rootTable.columns[relation.foreignKey];
|
|
4456
|
+
if (!fkColumn) {
|
|
4457
|
+
return { state: context.state, hydration: context.hydration };
|
|
4458
|
+
}
|
|
4459
|
+
const hasForeignKeySelected = context.state.ast.columns.some((col2) => {
|
|
4460
|
+
if (col2.type !== "Column") return false;
|
|
4461
|
+
const node = col2;
|
|
4462
|
+
const alias = node.alias ?? node.name;
|
|
4463
|
+
return alias === relation.foreignKey;
|
|
4464
|
+
});
|
|
4465
|
+
if (hasForeignKeySelected) {
|
|
4466
|
+
return { state: context.state, hydration: context.hydration };
|
|
4467
|
+
}
|
|
4468
|
+
return context.selectColumns(context.state, context.hydration, {
|
|
4469
|
+
[relation.foreignKey]: fkColumn
|
|
4470
|
+
});
|
|
4471
|
+
};
|
|
4472
|
+
var standardIncludeStrategy = (context) => {
|
|
4473
|
+
const relation = context.relation;
|
|
4474
|
+
let { state, hydration } = context;
|
|
4475
|
+
const fkSelectionResult = ensureRootForeignKeySelected(context, relation);
|
|
4476
|
+
state = fkSelectionResult.state;
|
|
4477
|
+
hydration = fkSelectionResult.hydration;
|
|
4478
|
+
const targetColumns = resolveTargetColumns(relation, context.options);
|
|
4479
|
+
const targetSelection = buildTypedSelection(
|
|
4480
|
+
relation.target.columns,
|
|
4481
|
+
context.aliasPrefix,
|
|
4482
|
+
targetColumns,
|
|
4483
|
+
(key) => `Column '${key}' not found on relation '${context.relationName}'`
|
|
4484
|
+
);
|
|
4485
|
+
const relationSelectionResult = context.selectColumns(state, hydration, targetSelection);
|
|
4486
|
+
state = relationSelectionResult.state;
|
|
4487
|
+
hydration = relationSelectionResult.hydration;
|
|
4488
|
+
hydration = hydration.onRelationIncluded(
|
|
4489
|
+
state,
|
|
4490
|
+
relation,
|
|
4491
|
+
context.relationName,
|
|
4492
|
+
context.aliasPrefix,
|
|
4493
|
+
targetColumns
|
|
4494
|
+
);
|
|
4495
|
+
return { state, hydration };
|
|
4496
|
+
};
|
|
4497
|
+
var belongsToManyStrategy = (context) => {
|
|
4498
|
+
const relation = context.relation;
|
|
4499
|
+
let { state, hydration } = context;
|
|
4500
|
+
const targetColumns = resolveTargetColumns(relation, context.options);
|
|
4501
|
+
const targetSelection = buildTypedSelection(
|
|
4502
|
+
relation.target.columns,
|
|
4503
|
+
context.aliasPrefix,
|
|
4504
|
+
targetColumns,
|
|
4505
|
+
(key) => `Column '${key}' not found on relation '${context.relationName}'`
|
|
4506
|
+
);
|
|
4507
|
+
const pivotAliasPrefix = context.options?.pivot?.aliasPrefix ?? `${context.aliasPrefix}_pivot`;
|
|
4508
|
+
const pivotPk = relation.pivotPrimaryKey || findPrimaryKey(relation.pivotTable);
|
|
4509
|
+
const defaultPivotColumns = relation.defaultPivotColumns ?? buildDefaultPivotColumns(relation, pivotPk);
|
|
4510
|
+
const pivotColumns = context.options?.pivot?.columns ? [...context.options.pivot.columns] : [...defaultPivotColumns];
|
|
4511
|
+
const pivotSelection = buildTypedSelection(
|
|
4512
|
+
relation.pivotTable.columns,
|
|
4513
|
+
pivotAliasPrefix,
|
|
4514
|
+
pivotColumns,
|
|
4515
|
+
(key) => `Column '${key}' not found on pivot table '${relation.pivotTable.name}'`
|
|
4516
|
+
);
|
|
4517
|
+
const combinedSelection = {
|
|
4518
|
+
...targetSelection,
|
|
4519
|
+
...pivotSelection
|
|
4520
|
+
};
|
|
4521
|
+
const relationSelectionResult = context.selectColumns(state, hydration, combinedSelection);
|
|
4522
|
+
state = relationSelectionResult.state;
|
|
4523
|
+
hydration = relationSelectionResult.hydration;
|
|
4524
|
+
hydration = hydration.onRelationIncluded(
|
|
4525
|
+
state,
|
|
4526
|
+
relation,
|
|
4527
|
+
context.relationName,
|
|
4528
|
+
context.aliasPrefix,
|
|
4529
|
+
targetColumns,
|
|
4530
|
+
{ aliasPrefix: pivotAliasPrefix, columns: pivotColumns }
|
|
4531
|
+
);
|
|
4532
|
+
return { state, hydration };
|
|
4533
|
+
};
|
|
4534
|
+
var relationIncludeStrategies = {
|
|
4535
|
+
[RelationKinds.HasMany]: standardIncludeStrategy,
|
|
4536
|
+
[RelationKinds.HasOne]: standardIncludeStrategy,
|
|
4537
|
+
[RelationKinds.BelongsTo]: standardIncludeStrategy,
|
|
4538
|
+
[RelationKinds.BelongsToMany]: belongsToManyStrategy
|
|
4539
|
+
};
|
|
4540
|
+
|
|
4541
|
+
// src/query-builder/relation-service.ts
|
|
4542
|
+
var RelationService = class {
|
|
4543
|
+
/**
|
|
4544
|
+
* Creates a new RelationService instance
|
|
4545
|
+
* @param table - Table definition
|
|
4546
|
+
* @param state - Current query state
|
|
4547
|
+
* @param hydration - Hydration manager
|
|
4548
|
+
*/
|
|
4549
|
+
constructor(table, state, hydration, createQueryAstService) {
|
|
4550
|
+
this.table = table;
|
|
4551
|
+
this.state = state;
|
|
4552
|
+
this.hydration = hydration;
|
|
4553
|
+
this.createQueryAstService = createQueryAstService;
|
|
4554
|
+
this.projectionHelper = new RelationProjectionHelper(
|
|
4555
|
+
table,
|
|
4556
|
+
(state2, hydration2, columns) => this.selectColumns(state2, hydration2, columns)
|
|
4557
|
+
);
|
|
4558
|
+
this.joinPlanner = new RelationJoinPlanner(table, createQueryAstService);
|
|
4559
|
+
this.cteBuilder = new RelationCteBuilder(table, createQueryAstService);
|
|
4560
|
+
}
|
|
4561
|
+
projectionHelper;
|
|
4562
|
+
joinPlanner;
|
|
4563
|
+
cteBuilder;
|
|
4564
|
+
/**
|
|
4565
|
+
* Joins a relation to the query
|
|
4566
|
+
* @param relationName - Name of the relation to join
|
|
4567
|
+
* @param joinKind - Type of join to use
|
|
4568
|
+
* @param extraCondition - Additional join condition
|
|
4569
|
+
* @returns Relation result with updated state and hydration
|
|
4570
|
+
*/
|
|
4571
|
+
joinRelation(relationName, joinKind, extraCondition, tableSource) {
|
|
4572
|
+
const relation = this.getRelation(relationName);
|
|
4573
|
+
const nextState = this.joinPlanner.withJoin(
|
|
4574
|
+
this.state,
|
|
4575
|
+
relationName,
|
|
4576
|
+
relation,
|
|
4577
|
+
joinKind,
|
|
4578
|
+
extraCondition,
|
|
4579
|
+
tableSource
|
|
4580
|
+
);
|
|
4581
|
+
return { state: nextState, hydration: this.hydration };
|
|
4582
|
+
}
|
|
4583
|
+
/**
|
|
4584
|
+
* Matches records based on a relation with an optional predicate
|
|
4585
|
+
* @param relationName - Name of the relation to match
|
|
4586
|
+
* @param predicate - Optional predicate expression
|
|
4587
|
+
* @returns Relation result with updated state and hydration
|
|
4588
|
+
*/
|
|
4589
|
+
match(relationName, predicate) {
|
|
4590
|
+
const joined = this.joinRelation(relationName, JOIN_KINDS.INNER, predicate);
|
|
4591
|
+
const pk = findPrimaryKey(this.table);
|
|
4592
|
+
const distinctCols = [{ type: "Column", table: this.rootTableName(), name: pk }];
|
|
4593
|
+
const existingDistinct = joined.state.ast.distinct ? joined.state.ast.distinct : [];
|
|
4594
|
+
const nextState = this.astService(joined.state).withDistinct([...existingDistinct, ...distinctCols]);
|
|
4595
|
+
return { state: nextState, hydration: joined.hydration };
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Includes a relation in the query result
|
|
4599
|
+
* @param relationName - Name of the relation to include
|
|
4600
|
+
* @param options - Options for relation inclusion
|
|
4601
|
+
* @returns Relation result with updated state and hydration
|
|
4602
|
+
*/
|
|
4603
|
+
include(relationName, options) {
|
|
4604
|
+
let state = this.state;
|
|
4605
|
+
let hydration = this.hydration;
|
|
4606
|
+
const relation = this.getRelation(relationName);
|
|
4607
|
+
const aliasPrefix = options?.aliasPrefix ?? relationName;
|
|
4608
|
+
const alreadyJoined = state.ast.joins.some((j) => getJoinRelationName(j) === relationName);
|
|
4609
|
+
const { selfFilters, crossFilters } = splitFilterExpressions(
|
|
4610
|
+
options?.filter,
|
|
4611
|
+
/* @__PURE__ */ new Set([relation.target.name])
|
|
4612
|
+
);
|
|
4613
|
+
const canUseCte = !alreadyJoined && selfFilters.length > 0;
|
|
4614
|
+
const joinFilters = [...crossFilters];
|
|
4615
|
+
if (!canUseCte) {
|
|
4616
|
+
joinFilters.push(...selfFilters);
|
|
4612
4617
|
}
|
|
4613
|
-
|
|
4614
|
-
|
|
4618
|
+
const joinCondition = this.combineWithAnd(joinFilters);
|
|
4619
|
+
let tableSourceOverride;
|
|
4620
|
+
if (canUseCte) {
|
|
4621
|
+
const predicate = this.combineWithAnd(selfFilters);
|
|
4622
|
+
const cteInfo = this.cteBuilder.createFilteredRelationCte(
|
|
4623
|
+
state,
|
|
4624
|
+
relationName,
|
|
4625
|
+
relation,
|
|
4626
|
+
predicate
|
|
4627
|
+
);
|
|
4628
|
+
state = cteInfo.state;
|
|
4629
|
+
tableSourceOverride = cteInfo.table;
|
|
4615
4630
|
}
|
|
4616
|
-
|
|
4631
|
+
if (!alreadyJoined) {
|
|
4632
|
+
state = this.joinPlanner.withJoin(
|
|
4633
|
+
state,
|
|
4634
|
+
relationName,
|
|
4635
|
+
relation,
|
|
4636
|
+
options?.joinKind ?? JOIN_KINDS.LEFT,
|
|
4637
|
+
joinCondition,
|
|
4638
|
+
tableSourceOverride
|
|
4639
|
+
);
|
|
4640
|
+
}
|
|
4641
|
+
const projectionResult = this.projectionHelper.ensureBaseProjection(state, hydration);
|
|
4642
|
+
state = projectionResult.state;
|
|
4643
|
+
hydration = projectionResult.hydration;
|
|
4644
|
+
const strategy = relationIncludeStrategies[relation.type];
|
|
4645
|
+
const result = strategy({
|
|
4646
|
+
rootTable: this.table,
|
|
4647
|
+
state,
|
|
4648
|
+
hydration,
|
|
4649
|
+
relation,
|
|
4650
|
+
relationName,
|
|
4651
|
+
aliasPrefix,
|
|
4652
|
+
options,
|
|
4653
|
+
selectColumns: (nextState, nextHydration, columns) => this.selectColumns(nextState, nextHydration, columns)
|
|
4654
|
+
});
|
|
4655
|
+
return { state: result.state, hydration: result.hydration };
|
|
4656
|
+
}
|
|
4657
|
+
/**
|
|
4658
|
+
* Applies relation correlation to a query AST
|
|
4659
|
+
* @param relationName - Name of the relation
|
|
4660
|
+
* @param ast - Query AST to modify
|
|
4661
|
+
* @returns Modified query AST with relation correlation
|
|
4662
|
+
*/
|
|
4663
|
+
applyRelationCorrelation(relationName, ast, additionalCorrelation) {
|
|
4664
|
+
const relation = this.getRelation(relationName);
|
|
4665
|
+
const rootAlias = this.state.ast.from.type === "Table" ? this.state.ast.from.alias : void 0;
|
|
4666
|
+
let correlation = buildRelationCorrelation(this.table, relation, rootAlias);
|
|
4667
|
+
if (additionalCorrelation) {
|
|
4668
|
+
correlation = and(correlation, additionalCorrelation);
|
|
4669
|
+
}
|
|
4670
|
+
const whereInSubquery = ast.where ? and(correlation, ast.where) : correlation;
|
|
4671
|
+
return {
|
|
4672
|
+
...ast,
|
|
4673
|
+
where: whereInSubquery
|
|
4674
|
+
};
|
|
4675
|
+
}
|
|
4676
|
+
/**
|
|
4677
|
+
* Selects columns for a relation
|
|
4678
|
+
* @param state - Current query state
|
|
4679
|
+
* @param hydration - Hydration manager
|
|
4680
|
+
* @param columns - Columns to select
|
|
4681
|
+
* @returns Relation result with updated state and hydration
|
|
4682
|
+
*/
|
|
4683
|
+
selectColumns(state, hydration, columns) {
|
|
4684
|
+
const { state: nextState, addedColumns } = this.astService(state).select(columns);
|
|
4685
|
+
return {
|
|
4686
|
+
state: nextState,
|
|
4687
|
+
hydration: hydration.onColumnsSelected(nextState, addedColumns)
|
|
4688
|
+
};
|
|
4689
|
+
}
|
|
4690
|
+
combineWithAnd(expressions) {
|
|
4691
|
+
if (expressions.length === 0) return void 0;
|
|
4692
|
+
if (expressions.length === 1) return expressions[0];
|
|
4693
|
+
return {
|
|
4694
|
+
type: "LogicalExpression",
|
|
4695
|
+
operator: "AND",
|
|
4696
|
+
operands: expressions
|
|
4697
|
+
};
|
|
4617
4698
|
}
|
|
4618
4699
|
/**
|
|
4619
4700
|
* Gets a relation definition by name
|
|
@@ -4903,8 +4984,52 @@ var hasEntityMeta = (entity) => {
|
|
|
4903
4984
|
return Boolean(getEntityMeta(entity));
|
|
4904
4985
|
};
|
|
4905
4986
|
|
|
4906
|
-
// src/orm/
|
|
4987
|
+
// src/orm/entity-hydration.ts
|
|
4907
4988
|
var toKey2 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
4989
|
+
var populateHydrationCache = (entity, row, meta) => {
|
|
4990
|
+
for (const relationName of Object.keys(meta.table.relations)) {
|
|
4991
|
+
const relation = meta.table.relations[relationName];
|
|
4992
|
+
const data = row[relationName];
|
|
4993
|
+
if (relation.type === RelationKinds.HasOne) {
|
|
4994
|
+
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
4995
|
+
const rootValue = entity[localKey];
|
|
4996
|
+
if (rootValue === void 0 || rootValue === null) continue;
|
|
4997
|
+
if (!data || typeof data !== "object") continue;
|
|
4998
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4999
|
+
cache.set(toKey2(rootValue), data);
|
|
5000
|
+
meta.relationHydration.set(relationName, cache);
|
|
5001
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
5002
|
+
continue;
|
|
5003
|
+
}
|
|
5004
|
+
if (!Array.isArray(data)) continue;
|
|
5005
|
+
if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
|
|
5006
|
+
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
5007
|
+
const rootValue = entity[localKey];
|
|
5008
|
+
if (rootValue === void 0 || rootValue === null) continue;
|
|
5009
|
+
const cache = /* @__PURE__ */ new Map();
|
|
5010
|
+
cache.set(toKey2(rootValue), data);
|
|
5011
|
+
meta.relationHydration.set(relationName, cache);
|
|
5012
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
5013
|
+
continue;
|
|
5014
|
+
}
|
|
5015
|
+
if (relation.type === RelationKinds.BelongsTo) {
|
|
5016
|
+
const targetKey = relation.localKey || findPrimaryKey(relation.target);
|
|
5017
|
+
const cache = /* @__PURE__ */ new Map();
|
|
5018
|
+
for (const item of data) {
|
|
5019
|
+
const pkValue = item[targetKey];
|
|
5020
|
+
if (pkValue === void 0 || pkValue === null) continue;
|
|
5021
|
+
cache.set(toKey2(pkValue), item);
|
|
5022
|
+
}
|
|
5023
|
+
if (cache.size) {
|
|
5024
|
+
meta.relationHydration.set(relationName, cache);
|
|
5025
|
+
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
};
|
|
5030
|
+
|
|
5031
|
+
// src/orm/relations/has-many.ts
|
|
5032
|
+
var toKey3 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
4908
5033
|
var hideInternal = (obj, keys) => {
|
|
4909
5034
|
for (const key of keys) {
|
|
4910
5035
|
Object.defineProperty(obj, key, {
|
|
@@ -4938,13 +5063,13 @@ var DefaultHasManyCollection = class {
|
|
|
4938
5063
|
this.loader = loader;
|
|
4939
5064
|
this.createEntity = createEntity;
|
|
4940
5065
|
this.localKey = localKey;
|
|
4941
|
-
this.loaded = false;
|
|
4942
|
-
this.items = [];
|
|
4943
|
-
this.added = /* @__PURE__ */ new Set();
|
|
4944
|
-
this.removed = /* @__PURE__ */ new Set();
|
|
4945
5066
|
hideInternal(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
4946
5067
|
this.hydrateFromCache();
|
|
4947
5068
|
}
|
|
5069
|
+
loaded = false;
|
|
5070
|
+
items = [];
|
|
5071
|
+
added = /* @__PURE__ */ new Set();
|
|
5072
|
+
removed = /* @__PURE__ */ new Set();
|
|
4948
5073
|
/**
|
|
4949
5074
|
* Loads the related entities if not already loaded.
|
|
4950
5075
|
* @returns Promise resolving to the array of child entities
|
|
@@ -4952,7 +5077,7 @@ var DefaultHasManyCollection = class {
|
|
|
4952
5077
|
async load() {
|
|
4953
5078
|
if (this.loaded) return this.items;
|
|
4954
5079
|
const map = await this.loader();
|
|
4955
|
-
const key =
|
|
5080
|
+
const key = toKey3(this.root[this.localKey]);
|
|
4956
5081
|
const rows = map.get(key) ?? [];
|
|
4957
5082
|
this.items = rows.map((row) => this.createEntity(row));
|
|
4958
5083
|
this.loaded = true;
|
|
@@ -5064,7 +5189,7 @@ var DefaultHasManyCollection = class {
|
|
|
5064
5189
|
};
|
|
5065
5190
|
|
|
5066
5191
|
// src/orm/relations/has-one.ts
|
|
5067
|
-
var
|
|
5192
|
+
var toKey4 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
5068
5193
|
var hideInternal2 = (obj, keys) => {
|
|
5069
5194
|
for (const key of keys) {
|
|
5070
5195
|
Object.defineProperty(obj, key, {
|
|
@@ -5097,8 +5222,6 @@ var DefaultHasOneReference = class {
|
|
|
5097
5222
|
this.loader = loader;
|
|
5098
5223
|
this.createEntity = createEntity;
|
|
5099
5224
|
this.localKey = localKey;
|
|
5100
|
-
this.loaded = false;
|
|
5101
|
-
this.current = null;
|
|
5102
5225
|
hideInternal2(this, [
|
|
5103
5226
|
"ctx",
|
|
5104
5227
|
"meta",
|
|
@@ -5112,6 +5235,8 @@ var DefaultHasOneReference = class {
|
|
|
5112
5235
|
]);
|
|
5113
5236
|
this.populateFromHydrationCache();
|
|
5114
5237
|
}
|
|
5238
|
+
loaded = false;
|
|
5239
|
+
current = null;
|
|
5115
5240
|
async load() {
|
|
5116
5241
|
if (this.loaded) return this.current;
|
|
5117
5242
|
const map = await this.loader();
|
|
@@ -5120,7 +5245,7 @@ var DefaultHasOneReference = class {
|
|
|
5120
5245
|
this.loaded = true;
|
|
5121
5246
|
return this.current;
|
|
5122
5247
|
}
|
|
5123
|
-
const row = map.get(
|
|
5248
|
+
const row = map.get(toKey4(keyValue));
|
|
5124
5249
|
this.current = row ? this.createEntity(row) : null;
|
|
5125
5250
|
this.loaded = true;
|
|
5126
5251
|
return this.current;
|
|
@@ -5192,7 +5317,7 @@ var DefaultHasOneReference = class {
|
|
|
5192
5317
|
};
|
|
5193
5318
|
|
|
5194
5319
|
// src/orm/relations/belongs-to.ts
|
|
5195
|
-
var
|
|
5320
|
+
var toKey5 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
5196
5321
|
var hideInternal3 = (obj, keys) => {
|
|
5197
5322
|
for (const key of keys) {
|
|
5198
5323
|
Object.defineProperty(obj, key, {
|
|
@@ -5225,11 +5350,11 @@ var DefaultBelongsToReference = class {
|
|
|
5225
5350
|
this.loader = loader;
|
|
5226
5351
|
this.createEntity = createEntity;
|
|
5227
5352
|
this.targetKey = targetKey;
|
|
5228
|
-
this.loaded = false;
|
|
5229
|
-
this.current = null;
|
|
5230
5353
|
hideInternal3(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "targetKey"]);
|
|
5231
5354
|
this.populateFromHydrationCache();
|
|
5232
5355
|
}
|
|
5356
|
+
loaded = false;
|
|
5357
|
+
current = null;
|
|
5233
5358
|
async load() {
|
|
5234
5359
|
if (this.loaded) return this.current;
|
|
5235
5360
|
const map = await this.loader();
|
|
@@ -5237,7 +5362,7 @@ var DefaultBelongsToReference = class {
|
|
|
5237
5362
|
if (fkValue === null || fkValue === void 0) {
|
|
5238
5363
|
this.current = null;
|
|
5239
5364
|
} else {
|
|
5240
|
-
const row = map.get(
|
|
5365
|
+
const row = map.get(toKey5(fkValue));
|
|
5241
5366
|
this.current = row ? this.createEntity(row) : null;
|
|
5242
5367
|
}
|
|
5243
5368
|
this.loaded = true;
|
|
@@ -5294,7 +5419,7 @@ var DefaultBelongsToReference = class {
|
|
|
5294
5419
|
};
|
|
5295
5420
|
|
|
5296
5421
|
// src/orm/relations/many-to-many.ts
|
|
5297
|
-
var
|
|
5422
|
+
var toKey6 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
5298
5423
|
var hideInternal4 = (obj, keys) => {
|
|
5299
5424
|
for (const key of keys) {
|
|
5300
5425
|
Object.defineProperty(obj, key, {
|
|
@@ -5327,11 +5452,11 @@ var DefaultManyToManyCollection = class {
|
|
|
5327
5452
|
this.loader = loader;
|
|
5328
5453
|
this.createEntity = createEntity;
|
|
5329
5454
|
this.localKey = localKey;
|
|
5330
|
-
this.loaded = false;
|
|
5331
|
-
this.items = [];
|
|
5332
5455
|
hideInternal4(this, ["ctx", "meta", "root", "relationName", "relation", "rootTable", "loader", "createEntity", "localKey"]);
|
|
5333
5456
|
this.hydrateFromCache();
|
|
5334
5457
|
}
|
|
5458
|
+
loaded = false;
|
|
5459
|
+
items = [];
|
|
5335
5460
|
/**
|
|
5336
5461
|
* Loads the collection items if not already loaded.
|
|
5337
5462
|
* @returns A promise that resolves to the array of target entities.
|
|
@@ -5339,7 +5464,7 @@ var DefaultManyToManyCollection = class {
|
|
|
5339
5464
|
async load() {
|
|
5340
5465
|
if (this.loaded) return this.items;
|
|
5341
5466
|
const map = await this.loader();
|
|
5342
|
-
const key =
|
|
5467
|
+
const key = toKey6(this.root[this.localKey]);
|
|
5343
5468
|
const rows = map.get(key) ?? [];
|
|
5344
5469
|
this.items = rows.map((row) => {
|
|
5345
5470
|
const entity = this.createEntity(row);
|
|
@@ -5421,15 +5546,15 @@ var DefaultManyToManyCollection = class {
|
|
|
5421
5546
|
*/
|
|
5422
5547
|
async syncByIds(ids) {
|
|
5423
5548
|
await this.load();
|
|
5424
|
-
const normalized = new Set(ids.map((id) =>
|
|
5425
|
-
const currentIds = new Set(this.items.map((item) =>
|
|
5549
|
+
const normalized = new Set(ids.map((id) => toKey6(id)));
|
|
5550
|
+
const currentIds = new Set(this.items.map((item) => toKey6(this.extractId(item))));
|
|
5426
5551
|
for (const id of normalized) {
|
|
5427
5552
|
if (!currentIds.has(id)) {
|
|
5428
5553
|
this.attach(id);
|
|
5429
5554
|
}
|
|
5430
5555
|
}
|
|
5431
5556
|
for (const item of [...this.items]) {
|
|
5432
|
-
const itemId =
|
|
5557
|
+
const itemId = toKey6(this.extractId(item));
|
|
5433
5558
|
if (!normalized.has(itemId)) {
|
|
5434
5559
|
this.detach(item);
|
|
5435
5560
|
}
|
|
@@ -5476,7 +5601,7 @@ var DefaultManyToManyCollection = class {
|
|
|
5476
5601
|
}
|
|
5477
5602
|
};
|
|
5478
5603
|
|
|
5479
|
-
// src/orm/lazy-batch.ts
|
|
5604
|
+
// src/orm/lazy-batch/shared.ts
|
|
5480
5605
|
var hasColumns = (columns) => Boolean(columns && columns.length > 0);
|
|
5481
5606
|
var buildColumnSelection = (table, columns, missingMsg) => {
|
|
5482
5607
|
return columns.reduce((acc, column) => {
|
|
@@ -5517,7 +5642,7 @@ var executeQuery = async (ctx, qb) => {
|
|
|
5517
5642
|
const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
5518
5643
|
return rowsFromResults(results);
|
|
5519
5644
|
};
|
|
5520
|
-
var
|
|
5645
|
+
var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
5521
5646
|
var collectKeysFromRoots = (roots, key) => {
|
|
5522
5647
|
const collected = /* @__PURE__ */ new Set();
|
|
5523
5648
|
for (const tracked of roots) {
|
|
@@ -5542,7 +5667,7 @@ var groupRowsByMany = (rows, keyColumn) => {
|
|
|
5542
5667
|
for (const row of rows) {
|
|
5543
5668
|
const value = row[keyColumn];
|
|
5544
5669
|
if (value === null || value === void 0) continue;
|
|
5545
|
-
const key =
|
|
5670
|
+
const key = toKey7(value);
|
|
5546
5671
|
const bucket = grouped.get(key) ?? [];
|
|
5547
5672
|
bucket.push(row);
|
|
5548
5673
|
grouped.set(key, bucket);
|
|
@@ -5554,13 +5679,15 @@ var groupRowsByUnique = (rows, keyColumn) => {
|
|
|
5554
5679
|
for (const row of rows) {
|
|
5555
5680
|
const value = row[keyColumn];
|
|
5556
5681
|
if (value === null || value === void 0) continue;
|
|
5557
|
-
const key =
|
|
5682
|
+
const key = toKey7(value);
|
|
5558
5683
|
if (!lookup.has(key)) {
|
|
5559
5684
|
lookup.set(key, row);
|
|
5560
5685
|
}
|
|
5561
5686
|
}
|
|
5562
5687
|
return lookup;
|
|
5563
5688
|
};
|
|
5689
|
+
|
|
5690
|
+
// src/orm/lazy-batch/has-many.ts
|
|
5564
5691
|
var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options) => {
|
|
5565
5692
|
const localKey = relation.localKey || findPrimaryKey(rootTable);
|
|
5566
5693
|
const roots = ctx.getEntitiesForTable(rootTable);
|
|
@@ -5593,6 +5720,8 @@ var loadHasManyRelation = async (ctx, rootTable, relationName, relation, options
|
|
|
5593
5720
|
}
|
|
5594
5721
|
return filtered;
|
|
5595
5722
|
};
|
|
5723
|
+
|
|
5724
|
+
// src/orm/lazy-batch/has-one.ts
|
|
5596
5725
|
var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options) => {
|
|
5597
5726
|
const localKey = relation.localKey || findPrimaryKey(rootTable);
|
|
5598
5727
|
const roots = ctx.getEntitiesForTable(rootTable);
|
|
@@ -5625,6 +5754,8 @@ var loadHasOneRelation = async (ctx, rootTable, relationName, relation, options)
|
|
|
5625
5754
|
}
|
|
5626
5755
|
return filtered;
|
|
5627
5756
|
};
|
|
5757
|
+
|
|
5758
|
+
// src/orm/lazy-batch/belongs-to.ts
|
|
5628
5759
|
var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, options) => {
|
|
5629
5760
|
const roots = ctx.getEntitiesForTable(rootTable);
|
|
5630
5761
|
const getForeignKeys = () => collectKeysFromRoots(roots, relation.foreignKey);
|
|
@@ -5693,6 +5824,8 @@ var loadBelongsToRelation = async (ctx, rootTable, relationName, relation, optio
|
|
|
5693
5824
|
}
|
|
5694
5825
|
return filtered;
|
|
5695
5826
|
};
|
|
5827
|
+
|
|
5828
|
+
// src/orm/lazy-batch/belongs-to-many.ts
|
|
5696
5829
|
var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, options) => {
|
|
5697
5830
|
const rootKey = relation.localKey || findPrimaryKey(rootTable);
|
|
5698
5831
|
const roots = ctx.getEntitiesForTable(rootTable);
|
|
@@ -5731,12 +5864,12 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
|
|
|
5731
5864
|
if (rootValue === null || rootValue === void 0 || targetValue === null || targetValue === void 0) {
|
|
5732
5865
|
continue;
|
|
5733
5866
|
}
|
|
5734
|
-
const bucket = rootLookup.get(
|
|
5867
|
+
const bucket = rootLookup.get(toKey7(rootValue)) ?? [];
|
|
5735
5868
|
bucket.push({
|
|
5736
5869
|
targetId: targetValue,
|
|
5737
5870
|
pivot: pivotVisibleColumns.size ? filterRow(pivot, pivotVisibleColumns) : {}
|
|
5738
5871
|
});
|
|
5739
|
-
rootLookup.set(
|
|
5872
|
+
rootLookup.set(toKey7(rootValue), bucket);
|
|
5740
5873
|
targetIds.add(targetValue);
|
|
5741
5874
|
}
|
|
5742
5875
|
if (!targetIds.size) {
|
|
@@ -5749,156 +5882,61 @@ var loadBelongsToManyRelation = async (ctx, rootTable, relationName, relation, o
|
|
|
5749
5882
|
const targetSelectedColumns = targetRequestedColumns ? [...targetRequestedColumns] : Object.keys(relation.target.columns);
|
|
5750
5883
|
if (!targetSelectedColumns.includes(targetKey)) {
|
|
5751
5884
|
targetSelectedColumns.push(targetKey);
|
|
5752
|
-
}
|
|
5753
|
-
const targetSelection = buildColumnSelection(
|
|
5754
|
-
relation.target,
|
|
5755
|
-
targetSelectedColumns,
|
|
5756
|
-
(column) => `Column '${column}' not found on relation '${relationName}'`
|
|
5757
|
-
);
|
|
5758
|
-
const targetRows = await fetchRowsForKeys(
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
var relationLoaderCache = (meta, relationName, factory) => {
|
|
5779
|
-
if (meta.relationCache.has(relationName)) {
|
|
5780
|
-
return meta.relationCache.get(relationName);
|
|
5781
|
-
}
|
|
5782
|
-
const promise = factory().then((value) => {
|
|
5783
|
-
for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
|
|
5784
|
-
const otherMeta = getEntityMeta(tracked.entity);
|
|
5785
|
-
if (!otherMeta) continue;
|
|
5786
|
-
otherMeta.relationHydration.set(relationName, value);
|
|
5787
|
-
}
|
|
5788
|
-
return value;
|
|
5789
|
-
});
|
|
5790
|
-
meta.relationCache.set(relationName, promise);
|
|
5791
|
-
for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
|
|
5792
|
-
const otherMeta = getEntityMeta(tracked.entity);
|
|
5793
|
-
if (!otherMeta) continue;
|
|
5794
|
-
otherMeta.relationCache.set(relationName, promise);
|
|
5795
|
-
}
|
|
5796
|
-
return promise;
|
|
5797
|
-
};
|
|
5798
|
-
var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
|
|
5799
|
-
const target = { ...row };
|
|
5800
|
-
const meta = {
|
|
5801
|
-
ctx,
|
|
5802
|
-
table,
|
|
5803
|
-
lazyRelations: [...lazyRelations],
|
|
5804
|
-
lazyRelationOptions: new Map(lazyRelationOptions),
|
|
5805
|
-
relationCache: /* @__PURE__ */ new Map(),
|
|
5806
|
-
relationHydration: /* @__PURE__ */ new Map(),
|
|
5807
|
-
relationWrappers: /* @__PURE__ */ new Map()
|
|
5808
|
-
};
|
|
5809
|
-
Object.defineProperty(target, ENTITY_META, {
|
|
5810
|
-
value: meta,
|
|
5811
|
-
enumerable: false,
|
|
5812
|
-
writable: false
|
|
5813
|
-
});
|
|
5814
|
-
const handler = {
|
|
5815
|
-
get(targetObj, prop, receiver) {
|
|
5816
|
-
if (prop === ENTITY_META) {
|
|
5817
|
-
return meta;
|
|
5818
|
-
}
|
|
5819
|
-
if (prop === "$load") {
|
|
5820
|
-
return async (relationName) => {
|
|
5821
|
-
const wrapper = getRelationWrapper(meta, relationName, receiver);
|
|
5822
|
-
if (wrapper && typeof wrapper.load === "function") {
|
|
5823
|
-
return wrapper.load();
|
|
5824
|
-
}
|
|
5825
|
-
return void 0;
|
|
5826
|
-
};
|
|
5827
|
-
}
|
|
5828
|
-
if (typeof prop === "string" && table.relations[prop]) {
|
|
5829
|
-
return getRelationWrapper(meta, prop, receiver);
|
|
5830
|
-
}
|
|
5831
|
-
return Reflect.get(targetObj, prop, receiver);
|
|
5832
|
-
},
|
|
5833
|
-
set(targetObj, prop, value, receiver) {
|
|
5834
|
-
const result = Reflect.set(targetObj, prop, value, receiver);
|
|
5835
|
-
if (typeof prop === "string" && table.columns[prop]) {
|
|
5836
|
-
ctx.markDirty(receiver);
|
|
5837
|
-
}
|
|
5838
|
-
return result;
|
|
5839
|
-
}
|
|
5840
|
-
};
|
|
5841
|
-
const proxy = new Proxy(target, handler);
|
|
5842
|
-
populateHydrationCache(proxy, row, meta);
|
|
5843
|
-
return proxy;
|
|
5844
|
-
};
|
|
5845
|
-
var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
|
|
5846
|
-
const pkName = findPrimaryKey(table);
|
|
5847
|
-
const pkValue = row[pkName];
|
|
5848
|
-
if (pkValue !== void 0 && pkValue !== null) {
|
|
5849
|
-
const tracked = ctx.getEntity(table, pkValue);
|
|
5850
|
-
if (tracked) return tracked;
|
|
5851
|
-
}
|
|
5852
|
-
const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
|
|
5853
|
-
if (pkValue !== void 0 && pkValue !== null) {
|
|
5854
|
-
ctx.trackManaged(table, pkValue, entity);
|
|
5855
|
-
} else {
|
|
5856
|
-
ctx.trackNew(table, entity);
|
|
5857
|
-
}
|
|
5858
|
-
return entity;
|
|
5859
|
-
};
|
|
5860
|
-
var toKey7 = (value) => value === null || value === void 0 ? "" : String(value);
|
|
5861
|
-
var populateHydrationCache = (entity, row, meta) => {
|
|
5862
|
-
for (const relationName of Object.keys(meta.table.relations)) {
|
|
5863
|
-
const relation = meta.table.relations[relationName];
|
|
5864
|
-
const data = row[relationName];
|
|
5865
|
-
if (relation.type === RelationKinds.HasOne) {
|
|
5866
|
-
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
5867
|
-
const rootValue = entity[localKey];
|
|
5868
|
-
if (rootValue === void 0 || rootValue === null) continue;
|
|
5869
|
-
if (!data || typeof data !== "object") continue;
|
|
5870
|
-
const cache = /* @__PURE__ */ new Map();
|
|
5871
|
-
cache.set(toKey7(rootValue), data);
|
|
5872
|
-
meta.relationHydration.set(relationName, cache);
|
|
5873
|
-
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
5874
|
-
continue;
|
|
5875
|
-
}
|
|
5876
|
-
if (!Array.isArray(data)) continue;
|
|
5877
|
-
if (relation.type === RelationKinds.HasMany || relation.type === RelationKinds.BelongsToMany) {
|
|
5878
|
-
const localKey = relation.localKey || findPrimaryKey(meta.table);
|
|
5879
|
-
const rootValue = entity[localKey];
|
|
5880
|
-
if (rootValue === void 0 || rootValue === null) continue;
|
|
5881
|
-
const cache = /* @__PURE__ */ new Map();
|
|
5882
|
-
cache.set(toKey7(rootValue), data);
|
|
5883
|
-
meta.relationHydration.set(relationName, cache);
|
|
5884
|
-
meta.relationCache.set(relationName, Promise.resolve(cache));
|
|
5885
|
-
continue;
|
|
5885
|
+
}
|
|
5886
|
+
const targetSelection = buildColumnSelection(
|
|
5887
|
+
relation.target,
|
|
5888
|
+
targetSelectedColumns,
|
|
5889
|
+
(column) => `Column '${column}' not found on relation '${relationName}'`
|
|
5890
|
+
);
|
|
5891
|
+
const targetRows = await fetchRowsForKeys(
|
|
5892
|
+
ctx,
|
|
5893
|
+
relation.target,
|
|
5894
|
+
targetPkColumn,
|
|
5895
|
+
targetIds,
|
|
5896
|
+
targetSelection,
|
|
5897
|
+
options?.filter
|
|
5898
|
+
);
|
|
5899
|
+
const targetMap = groupRowsByUnique(targetRows, targetKey);
|
|
5900
|
+
const targetVisibleColumns = new Set(targetSelectedColumns);
|
|
5901
|
+
const result = /* @__PURE__ */ new Map();
|
|
5902
|
+
for (const [rootId, entries] of rootLookup.entries()) {
|
|
5903
|
+
const bucket = [];
|
|
5904
|
+
for (const entry of entries) {
|
|
5905
|
+
const targetRow = targetMap.get(toKey7(entry.targetId));
|
|
5906
|
+
if (!targetRow) continue;
|
|
5907
|
+
bucket.push({
|
|
5908
|
+
...targetRequestedColumns ? filterRow(targetRow, targetVisibleColumns) : targetRow,
|
|
5909
|
+
_pivot: entry.pivot
|
|
5910
|
+
});
|
|
5886
5911
|
}
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5912
|
+
result.set(rootId, bucket);
|
|
5913
|
+
}
|
|
5914
|
+
return result;
|
|
5915
|
+
};
|
|
5916
|
+
|
|
5917
|
+
// src/orm/entity-relation-cache.ts
|
|
5918
|
+
var relationLoaderCache = (meta, relationName, factory) => {
|
|
5919
|
+
if (meta.relationCache.has(relationName)) {
|
|
5920
|
+
return meta.relationCache.get(relationName);
|
|
5921
|
+
}
|
|
5922
|
+
const promise = factory().then((value) => {
|
|
5923
|
+
for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
|
|
5924
|
+
const otherMeta = getEntityMeta(tracked.entity);
|
|
5925
|
+
if (!otherMeta) continue;
|
|
5926
|
+
otherMeta.relationHydration.set(relationName, value);
|
|
5899
5927
|
}
|
|
5928
|
+
return value;
|
|
5929
|
+
});
|
|
5930
|
+
meta.relationCache.set(relationName, promise);
|
|
5931
|
+
for (const tracked of meta.ctx.getEntitiesForTable(meta.table)) {
|
|
5932
|
+
const otherMeta = getEntityMeta(tracked.entity);
|
|
5933
|
+
if (!otherMeta) continue;
|
|
5934
|
+
otherMeta.relationCache.set(relationName, promise);
|
|
5900
5935
|
}
|
|
5936
|
+
return promise;
|
|
5901
5937
|
};
|
|
5938
|
+
|
|
5939
|
+
// src/orm/entity-relations.ts
|
|
5902
5940
|
var proxifyRelationWrapper = (wrapper) => {
|
|
5903
5941
|
return new Proxy(wrapper, {
|
|
5904
5942
|
get(target, prop, receiver) {
|
|
@@ -5951,98 +5989,93 @@ var proxifyRelationWrapper = (wrapper) => {
|
|
|
5951
5989
|
}
|
|
5952
5990
|
});
|
|
5953
5991
|
};
|
|
5954
|
-
var getRelationWrapper = (meta, relationName, owner) => {
|
|
5955
|
-
|
|
5956
|
-
|
|
5992
|
+
var getRelationWrapper = (meta, relationName, owner, createEntity) => {
|
|
5993
|
+
const relationKey = relationName;
|
|
5994
|
+
if (meta.relationWrappers.has(relationKey)) {
|
|
5995
|
+
return meta.relationWrappers.get(relationKey);
|
|
5957
5996
|
}
|
|
5958
|
-
const relation = meta.table.relations[
|
|
5997
|
+
const relation = meta.table.relations[relationKey];
|
|
5959
5998
|
if (!relation) return void 0;
|
|
5960
|
-
const wrapper = instantiateWrapper(meta,
|
|
5999
|
+
const wrapper = instantiateWrapper(meta, relationKey, relation, owner, createEntity);
|
|
5961
6000
|
if (!wrapper) return void 0;
|
|
5962
6001
|
const proxied = proxifyRelationWrapper(wrapper);
|
|
5963
|
-
meta.relationWrappers.set(
|
|
6002
|
+
meta.relationWrappers.set(relationKey, proxied);
|
|
5964
6003
|
return proxied;
|
|
5965
6004
|
};
|
|
5966
|
-
var instantiateWrapper = (meta, relationName, relation, owner) => {
|
|
6005
|
+
var instantiateWrapper = (meta, relationName, relation, owner, createEntity) => {
|
|
6006
|
+
const metaBase = meta;
|
|
5967
6007
|
const lazyOptions = meta.lazyRelationOptions.get(relationName);
|
|
6008
|
+
const loadCached = (factory) => relationLoaderCache(metaBase, relationName, factory);
|
|
5968
6009
|
switch (relation.type) {
|
|
5969
6010
|
case RelationKinds.HasOne: {
|
|
5970
6011
|
const hasOne2 = relation;
|
|
5971
6012
|
const localKey = hasOne2.localKey || findPrimaryKey(meta.table);
|
|
5972
|
-
const loader = () =>
|
|
5973
|
-
meta,
|
|
5974
|
-
relationName,
|
|
6013
|
+
const loader = () => loadCached(
|
|
5975
6014
|
() => loadHasOneRelation(meta.ctx, meta.table, relationName, hasOne2, lazyOptions)
|
|
5976
6015
|
);
|
|
5977
6016
|
return new DefaultHasOneReference(
|
|
5978
6017
|
meta.ctx,
|
|
5979
|
-
|
|
6018
|
+
metaBase,
|
|
5980
6019
|
owner,
|
|
5981
6020
|
relationName,
|
|
5982
6021
|
hasOne2,
|
|
5983
6022
|
meta.table,
|
|
5984
6023
|
loader,
|
|
5985
|
-
(row) =>
|
|
6024
|
+
(row) => createEntity(hasOne2.target, row),
|
|
5986
6025
|
localKey
|
|
5987
6026
|
);
|
|
5988
6027
|
}
|
|
5989
6028
|
case RelationKinds.HasMany: {
|
|
5990
6029
|
const hasMany2 = relation;
|
|
5991
6030
|
const localKey = hasMany2.localKey || findPrimaryKey(meta.table);
|
|
5992
|
-
const loader = () =>
|
|
5993
|
-
meta,
|
|
5994
|
-
relationName,
|
|
6031
|
+
const loader = () => loadCached(
|
|
5995
6032
|
() => loadHasManyRelation(meta.ctx, meta.table, relationName, hasMany2, lazyOptions)
|
|
5996
6033
|
);
|
|
5997
6034
|
return new DefaultHasManyCollection(
|
|
5998
6035
|
meta.ctx,
|
|
5999
|
-
|
|
6036
|
+
metaBase,
|
|
6000
6037
|
owner,
|
|
6001
6038
|
relationName,
|
|
6002
6039
|
hasMany2,
|
|
6003
6040
|
meta.table,
|
|
6004
6041
|
loader,
|
|
6005
|
-
(row) =>
|
|
6042
|
+
(row) => createEntity(relation.target, row),
|
|
6006
6043
|
localKey
|
|
6007
6044
|
);
|
|
6008
6045
|
}
|
|
6009
6046
|
case RelationKinds.BelongsTo: {
|
|
6010
6047
|
const belongsTo2 = relation;
|
|
6011
6048
|
const targetKey = belongsTo2.localKey || findPrimaryKey(belongsTo2.target);
|
|
6012
|
-
const loader = () =>
|
|
6013
|
-
meta,
|
|
6014
|
-
relationName,
|
|
6049
|
+
const loader = () => loadCached(
|
|
6015
6050
|
() => loadBelongsToRelation(meta.ctx, meta.table, relationName, belongsTo2, lazyOptions)
|
|
6016
6051
|
);
|
|
6017
6052
|
return new DefaultBelongsToReference(
|
|
6018
6053
|
meta.ctx,
|
|
6019
|
-
|
|
6054
|
+
metaBase,
|
|
6020
6055
|
owner,
|
|
6021
6056
|
relationName,
|
|
6022
6057
|
belongsTo2,
|
|
6023
6058
|
meta.table,
|
|
6024
6059
|
loader,
|
|
6025
|
-
(row) =>
|
|
6060
|
+
(row) => createEntity(relation.target, row),
|
|
6026
6061
|
targetKey
|
|
6027
6062
|
);
|
|
6028
6063
|
}
|
|
6029
6064
|
case RelationKinds.BelongsToMany: {
|
|
6030
6065
|
const many = relation;
|
|
6031
6066
|
const localKey = many.localKey || findPrimaryKey(meta.table);
|
|
6032
|
-
const loader = () =>
|
|
6033
|
-
meta,
|
|
6034
|
-
relationName,
|
|
6067
|
+
const loader = () => loadCached(
|
|
6035
6068
|
() => loadBelongsToManyRelation(meta.ctx, meta.table, relationName, many, lazyOptions)
|
|
6036
6069
|
);
|
|
6037
6070
|
return new DefaultManyToManyCollection(
|
|
6038
6071
|
meta.ctx,
|
|
6039
|
-
|
|
6072
|
+
metaBase,
|
|
6040
6073
|
owner,
|
|
6041
6074
|
relationName,
|
|
6042
6075
|
many,
|
|
6043
6076
|
meta.table,
|
|
6044
6077
|
loader,
|
|
6045
|
-
(row) =>
|
|
6078
|
+
(row) => createEntity(relation.target, row),
|
|
6046
6079
|
localKey
|
|
6047
6080
|
);
|
|
6048
6081
|
}
|
|
@@ -6051,6 +6084,71 @@ var instantiateWrapper = (meta, relationName, relation, owner) => {
|
|
|
6051
6084
|
}
|
|
6052
6085
|
};
|
|
6053
6086
|
|
|
6087
|
+
// src/orm/entity.ts
|
|
6088
|
+
var createEntityProxy = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
|
|
6089
|
+
const target = { ...row };
|
|
6090
|
+
const meta = {
|
|
6091
|
+
ctx,
|
|
6092
|
+
table,
|
|
6093
|
+
lazyRelations: [...lazyRelations],
|
|
6094
|
+
lazyRelationOptions: new Map(lazyRelationOptions),
|
|
6095
|
+
relationCache: /* @__PURE__ */ new Map(),
|
|
6096
|
+
relationHydration: /* @__PURE__ */ new Map(),
|
|
6097
|
+
relationWrappers: /* @__PURE__ */ new Map()
|
|
6098
|
+
};
|
|
6099
|
+
const createRelationEntity = (relationTable, relationRow) => createEntityFromRow(meta.ctx, relationTable, relationRow);
|
|
6100
|
+
Object.defineProperty(target, ENTITY_META, {
|
|
6101
|
+
value: meta,
|
|
6102
|
+
enumerable: false,
|
|
6103
|
+
writable: false
|
|
6104
|
+
});
|
|
6105
|
+
const handler = {
|
|
6106
|
+
get(targetObj, prop, receiver) {
|
|
6107
|
+
if (prop === ENTITY_META) {
|
|
6108
|
+
return meta;
|
|
6109
|
+
}
|
|
6110
|
+
if (prop === "$load") {
|
|
6111
|
+
return async (relationName) => {
|
|
6112
|
+
const wrapper = getRelationWrapper(meta, relationName, receiver, createRelationEntity);
|
|
6113
|
+
if (wrapper && typeof wrapper.load === "function") {
|
|
6114
|
+
return wrapper.load();
|
|
6115
|
+
}
|
|
6116
|
+
return void 0;
|
|
6117
|
+
};
|
|
6118
|
+
}
|
|
6119
|
+
if (typeof prop === "string" && table.relations[prop]) {
|
|
6120
|
+
return getRelationWrapper(meta, prop, receiver, createRelationEntity);
|
|
6121
|
+
}
|
|
6122
|
+
return Reflect.get(targetObj, prop, receiver);
|
|
6123
|
+
},
|
|
6124
|
+
set(targetObj, prop, value, receiver) {
|
|
6125
|
+
const result = Reflect.set(targetObj, prop, value, receiver);
|
|
6126
|
+
if (typeof prop === "string" && table.columns[prop]) {
|
|
6127
|
+
ctx.markDirty(receiver);
|
|
6128
|
+
}
|
|
6129
|
+
return result;
|
|
6130
|
+
}
|
|
6131
|
+
};
|
|
6132
|
+
const proxy = new Proxy(target, handler);
|
|
6133
|
+
populateHydrationCache(proxy, row, meta);
|
|
6134
|
+
return proxy;
|
|
6135
|
+
};
|
|
6136
|
+
var createEntityFromRow = (ctx, table, row, lazyRelations = [], lazyRelationOptions = /* @__PURE__ */ new Map()) => {
|
|
6137
|
+
const pkName = findPrimaryKey(table);
|
|
6138
|
+
const pkValue = row[pkName];
|
|
6139
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
6140
|
+
const tracked = ctx.getEntity(table, pkValue);
|
|
6141
|
+
if (tracked) return tracked;
|
|
6142
|
+
}
|
|
6143
|
+
const entity = createEntityProxy(ctx, table, row, lazyRelations, lazyRelationOptions);
|
|
6144
|
+
if (pkValue !== void 0 && pkValue !== null) {
|
|
6145
|
+
ctx.trackManaged(table, pkValue, entity);
|
|
6146
|
+
} else {
|
|
6147
|
+
ctx.trackNew(table, entity);
|
|
6148
|
+
}
|
|
6149
|
+
return entity;
|
|
6150
|
+
};
|
|
6151
|
+
|
|
6054
6152
|
// src/orm/execute.ts
|
|
6055
6153
|
var flattenResults = (results) => {
|
|
6056
6154
|
const rows = [];
|
|
@@ -6140,14 +6238,416 @@ var loadLazyRelationsForTable = async (ctx, table, lazyRelations, lazyRelationOp
|
|
|
6140
6238
|
}
|
|
6141
6239
|
};
|
|
6142
6240
|
|
|
6143
|
-
// src/query-builder/query-resolution.ts
|
|
6144
|
-
function resolveSelectQuery(query) {
|
|
6145
|
-
const candidate = query;
|
|
6146
|
-
return typeof candidate.getAST === "function" && candidate.getAST ? candidate.getAST() : query;
|
|
6147
|
-
}
|
|
6241
|
+
// src/query-builder/query-resolution.ts
|
|
6242
|
+
function resolveSelectQuery(query) {
|
|
6243
|
+
const candidate = query;
|
|
6244
|
+
return typeof candidate.getAST === "function" && candidate.getAST ? candidate.getAST() : query;
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
// src/query-builder/select/select-operations.ts
|
|
6248
|
+
function applyOrderBy(context, predicateFacet, term, directionOrOptions) {
|
|
6249
|
+
const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
|
|
6250
|
+
const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
|
|
6251
|
+
return predicateFacet.orderBy(context, term, dir, options.nulls, options.collation);
|
|
6252
|
+
}
|
|
6253
|
+
async function executeCount(context, env, session) {
|
|
6254
|
+
const unpagedAst = {
|
|
6255
|
+
...context.state.ast,
|
|
6256
|
+
orderBy: void 0,
|
|
6257
|
+
limit: void 0,
|
|
6258
|
+
offset: void 0
|
|
6259
|
+
};
|
|
6260
|
+
const nextState = new SelectQueryState(env.table, unpagedAst);
|
|
6261
|
+
const nextContext = {
|
|
6262
|
+
...context,
|
|
6263
|
+
state: nextState
|
|
6264
|
+
};
|
|
6265
|
+
const subAst = nextContext.hydration.applyToAst(nextState.ast);
|
|
6266
|
+
const countQuery = {
|
|
6267
|
+
type: "SelectQuery",
|
|
6268
|
+
from: derivedTable(subAst, "__metal_count"),
|
|
6269
|
+
columns: [{ type: "Function", name: "COUNT", args: [], alias: "total" }],
|
|
6270
|
+
joins: []
|
|
6271
|
+
};
|
|
6272
|
+
const execCtx = session.getExecutionContext();
|
|
6273
|
+
const compiled = execCtx.dialect.compileSelect(countQuery);
|
|
6274
|
+
const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
6275
|
+
const value = results[0]?.values?.[0]?.[0];
|
|
6276
|
+
if (typeof value === "number") return value;
|
|
6277
|
+
if (typeof value === "bigint") return Number(value);
|
|
6278
|
+
if (typeof value === "string") return Number(value);
|
|
6279
|
+
return value === null || value === void 0 ? 0 : Number(value);
|
|
6280
|
+
}
|
|
6281
|
+
async function executePagedQuery(builder, session, options, countCallback) {
|
|
6282
|
+
const { page, pageSize } = options;
|
|
6283
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
6284
|
+
throw new Error("executePaged: page must be an integer >= 1");
|
|
6285
|
+
}
|
|
6286
|
+
if (!Number.isInteger(pageSize) || pageSize < 1) {
|
|
6287
|
+
throw new Error("executePaged: pageSize must be an integer >= 1");
|
|
6288
|
+
}
|
|
6289
|
+
const offset = (page - 1) * pageSize;
|
|
6290
|
+
const [items, totalItems] = await Promise.all([
|
|
6291
|
+
builder.limit(pageSize).offset(offset).execute(session),
|
|
6292
|
+
countCallback(session)
|
|
6293
|
+
]);
|
|
6294
|
+
return { items, totalItems };
|
|
6295
|
+
}
|
|
6296
|
+
function buildWhereHasPredicate(env, context, relationFacet, createChildBuilder, relationName, callbackOrOptions, maybeOptions, negate = false) {
|
|
6297
|
+
const relation = env.table.relations[relationName];
|
|
6298
|
+
if (!relation) {
|
|
6299
|
+
throw new Error(`Relation '${relationName}' not found on table '${env.table.name}'`);
|
|
6300
|
+
}
|
|
6301
|
+
const callback = typeof callbackOrOptions === "function" ? callbackOrOptions : void 0;
|
|
6302
|
+
const options = typeof callbackOrOptions === "function" ? maybeOptions : callbackOrOptions;
|
|
6303
|
+
let subQb = createChildBuilder(relation.target);
|
|
6304
|
+
if (callback) {
|
|
6305
|
+
subQb = callback(subQb);
|
|
6306
|
+
}
|
|
6307
|
+
const subAst = subQb.getAST();
|
|
6308
|
+
const finalSubAst = relationFacet.applyRelationCorrelation(
|
|
6309
|
+
context,
|
|
6310
|
+
relationName,
|
|
6311
|
+
subAst,
|
|
6312
|
+
options?.correlate
|
|
6313
|
+
);
|
|
6314
|
+
return negate ? notExists(finalSubAst) : exists(finalSubAst);
|
|
6315
|
+
}
|
|
6316
|
+
|
|
6317
|
+
// src/query-builder/select/from-facet.ts
|
|
6318
|
+
var SelectFromFacet = class {
|
|
6319
|
+
/**
|
|
6320
|
+
* Creates a new SelectFromFacet instance
|
|
6321
|
+
* @param env - Query builder environment
|
|
6322
|
+
* @param createAstService - Function to create AST service
|
|
6323
|
+
*/
|
|
6324
|
+
constructor(env, createAstService) {
|
|
6325
|
+
this.env = env;
|
|
6326
|
+
this.createAstService = createAstService;
|
|
6327
|
+
}
|
|
6328
|
+
/**
|
|
6329
|
+
* Applies an alias to the FROM table
|
|
6330
|
+
* @param context - Current query context
|
|
6331
|
+
* @param alias - Alias to apply
|
|
6332
|
+
* @returns Updated query context with aliased FROM
|
|
6333
|
+
*/
|
|
6334
|
+
as(context, alias) {
|
|
6335
|
+
const from = context.state.ast.from;
|
|
6336
|
+
if (from.type !== "Table") {
|
|
6337
|
+
throw new Error("Cannot alias non-table FROM sources");
|
|
6338
|
+
}
|
|
6339
|
+
const nextFrom = { ...from, alias };
|
|
6340
|
+
const astService = this.createAstService(context.state);
|
|
6341
|
+
const nextState = astService.withFrom(nextFrom);
|
|
6342
|
+
return { state: nextState, hydration: context.hydration };
|
|
6343
|
+
}
|
|
6344
|
+
/**
|
|
6345
|
+
* Sets the FROM clause to a subquery
|
|
6346
|
+
* @param context - Current query context
|
|
6347
|
+
* @param subAst - Subquery AST
|
|
6348
|
+
* @param alias - Alias for the subquery
|
|
6349
|
+
* @param columnAliases - Optional column aliases
|
|
6350
|
+
* @returns Updated query context with subquery FROM
|
|
6351
|
+
*/
|
|
6352
|
+
fromSubquery(context, subAst, alias, columnAliases) {
|
|
6353
|
+
const fromNode = derivedTable(subAst, alias, columnAliases);
|
|
6354
|
+
const astService = this.createAstService(context.state);
|
|
6355
|
+
const nextState = astService.withFrom(fromNode);
|
|
6356
|
+
return { state: nextState, hydration: context.hydration };
|
|
6357
|
+
}
|
|
6358
|
+
/**
|
|
6359
|
+
* Sets the FROM clause to a function table
|
|
6360
|
+
* @param context - Current query context
|
|
6361
|
+
* @param name - Function name
|
|
6362
|
+
* @param args - Function arguments
|
|
6363
|
+
* @param alias - Optional alias for the function table
|
|
6364
|
+
* @param options - Optional function table options
|
|
6365
|
+
* @returns Updated query context with function table FROM
|
|
6366
|
+
*/
|
|
6367
|
+
fromFunctionTable(context, name, args, alias, options) {
|
|
6368
|
+
const functionTable = fnTable(name, args, alias, options);
|
|
6369
|
+
const astService = this.createAstService(context.state);
|
|
6370
|
+
const nextState = astService.withFrom(functionTable);
|
|
6371
|
+
return { state: nextState, hydration: context.hydration };
|
|
6372
|
+
}
|
|
6373
|
+
};
|
|
6374
|
+
|
|
6375
|
+
// src/query-builder/select/join-facet.ts
|
|
6376
|
+
var SelectJoinFacet = class {
|
|
6377
|
+
constructor(env, createAstService) {
|
|
6378
|
+
this.env = env;
|
|
6379
|
+
this.createAstService = createAstService;
|
|
6380
|
+
}
|
|
6381
|
+
applyJoin(context, table, condition, kind) {
|
|
6382
|
+
const joinNode = createJoinNode(kind, { type: "Table", name: table.name, schema: table.schema }, condition);
|
|
6383
|
+
const astService = this.createAstService(context.state);
|
|
6384
|
+
const nextState = astService.withJoin(joinNode);
|
|
6385
|
+
return { state: nextState, hydration: context.hydration };
|
|
6386
|
+
}
|
|
6387
|
+
joinSubquery(context, subAst, alias, condition, joinKind, columnAliases) {
|
|
6388
|
+
const joinNode = createJoinNode(joinKind, derivedTable(subAst, alias, columnAliases), condition);
|
|
6389
|
+
const astService = this.createAstService(context.state);
|
|
6390
|
+
const nextState = astService.withJoin(joinNode);
|
|
6391
|
+
return { state: nextState, hydration: context.hydration };
|
|
6392
|
+
}
|
|
6393
|
+
joinFunctionTable(context, name, args, alias, condition, joinKind, options) {
|
|
6394
|
+
const functionTable = fnTable(name, args, alias, options);
|
|
6395
|
+
const joinNode = createJoinNode(joinKind, functionTable, condition);
|
|
6396
|
+
const astService = this.createAstService(context.state);
|
|
6397
|
+
const nextState = astService.withJoin(joinNode);
|
|
6398
|
+
return { state: nextState, hydration: context.hydration };
|
|
6399
|
+
}
|
|
6400
|
+
};
|
|
6401
|
+
|
|
6402
|
+
// src/query-builder/select/projection-facet.ts
|
|
6403
|
+
var SelectProjectionFacet = class {
|
|
6404
|
+
/**
|
|
6405
|
+
* Creates a new SelectProjectionFacet instance
|
|
6406
|
+
* @param columnSelector - Column selector dependency
|
|
6407
|
+
*/
|
|
6408
|
+
constructor(columnSelector) {
|
|
6409
|
+
this.columnSelector = columnSelector;
|
|
6410
|
+
}
|
|
6411
|
+
/**
|
|
6412
|
+
* Selects columns for the query
|
|
6413
|
+
* @param context - Current query context
|
|
6414
|
+
* @param columns - Columns to select
|
|
6415
|
+
* @returns Updated query context with selected columns
|
|
6416
|
+
*/
|
|
6417
|
+
select(context, columns) {
|
|
6418
|
+
return this.columnSelector.select(context, columns);
|
|
6419
|
+
}
|
|
6420
|
+
/**
|
|
6421
|
+
* Selects raw column expressions
|
|
6422
|
+
* @param context - Current query context
|
|
6423
|
+
* @param cols - Raw column expressions
|
|
6424
|
+
* @returns Updated query context with raw column selections
|
|
6425
|
+
*/
|
|
6426
|
+
selectRaw(context, cols) {
|
|
6427
|
+
return this.columnSelector.selectRaw(context, cols);
|
|
6428
|
+
}
|
|
6429
|
+
/**
|
|
6430
|
+
* Selects a subquery as a column
|
|
6431
|
+
* @param context - Current query context
|
|
6432
|
+
* @param alias - Alias for the subquery
|
|
6433
|
+
* @param query - Subquery to select
|
|
6434
|
+
* @returns Updated query context with subquery selection
|
|
6435
|
+
*/
|
|
6436
|
+
selectSubquery(context, alias, query) {
|
|
6437
|
+
return this.columnSelector.selectSubquery(context, alias, query);
|
|
6438
|
+
}
|
|
6439
|
+
/**
|
|
6440
|
+
* Adds DISTINCT clause to the query
|
|
6441
|
+
* @param context - Current query context
|
|
6442
|
+
* @param cols - Columns to make distinct
|
|
6443
|
+
* @returns Updated query context with DISTINCT clause
|
|
6444
|
+
*/
|
|
6445
|
+
distinct(context, cols) {
|
|
6446
|
+
return this.columnSelector.distinct(context, cols);
|
|
6447
|
+
}
|
|
6448
|
+
};
|
|
6449
|
+
|
|
6450
|
+
// src/query-builder/select/predicate-facet.ts
|
|
6451
|
+
var SelectPredicateFacet = class {
|
|
6452
|
+
/**
|
|
6453
|
+
* Creates a new SelectPredicateFacet instance
|
|
6454
|
+
* @param env - Query builder environment
|
|
6455
|
+
* @param createAstService - Function to create AST service
|
|
6456
|
+
*/
|
|
6457
|
+
constructor(env, createAstService) {
|
|
6458
|
+
this.env = env;
|
|
6459
|
+
this.createAstService = createAstService;
|
|
6460
|
+
}
|
|
6461
|
+
/**
|
|
6462
|
+
* Adds a WHERE condition to the query
|
|
6463
|
+
* @param context - Current query context
|
|
6464
|
+
* @param expr - WHERE expression
|
|
6465
|
+
* @returns Updated query context with WHERE condition
|
|
6466
|
+
*/
|
|
6467
|
+
where(context, expr) {
|
|
6468
|
+
const astService = this.createAstService(context.state);
|
|
6469
|
+
const nextState = astService.withWhere(expr);
|
|
6470
|
+
return { state: nextState, hydration: context.hydration };
|
|
6471
|
+
}
|
|
6472
|
+
/**
|
|
6473
|
+
* Adds a GROUP BY clause to the query
|
|
6474
|
+
* @param context - Current query context
|
|
6475
|
+
* @param term - Column or ordering term to group by
|
|
6476
|
+
* @returns Updated query context with GROUP BY clause
|
|
6477
|
+
*/
|
|
6478
|
+
groupBy(context, term) {
|
|
6479
|
+
const astService = this.createAstService(context.state);
|
|
6480
|
+
const nextState = astService.withGroupBy(term);
|
|
6481
|
+
return { state: nextState, hydration: context.hydration };
|
|
6482
|
+
}
|
|
6483
|
+
/**
|
|
6484
|
+
* Adds a HAVING condition to the query
|
|
6485
|
+
* @param context - Current query context
|
|
6486
|
+
* @param expr - HAVING expression
|
|
6487
|
+
* @returns Updated query context with HAVING condition
|
|
6488
|
+
*/
|
|
6489
|
+
having(context, expr) {
|
|
6490
|
+
const astService = this.createAstService(context.state);
|
|
6491
|
+
const nextState = astService.withHaving(expr);
|
|
6492
|
+
return { state: nextState, hydration: context.hydration };
|
|
6493
|
+
}
|
|
6494
|
+
/**
|
|
6495
|
+
* Adds an ORDER BY clause to the query
|
|
6496
|
+
* @param context - Current query context
|
|
6497
|
+
* @param term - Column or ordering term to order by
|
|
6498
|
+
* @param direction - Order direction
|
|
6499
|
+
* @param nulls - Nulls ordering
|
|
6500
|
+
* @param collation - Collation
|
|
6501
|
+
* @returns Updated query context with ORDER BY clause
|
|
6502
|
+
*/
|
|
6503
|
+
orderBy(context, term, direction, nulls, collation) {
|
|
6504
|
+
const astService = this.createAstService(context.state);
|
|
6505
|
+
const nextState = astService.withOrderBy(term, direction, nulls, collation);
|
|
6506
|
+
return { state: nextState, hydration: context.hydration };
|
|
6507
|
+
}
|
|
6508
|
+
/**
|
|
6509
|
+
* Adds a LIMIT clause to the query
|
|
6510
|
+
* @param context - Current query context
|
|
6511
|
+
* @param n - Maximum number of rows
|
|
6512
|
+
* @returns Updated query context with LIMIT clause
|
|
6513
|
+
*/
|
|
6514
|
+
limit(context, n) {
|
|
6515
|
+
const astService = this.createAstService(context.state);
|
|
6516
|
+
const nextState = astService.withLimit(n);
|
|
6517
|
+
return { state: nextState, hydration: context.hydration };
|
|
6518
|
+
}
|
|
6519
|
+
/**
|
|
6520
|
+
* Adds an OFFSET clause to the query
|
|
6521
|
+
* @param context - Current query context
|
|
6522
|
+
* @param n - Number of rows to skip
|
|
6523
|
+
* @returns Updated query context with OFFSET clause
|
|
6524
|
+
*/
|
|
6525
|
+
offset(context, n) {
|
|
6526
|
+
const astService = this.createAstService(context.state);
|
|
6527
|
+
const nextState = astService.withOffset(n);
|
|
6528
|
+
return { state: nextState, hydration: context.hydration };
|
|
6529
|
+
}
|
|
6530
|
+
};
|
|
6531
|
+
|
|
6532
|
+
// src/query-builder/select/cte-facet.ts
|
|
6533
|
+
var SelectCTEFacet = class {
|
|
6534
|
+
/**
|
|
6535
|
+
* Creates a new SelectCTEFacet instance
|
|
6536
|
+
* @param env - Query builder environment
|
|
6537
|
+
* @param createAstService - Function to create AST service
|
|
6538
|
+
*/
|
|
6539
|
+
constructor(env, createAstService) {
|
|
6540
|
+
this.env = env;
|
|
6541
|
+
this.createAstService = createAstService;
|
|
6542
|
+
}
|
|
6543
|
+
/**
|
|
6544
|
+
* Adds a Common Table Expression to the query
|
|
6545
|
+
* @param context - Current query context
|
|
6546
|
+
* @param name - CTE name
|
|
6547
|
+
* @param subAst - CTE query AST
|
|
6548
|
+
* @param columns - Optional column names
|
|
6549
|
+
* @param recursive - Whether the CTE is recursive
|
|
6550
|
+
* @returns Updated query context with CTE
|
|
6551
|
+
*/
|
|
6552
|
+
withCTE(context, name, subAst, columns, recursive) {
|
|
6553
|
+
const astService = this.createAstService(context.state);
|
|
6554
|
+
const nextState = astService.withCte(name, subAst, columns, recursive);
|
|
6555
|
+
return { state: nextState, hydration: context.hydration };
|
|
6556
|
+
}
|
|
6557
|
+
};
|
|
6558
|
+
|
|
6559
|
+
// src/query-builder/select/setop-facet.ts
|
|
6560
|
+
var SelectSetOpFacet = class {
|
|
6561
|
+
/**
|
|
6562
|
+
* Creates a new SelectSetOpFacet instance
|
|
6563
|
+
* @param env - Query builder environment
|
|
6564
|
+
* @param createAstService - Function to create AST service
|
|
6565
|
+
*/
|
|
6566
|
+
constructor(env, createAstService) {
|
|
6567
|
+
this.env = env;
|
|
6568
|
+
this.createAstService = createAstService;
|
|
6569
|
+
}
|
|
6570
|
+
/**
|
|
6571
|
+
* Applies a set operation to the query
|
|
6572
|
+
* @param context - Current query context
|
|
6573
|
+
* @param operator - Set operation kind
|
|
6574
|
+
* @param subAst - Subquery AST to combine
|
|
6575
|
+
* @returns Updated query context with set operation
|
|
6576
|
+
*/
|
|
6577
|
+
applySetOperation(context, operator, subAst) {
|
|
6578
|
+
const astService = this.createAstService(context.state);
|
|
6579
|
+
const nextState = astService.withSetOperation(operator, subAst);
|
|
6580
|
+
return { state: nextState, hydration: context.hydration };
|
|
6581
|
+
}
|
|
6582
|
+
};
|
|
6583
|
+
|
|
6584
|
+
// src/query-builder/select/relation-facet.ts
|
|
6585
|
+
var SelectRelationFacet = class {
|
|
6586
|
+
/**
|
|
6587
|
+
* Creates a new SelectRelationFacet instance
|
|
6588
|
+
* @param relationManager - Relation manager dependency
|
|
6589
|
+
*/
|
|
6590
|
+
constructor(relationManager) {
|
|
6591
|
+
this.relationManager = relationManager;
|
|
6592
|
+
}
|
|
6593
|
+
/**
|
|
6594
|
+
* Matches records based on a relationship
|
|
6595
|
+
* @param context - Current query context
|
|
6596
|
+
* @param relationName - Name of the relationship
|
|
6597
|
+
* @param predicate - Optional predicate
|
|
6598
|
+
* @returns Updated query context with relation match
|
|
6599
|
+
*/
|
|
6600
|
+
match(context, relationName, predicate) {
|
|
6601
|
+
return this.relationManager.match(context, relationName, predicate);
|
|
6602
|
+
}
|
|
6603
|
+
/**
|
|
6604
|
+
* Joins a related table
|
|
6605
|
+
* @param context - Current query context
|
|
6606
|
+
* @param relationName - Name of the relationship
|
|
6607
|
+
* @param joinKind - Type of join
|
|
6608
|
+
* @param extraCondition - Optional additional condition
|
|
6609
|
+
* @returns Updated query context with relation join
|
|
6610
|
+
*/
|
|
6611
|
+
joinRelation(context, relationName, joinKind, extraCondition) {
|
|
6612
|
+
return this.relationManager.joinRelation(context, relationName, joinKind, extraCondition);
|
|
6613
|
+
}
|
|
6614
|
+
/**
|
|
6615
|
+
* Includes related data in the query results
|
|
6616
|
+
* @param context - Current query context
|
|
6617
|
+
* @param relationName - Name of the relationship to include
|
|
6618
|
+
* @param options - Optional include options
|
|
6619
|
+
* @returns Updated query context with relation inclusion
|
|
6620
|
+
*/
|
|
6621
|
+
include(context, relationName, options) {
|
|
6622
|
+
return this.relationManager.include(context, relationName, options);
|
|
6623
|
+
}
|
|
6624
|
+
/**
|
|
6625
|
+
* Applies correlation for relation-based subqueries
|
|
6626
|
+
* @param context - Current query context
|
|
6627
|
+
* @param relationName - Name of the relationship
|
|
6628
|
+
* @param subAst - Subquery AST
|
|
6629
|
+
* @param extraCorrelate - Optional additional correlation
|
|
6630
|
+
* @returns Modified subquery AST with correlation
|
|
6631
|
+
*/
|
|
6632
|
+
applyRelationCorrelation(context, relationName, subAst, extraCorrelate) {
|
|
6633
|
+
return this.relationManager.applyRelationCorrelation(context, relationName, subAst, extraCorrelate);
|
|
6634
|
+
}
|
|
6635
|
+
};
|
|
6148
6636
|
|
|
6149
6637
|
// src/query-builder/select.ts
|
|
6150
6638
|
var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
6639
|
+
env;
|
|
6640
|
+
context;
|
|
6641
|
+
columnSelector;
|
|
6642
|
+
fromFacet;
|
|
6643
|
+
joinFacet;
|
|
6644
|
+
projectionFacet;
|
|
6645
|
+
predicateFacet;
|
|
6646
|
+
cteFacet;
|
|
6647
|
+
setOpFacet;
|
|
6648
|
+
relationFacet;
|
|
6649
|
+
lazyRelations;
|
|
6650
|
+
lazyRelationOptions;
|
|
6151
6651
|
/**
|
|
6152
6652
|
* Creates a new SelectQueryBuilder instance
|
|
6153
6653
|
* @param table - Table definition to query
|
|
@@ -6158,6 +6658,7 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6158
6658
|
constructor(table, state, hydration, dependencies, lazyRelations, lazyRelationOptions) {
|
|
6159
6659
|
const deps = resolveSelectQueryBuilderDependencies(dependencies);
|
|
6160
6660
|
this.env = { table, deps };
|
|
6661
|
+
const createAstService = (nextState) => deps.createQueryAstService(table, nextState);
|
|
6161
6662
|
const initialState = state ?? deps.createState(table);
|
|
6162
6663
|
const initialHydration = hydration ?? deps.createHydration(table);
|
|
6163
6664
|
this.context = {
|
|
@@ -6167,7 +6668,14 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6167
6668
|
this.lazyRelations = new Set(lazyRelations ?? []);
|
|
6168
6669
|
this.lazyRelationOptions = new Map(lazyRelationOptions ?? []);
|
|
6169
6670
|
this.columnSelector = deps.createColumnSelector(this.env);
|
|
6170
|
-
|
|
6671
|
+
const relationManager = deps.createRelationManager(this.env);
|
|
6672
|
+
this.fromFacet = new SelectFromFacet(this.env, createAstService);
|
|
6673
|
+
this.joinFacet = new SelectJoinFacet(this.env, createAstService);
|
|
6674
|
+
this.projectionFacet = new SelectProjectionFacet(this.columnSelector);
|
|
6675
|
+
this.predicateFacet = new SelectPredicateFacet(this.env, createAstService);
|
|
6676
|
+
this.cteFacet = new SelectCTEFacet(this.env, createAstService);
|
|
6677
|
+
this.setOpFacet = new SelectSetOpFacet(this.env, createAstService);
|
|
6678
|
+
this.relationFacet = new SelectRelationFacet(relationManager);
|
|
6171
6679
|
}
|
|
6172
6680
|
/**
|
|
6173
6681
|
* Creates a new SelectQueryBuilder instance with updated context and lazy relations
|
|
@@ -6188,14 +6696,11 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6188
6696
|
/**
|
|
6189
6697
|
* Applies an alias to the root FROM table.
|
|
6190
6698
|
* @param alias - Alias to apply
|
|
6699
|
+
* @example
|
|
6700
|
+
* const qb = new SelectQueryBuilder(userTable).as('u');
|
|
6191
6701
|
*/
|
|
6192
6702
|
as(alias) {
|
|
6193
|
-
const
|
|
6194
|
-
if (from.type !== "Table") {
|
|
6195
|
-
throw new Error("Cannot alias non-table FROM sources");
|
|
6196
|
-
}
|
|
6197
|
-
const nextFrom = { ...from, alias };
|
|
6198
|
-
const nextContext = this.applyAst(this.context, (service) => service.withFrom(nextFrom));
|
|
6703
|
+
const nextContext = this.fromFacet.as(this.context, alias);
|
|
6199
6704
|
return this.clone(nextContext);
|
|
6200
6705
|
}
|
|
6201
6706
|
/**
|
|
@@ -6220,29 +6725,6 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6220
6725
|
createChildBuilder(table) {
|
|
6221
6726
|
return new _SelectQueryBuilder(table, void 0, void 0, this.env.deps);
|
|
6222
6727
|
}
|
|
6223
|
-
/**
|
|
6224
|
-
* Applies an AST mutation using the query AST service
|
|
6225
|
-
* @param context - Current query context
|
|
6226
|
-
* @param mutator - Function that mutates the AST
|
|
6227
|
-
* @returns Updated query context
|
|
6228
|
-
*/
|
|
6229
|
-
applyAst(context, mutator) {
|
|
6230
|
-
const astService = this.env.deps.createQueryAstService(this.env.table, context.state);
|
|
6231
|
-
const nextState = mutator(astService);
|
|
6232
|
-
return { state: nextState, hydration: context.hydration };
|
|
6233
|
-
}
|
|
6234
|
-
/**
|
|
6235
|
-
* Applies a join to the query context
|
|
6236
|
-
* @param context - Current query context
|
|
6237
|
-
* @param table - Table to join
|
|
6238
|
-
* @param condition - Join condition
|
|
6239
|
-
* @param kind - Join kind
|
|
6240
|
-
* @returns Updated query context with join applied
|
|
6241
|
-
*/
|
|
6242
|
-
applyJoin(context, table, condition, kind) {
|
|
6243
|
-
const joinNode = createJoinNode(kind, { type: "Table", name: table.name, schema: table.schema }, condition);
|
|
6244
|
-
return this.applyAst(context, (service) => service.withJoin(joinNode));
|
|
6245
|
-
}
|
|
6246
6728
|
/**
|
|
6247
6729
|
* Applies a set operation to the query
|
|
6248
6730
|
* @param operator - Set operation kind
|
|
@@ -6251,12 +6733,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6251
6733
|
*/
|
|
6252
6734
|
applySetOperation(operator, query) {
|
|
6253
6735
|
const subAst = resolveSelectQuery(query);
|
|
6254
|
-
return this.
|
|
6736
|
+
return this.setOpFacet.applySetOperation(this.context, operator, subAst);
|
|
6255
6737
|
}
|
|
6256
6738
|
select(...args) {
|
|
6257
6739
|
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && typeof args[0] !== "string") {
|
|
6258
6740
|
const columns = args[0];
|
|
6259
|
-
return this.clone(this.
|
|
6741
|
+
return this.clone(this.projectionFacet.select(this.context, columns));
|
|
6260
6742
|
}
|
|
6261
6743
|
const cols = args;
|
|
6262
6744
|
const selection = {};
|
|
@@ -6267,15 +6749,17 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6267
6749
|
}
|
|
6268
6750
|
selection[key] = col2;
|
|
6269
6751
|
}
|
|
6270
|
-
return this.clone(this.
|
|
6752
|
+
return this.clone(this.projectionFacet.select(this.context, selection));
|
|
6271
6753
|
}
|
|
6272
6754
|
/**
|
|
6273
6755
|
* Selects raw column expressions
|
|
6274
6756
|
* @param cols - Column expressions as strings
|
|
6275
6757
|
* @returns New query builder instance with raw column selections
|
|
6758
|
+
* @example
|
|
6759
|
+
* qb.selectRaw('COUNT(*) as total', 'UPPER(name) as upper_name');
|
|
6276
6760
|
*/
|
|
6277
6761
|
selectRaw(...cols) {
|
|
6278
|
-
return this.clone(this.
|
|
6762
|
+
return this.clone(this.projectionFacet.selectRaw(this.context, cols));
|
|
6279
6763
|
}
|
|
6280
6764
|
/**
|
|
6281
6765
|
* Adds a Common Table Expression (CTE) to the query
|
|
@@ -6283,10 +6767,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6283
6767
|
* @param query - Query builder or query node for the CTE
|
|
6284
6768
|
* @param columns - Optional column names for the CTE
|
|
6285
6769
|
* @returns New query builder instance with the CTE
|
|
6770
|
+
* @example
|
|
6771
|
+
* const recentUsers = new SelectQueryBuilder(userTable)
|
|
6772
|
+
* .where(gt(userTable.columns.createdAt, subDays(now(), 30)));
|
|
6773
|
+
* const qb = new SelectQueryBuilder(userTable)
|
|
6774
|
+
* .with('recent_users', recentUsers)
|
|
6775
|
+
* .from('recent_users');
|
|
6286
6776
|
*/
|
|
6287
6777
|
with(name, query, columns) {
|
|
6288
6778
|
const subAst = resolveSelectQuery(query);
|
|
6289
|
-
const nextContext = this.
|
|
6779
|
+
const nextContext = this.cteFacet.withCTE(this.context, name, subAst, columns, false);
|
|
6290
6780
|
return this.clone(nextContext);
|
|
6291
6781
|
}
|
|
6292
6782
|
/**
|
|
@@ -6295,10 +6785,23 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6295
6785
|
* @param query - Query builder or query node for the CTE
|
|
6296
6786
|
* @param columns - Optional column names for the CTE
|
|
6297
6787
|
* @returns New query builder instance with the recursive CTE
|
|
6788
|
+
* @example
|
|
6789
|
+
* // Base case: select root nodes
|
|
6790
|
+
* const baseQuery = new SelectQueryBuilder(orgTable)
|
|
6791
|
+
* .where(eq(orgTable.columns.parentId, 1));
|
|
6792
|
+
* // Recursive case: join with the CTE itself
|
|
6793
|
+
* const recursiveQuery = new SelectQueryBuilder(orgTable)
|
|
6794
|
+
* .join('org_hierarchy', 'oh', eq(orgTable.columns.parentId, col('oh.id')));
|
|
6795
|
+
* // Combine base and recursive parts
|
|
6796
|
+
* const orgHierarchy = baseQuery.union(recursiveQuery);
|
|
6797
|
+
* // Use in main query
|
|
6798
|
+
* const qb = new SelectQueryBuilder(orgTable)
|
|
6799
|
+
* .withRecursive('org_hierarchy', orgHierarchy)
|
|
6800
|
+
* .from('org_hierarchy');
|
|
6298
6801
|
*/
|
|
6299
6802
|
withRecursive(name, query, columns) {
|
|
6300
6803
|
const subAst = resolveSelectQuery(query);
|
|
6301
|
-
const nextContext = this.
|
|
6804
|
+
const nextContext = this.cteFacet.withCTE(this.context, name, subAst, columns, true);
|
|
6302
6805
|
return this.clone(nextContext);
|
|
6303
6806
|
}
|
|
6304
6807
|
/**
|
|
@@ -6307,11 +6810,15 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6307
6810
|
* @param alias - Alias for the derived table
|
|
6308
6811
|
* @param columnAliases - Optional column alias list
|
|
6309
6812
|
* @returns New query builder instance with updated FROM
|
|
6813
|
+
* @example
|
|
6814
|
+
* const subquery = new SelectQueryBuilder(userTable)
|
|
6815
|
+
* .select('id', 'name')
|
|
6816
|
+
* .where(gt(userTable.columns.score, 100));
|
|
6817
|
+
* qb.fromSubquery(subquery, 'high_scorers', ['userId', 'userName']);
|
|
6310
6818
|
*/
|
|
6311
6819
|
fromSubquery(subquery, alias, columnAliases) {
|
|
6312
6820
|
const subAst = resolveSelectQuery(subquery);
|
|
6313
|
-
const
|
|
6314
|
-
const nextContext = this.applyAst(this.context, (service) => service.withFrom(fromNode));
|
|
6821
|
+
const nextContext = this.fromFacet.fromSubquery(this.context, subAst, alias, columnAliases);
|
|
6315
6822
|
return this.clone(nextContext);
|
|
6316
6823
|
}
|
|
6317
6824
|
/**
|
|
@@ -6320,10 +6827,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6320
6827
|
* @param args - Optional function arguments
|
|
6321
6828
|
* @param alias - Optional alias for the function table
|
|
6322
6829
|
* @param options - Optional function-table metadata (lateral, ordinality, column aliases, schema)
|
|
6830
|
+
* @example
|
|
6831
|
+
* qb.fromFunctionTable(
|
|
6832
|
+
* 'generate_series',
|
|
6833
|
+
* [literal(1), literal(10), literal(1)],
|
|
6834
|
+
* 'series',
|
|
6835
|
+
* { columnAliases: ['value'] }
|
|
6836
|
+
* );
|
|
6323
6837
|
*/
|
|
6324
6838
|
fromFunctionTable(name, args = [], alias, options) {
|
|
6325
|
-
const
|
|
6326
|
-
const nextContext = this.applyAst(this.context, (service) => service.withFrom(functionTable));
|
|
6839
|
+
const nextContext = this.fromFacet.fromFunctionTable(this.context, name, args, alias, options);
|
|
6327
6840
|
return this.clone(nextContext);
|
|
6328
6841
|
}
|
|
6329
6842
|
/**
|
|
@@ -6331,10 +6844,16 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6331
6844
|
* @param alias - Alias for the subquery column
|
|
6332
6845
|
* @param sub - Query builder or query node for the subquery
|
|
6333
6846
|
* @returns New query builder instance with the subquery selection
|
|
6847
|
+
* @example
|
|
6848
|
+
* const postCount = new SelectQueryBuilder(postTable)
|
|
6849
|
+
* .select(count(postTable.columns.id))
|
|
6850
|
+
* .where(eq(postTable.columns.userId, col('u.id')));
|
|
6851
|
+
* qb.select('id', 'name')
|
|
6852
|
+
* .selectSubquery('postCount', postCount);
|
|
6334
6853
|
*/
|
|
6335
6854
|
selectSubquery(alias, sub2) {
|
|
6336
6855
|
const query = resolveSelectQuery(sub2);
|
|
6337
|
-
return this.clone(this.
|
|
6856
|
+
return this.clone(this.projectionFacet.selectSubquery(this.context, alias, query));
|
|
6338
6857
|
}
|
|
6339
6858
|
/**
|
|
6340
6859
|
* Adds a JOIN against a derived table (subquery with alias)
|
|
@@ -6344,11 +6863,19 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6344
6863
|
* @param joinKind - Join kind (defaults to INNER)
|
|
6345
6864
|
* @param columnAliases - Optional column alias list for the derived table
|
|
6346
6865
|
* @returns New query builder instance with the derived-table join
|
|
6866
|
+
* @example
|
|
6867
|
+
* const activeUsers = new SelectQueryBuilder(userTable)
|
|
6868
|
+
* .where(eq(userTable.columns.active, true));
|
|
6869
|
+
* qb.joinSubquery(
|
|
6870
|
+
* activeUsers,
|
|
6871
|
+
* 'au',
|
|
6872
|
+
* eq(col('t.userId'), col('au.id')),
|
|
6873
|
+
* JOIN_KINDS.LEFT
|
|
6874
|
+
* );
|
|
6347
6875
|
*/
|
|
6348
6876
|
joinSubquery(subquery, alias, condition, joinKind = JOIN_KINDS.INNER, columnAliases) {
|
|
6349
6877
|
const subAst = resolveSelectQuery(subquery);
|
|
6350
|
-
const
|
|
6351
|
-
const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
|
|
6878
|
+
const nextContext = this.joinFacet.joinSubquery(this.context, subAst, alias, condition, joinKind, columnAliases);
|
|
6352
6879
|
return this.clone(nextContext);
|
|
6353
6880
|
}
|
|
6354
6881
|
/**
|
|
@@ -6359,11 +6886,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6359
6886
|
* @param condition - Join condition expression
|
|
6360
6887
|
* @param joinKind - Kind of join (defaults to INNER)
|
|
6361
6888
|
* @param options - Optional metadata (lateral, ordinality, column aliases, schema)
|
|
6889
|
+
* @example
|
|
6890
|
+
* qb.joinFunctionTable(
|
|
6891
|
+
* 'generate_series',
|
|
6892
|
+
* [literal(1), literal(10)],
|
|
6893
|
+
* 'gs',
|
|
6894
|
+
* eq(col('t.value'), col('gs.value')),
|
|
6895
|
+
* JOIN_KINDS.INNER,
|
|
6896
|
+
* { columnAliases: ['value'] }
|
|
6897
|
+
* );
|
|
6362
6898
|
*/
|
|
6363
6899
|
joinFunctionTable(name, args = [], alias, condition, joinKind = JOIN_KINDS.INNER, options) {
|
|
6364
|
-
const
|
|
6365
|
-
const joinNode = createJoinNode(joinKind, functionTable, condition);
|
|
6366
|
-
const nextContext = this.applyAst(this.context, (service) => service.withJoin(joinNode));
|
|
6900
|
+
const nextContext = this.joinFacet.joinFunctionTable(this.context, name, args, alias, condition, joinKind, options);
|
|
6367
6901
|
return this.clone(nextContext);
|
|
6368
6902
|
}
|
|
6369
6903
|
/**
|
|
@@ -6371,9 +6905,14 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6371
6905
|
* @param table - Table to join
|
|
6372
6906
|
* @param condition - Join condition expression
|
|
6373
6907
|
* @returns New query builder instance with the INNER JOIN
|
|
6908
|
+
* @example
|
|
6909
|
+
* qb.innerJoin(
|
|
6910
|
+
* postTable,
|
|
6911
|
+
* eq(userTable.columns.id, postTable.columns.userId)
|
|
6912
|
+
* );
|
|
6374
6913
|
*/
|
|
6375
6914
|
innerJoin(table, condition) {
|
|
6376
|
-
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
|
|
6915
|
+
const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.INNER);
|
|
6377
6916
|
return this.clone(nextContext);
|
|
6378
6917
|
}
|
|
6379
6918
|
/**
|
|
@@ -6381,9 +6920,14 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6381
6920
|
* @param table - Table to join
|
|
6382
6921
|
* @param condition - Join condition expression
|
|
6383
6922
|
* @returns New query builder instance with the LEFT JOIN
|
|
6923
|
+
* @example
|
|
6924
|
+
* qb.leftJoin(
|
|
6925
|
+
* postTable,
|
|
6926
|
+
* eq(userTable.columns.id, postTable.columns.userId)
|
|
6927
|
+
* );
|
|
6384
6928
|
*/
|
|
6385
6929
|
leftJoin(table, condition) {
|
|
6386
|
-
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
|
|
6930
|
+
const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.LEFT);
|
|
6387
6931
|
return this.clone(nextContext);
|
|
6388
6932
|
}
|
|
6389
6933
|
/**
|
|
@@ -6391,9 +6935,14 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6391
6935
|
* @param table - Table to join
|
|
6392
6936
|
* @param condition - Join condition expression
|
|
6393
6937
|
* @returns New query builder instance with the RIGHT JOIN
|
|
6938
|
+
* @example
|
|
6939
|
+
* qb.rightJoin(
|
|
6940
|
+
* postTable,
|
|
6941
|
+
* eq(userTable.columns.id, postTable.columns.userId)
|
|
6942
|
+
* );
|
|
6394
6943
|
*/
|
|
6395
6944
|
rightJoin(table, condition) {
|
|
6396
|
-
const nextContext = this.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
|
|
6945
|
+
const nextContext = this.joinFacet.applyJoin(this.context, table, condition, JOIN_KINDS.RIGHT);
|
|
6397
6946
|
return this.clone(nextContext);
|
|
6398
6947
|
}
|
|
6399
6948
|
/**
|
|
@@ -6401,9 +6950,11 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6401
6950
|
* @param relationName - Name of the relationship to match
|
|
6402
6951
|
* @param predicate - Optional predicate expression
|
|
6403
6952
|
* @returns New query builder instance with the relationship match
|
|
6953
|
+
* @example
|
|
6954
|
+
* qb.match('posts', eq(postTable.columns.published, true));
|
|
6404
6955
|
*/
|
|
6405
6956
|
match(relationName, predicate) {
|
|
6406
|
-
const nextContext = this.
|
|
6957
|
+
const nextContext = this.relationFacet.match(this.context, relationName, predicate);
|
|
6407
6958
|
return this.clone(nextContext);
|
|
6408
6959
|
}
|
|
6409
6960
|
/**
|
|
@@ -6412,9 +6963,13 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6412
6963
|
* @param joinKind - Type of join (defaults to INNER)
|
|
6413
6964
|
* @param extraCondition - Optional additional join condition
|
|
6414
6965
|
* @returns New query builder instance with the relationship join
|
|
6966
|
+
* @example
|
|
6967
|
+
* qb.joinRelation('posts', JOIN_KINDS.LEFT);
|
|
6968
|
+
* @example
|
|
6969
|
+
* qb.joinRelation('posts', JOIN_KINDS.INNER, eq(postTable.columns.published, true));
|
|
6415
6970
|
*/
|
|
6416
6971
|
joinRelation(relationName, joinKind = JOIN_KINDS.INNER, extraCondition) {
|
|
6417
|
-
const nextContext = this.
|
|
6972
|
+
const nextContext = this.relationFacet.joinRelation(this.context, relationName, joinKind, extraCondition);
|
|
6418
6973
|
return this.clone(nextContext);
|
|
6419
6974
|
}
|
|
6420
6975
|
/**
|
|
@@ -6422,9 +6977,18 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6422
6977
|
* @param relationName - Name of the relationship to include
|
|
6423
6978
|
* @param options - Optional include options
|
|
6424
6979
|
* @returns New query builder instance with the relationship inclusion
|
|
6980
|
+
* @example
|
|
6981
|
+
* qb.include('posts');
|
|
6982
|
+
* @example
|
|
6983
|
+
* qb.include('posts', { columns: ['id', 'title', 'published'] });
|
|
6984
|
+
* @example
|
|
6985
|
+
* qb.include('posts', {
|
|
6986
|
+
* columns: ['id', 'title'],
|
|
6987
|
+
* where: eq(postTable.columns.published, true)
|
|
6988
|
+
* });
|
|
6425
6989
|
*/
|
|
6426
6990
|
include(relationName, options) {
|
|
6427
|
-
const nextContext = this.
|
|
6991
|
+
const nextContext = this.relationFacet.include(this.context, relationName, options);
|
|
6428
6992
|
return this.clone(nextContext);
|
|
6429
6993
|
}
|
|
6430
6994
|
/**
|
|
@@ -6432,6 +6996,11 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6432
6996
|
* @param relationName - Name of the relation to include lazily
|
|
6433
6997
|
* @param options - Optional include options for lazy loading
|
|
6434
6998
|
* @returns New query builder instance with lazy relation inclusion
|
|
6999
|
+
* @example
|
|
7000
|
+
* const qb = new SelectQueryBuilder(userTable).includeLazy('posts');
|
|
7001
|
+
* const users = await qb.execute(session);
|
|
7002
|
+
* // Access posts later - they will be loaded on demand
|
|
7003
|
+
* const posts = await users[0].posts;
|
|
6435
7004
|
*/
|
|
6436
7005
|
includeLazy(relationName, options) {
|
|
6437
7006
|
let nextContext = this.context;
|
|
@@ -6461,14 +7030,22 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6461
7030
|
}
|
|
6462
7031
|
/**
|
|
6463
7032
|
* Convenience alias for including only specific columns from a relation.
|
|
7033
|
+
* @example
|
|
7034
|
+
* qb.includePick('posts', ['id', 'title', 'createdAt']);
|
|
6464
7035
|
*/
|
|
6465
7036
|
includePick(relationName, cols) {
|
|
6466
|
-
|
|
7037
|
+
const options = { columns: cols };
|
|
7038
|
+
return this.include(relationName, options);
|
|
6467
7039
|
}
|
|
6468
7040
|
/**
|
|
6469
7041
|
* Selects columns for the root table and relations from an array of entries
|
|
6470
7042
|
* @param config - Configuration array for deep column selection
|
|
6471
7043
|
* @returns New query builder instance with deep column selections
|
|
7044
|
+
* @example
|
|
7045
|
+
* qb.selectColumnsDeep([
|
|
7046
|
+
* { type: 'root', columns: ['id', 'name'] },
|
|
7047
|
+
* { type: 'relation', relationName: 'posts', columns: ['id', 'title'] }
|
|
7048
|
+
* ]);
|
|
6472
7049
|
*/
|
|
6473
7050
|
selectColumnsDeep(config) {
|
|
6474
7051
|
let currBuilder = this;
|
|
@@ -6476,7 +7053,8 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6476
7053
|
if (entry.type === "root") {
|
|
6477
7054
|
currBuilder = currBuilder.select(...entry.columns);
|
|
6478
7055
|
} else {
|
|
6479
|
-
|
|
7056
|
+
const options = { columns: entry.columns };
|
|
7057
|
+
currBuilder = currBuilder.include(entry.relationName, options);
|
|
6480
7058
|
}
|
|
6481
7059
|
}
|
|
6482
7060
|
return currBuilder;
|
|
@@ -6506,61 +7084,41 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6506
7084
|
* Executes the query and returns hydrated results
|
|
6507
7085
|
* @param ctx - ORM session context
|
|
6508
7086
|
* @returns Promise of entity instances
|
|
7087
|
+
* @example
|
|
7088
|
+
* const users = await qb.select('id', 'name')
|
|
7089
|
+
* .where(eq(userTable.columns.active, true))
|
|
7090
|
+
* .execute(session);
|
|
6509
7091
|
*/
|
|
6510
7092
|
async execute(ctx) {
|
|
6511
7093
|
return executeHydrated(ctx, this);
|
|
6512
7094
|
}
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
return this.clone(nextContext);
|
|
6520
|
-
}
|
|
7095
|
+
/**
|
|
7096
|
+
* Executes a count query for the current builder without LIMIT/OFFSET clauses.
|
|
7097
|
+
*
|
|
7098
|
+
* @example
|
|
7099
|
+
* const total = await qb.count(session);
|
|
7100
|
+
*/
|
|
6521
7101
|
async count(session) {
|
|
6522
|
-
|
|
6523
|
-
...this.context.state.ast,
|
|
6524
|
-
orderBy: void 0,
|
|
6525
|
-
limit: void 0,
|
|
6526
|
-
offset: void 0
|
|
6527
|
-
};
|
|
6528
|
-
const subAst = this.withAst(unpagedAst).getAST();
|
|
6529
|
-
const countQuery = {
|
|
6530
|
-
type: "SelectQuery",
|
|
6531
|
-
from: derivedTable(subAst, "__metal_count"),
|
|
6532
|
-
columns: [{ type: "Function", name: "COUNT", args: [], alias: "total" }],
|
|
6533
|
-
joins: []
|
|
6534
|
-
};
|
|
6535
|
-
const execCtx = session.getExecutionContext();
|
|
6536
|
-
const compiled = execCtx.dialect.compileSelect(countQuery);
|
|
6537
|
-
const results = await execCtx.interceptors.run({ sql: compiled.sql, params: compiled.params }, execCtx.executor);
|
|
6538
|
-
const value = results[0]?.values?.[0]?.[0];
|
|
6539
|
-
if (typeof value === "number") return value;
|
|
6540
|
-
if (typeof value === "bigint") return Number(value);
|
|
6541
|
-
if (typeof value === "string") return Number(value);
|
|
6542
|
-
return value === null || value === void 0 ? 0 : Number(value);
|
|
7102
|
+
return executeCount(this.context, this.env, session);
|
|
6543
7103
|
}
|
|
7104
|
+
/**
|
|
7105
|
+
* Executes the query and returns both the paged items and the total.
|
|
7106
|
+
*
|
|
7107
|
+
* @example
|
|
7108
|
+
* const { items, totalItems } = await qb.executePaged(session, { page: 1, pageSize: 20 });
|
|
7109
|
+
*/
|
|
6544
7110
|
async executePaged(session, options) {
|
|
6545
|
-
|
|
6546
|
-
if (!Number.isInteger(page) || page < 1) {
|
|
6547
|
-
throw new Error("executePaged: page must be an integer >= 1");
|
|
6548
|
-
}
|
|
6549
|
-
if (!Number.isInteger(pageSize) || pageSize < 1) {
|
|
6550
|
-
throw new Error("executePaged: pageSize must be an integer >= 1");
|
|
6551
|
-
}
|
|
6552
|
-
const offset = (page - 1) * pageSize;
|
|
6553
|
-
const [items, totalItems] = await Promise.all([
|
|
6554
|
-
this.limit(pageSize).offset(offset).execute(session),
|
|
6555
|
-
this.count(session)
|
|
6556
|
-
]);
|
|
6557
|
-
return { items, totalItems };
|
|
7111
|
+
return executePagedQuery(this, session, options, (sess) => this.count(sess));
|
|
6558
7112
|
}
|
|
6559
7113
|
/**
|
|
6560
7114
|
* Executes the query with provided execution and hydration contexts
|
|
6561
7115
|
* @param execCtx - Execution context
|
|
6562
7116
|
* @param hydCtx - Hydration context
|
|
6563
7117
|
* @returns Promise of entity instances
|
|
7118
|
+
* @example
|
|
7119
|
+
* const execCtx = new ExecutionContext(session);
|
|
7120
|
+
* const hydCtx = new HydrationContext();
|
|
7121
|
+
* const users = await qb.executeWithContexts(execCtx, hydCtx);
|
|
6564
7122
|
*/
|
|
6565
7123
|
async executeWithContexts(execCtx, hydCtx) {
|
|
6566
7124
|
return executeHydratedWithContexts(execCtx, hydCtx, this);
|
|
@@ -6569,27 +7127,41 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6569
7127
|
* Adds a WHERE condition to the query
|
|
6570
7128
|
* @param expr - Expression for the WHERE clause
|
|
6571
7129
|
* @returns New query builder instance with the WHERE condition
|
|
7130
|
+
* @example
|
|
7131
|
+
* qb.where(eq(userTable.columns.id, 1));
|
|
7132
|
+
* @example
|
|
7133
|
+
* qb.where(and(
|
|
7134
|
+
* eq(userTable.columns.active, true),
|
|
7135
|
+
* gt(userTable.columns.createdAt, subDays(now(), 30))
|
|
7136
|
+
* ));
|
|
6572
7137
|
*/
|
|
6573
7138
|
where(expr) {
|
|
6574
|
-
const nextContext = this.
|
|
7139
|
+
const nextContext = this.predicateFacet.where(this.context, expr);
|
|
6575
7140
|
return this.clone(nextContext);
|
|
6576
7141
|
}
|
|
6577
7142
|
/**
|
|
6578
7143
|
* Adds a GROUP BY clause to the query
|
|
6579
7144
|
* @param term - Column definition or ordering term to group by
|
|
6580
7145
|
* @returns New query builder instance with the GROUP BY clause
|
|
7146
|
+
* @example
|
|
7147
|
+
* qb.select('departmentId', count(userTable.columns.id))
|
|
7148
|
+
* .groupBy(userTable.columns.departmentId);
|
|
6581
7149
|
*/
|
|
6582
7150
|
groupBy(term) {
|
|
6583
|
-
const nextContext = this.
|
|
7151
|
+
const nextContext = this.predicateFacet.groupBy(this.context, term);
|
|
6584
7152
|
return this.clone(nextContext);
|
|
6585
7153
|
}
|
|
6586
7154
|
/**
|
|
6587
7155
|
* Adds a HAVING condition to the query
|
|
6588
7156
|
* @param expr - Expression for the HAVING clause
|
|
6589
7157
|
* @returns New query builder instance with the HAVING condition
|
|
7158
|
+
* @example
|
|
7159
|
+
* qb.select('departmentId', count(userTable.columns.id))
|
|
7160
|
+
* .groupBy(userTable.columns.departmentId)
|
|
7161
|
+
* .having(gt(count(userTable.columns.id), 5));
|
|
6590
7162
|
*/
|
|
6591
7163
|
having(expr) {
|
|
6592
|
-
const nextContext = this.
|
|
7164
|
+
const nextContext = this.predicateFacet.having(this.context, expr);
|
|
6593
7165
|
return this.clone(nextContext);
|
|
6594
7166
|
}
|
|
6595
7167
|
/**
|
|
@@ -6597,46 +7169,62 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6597
7169
|
* @param term - Column definition or ordering term to order by
|
|
6598
7170
|
* @param directionOrOptions - Order direction or options (defaults to ASC)
|
|
6599
7171
|
* @returns New query builder instance with the ORDER BY clause
|
|
7172
|
+
*
|
|
7173
|
+
* @example
|
|
7174
|
+
* qb.orderBy(userTable.columns.createdAt, 'DESC');
|
|
6600
7175
|
*/
|
|
6601
7176
|
orderBy(term, directionOrOptions = ORDER_DIRECTIONS.ASC) {
|
|
6602
|
-
const
|
|
6603
|
-
const dir = options.direction ?? ORDER_DIRECTIONS.ASC;
|
|
6604
|
-
const nextContext = this.applyAst(
|
|
6605
|
-
this.context,
|
|
6606
|
-
(service) => service.withOrderBy(term, dir, options.nulls, options.collation)
|
|
6607
|
-
);
|
|
7177
|
+
const nextContext = applyOrderBy(this.context, this.predicateFacet, term, directionOrOptions);
|
|
6608
7178
|
return this.clone(nextContext);
|
|
6609
7179
|
}
|
|
6610
7180
|
/**
|
|
6611
7181
|
* Adds a DISTINCT clause to the query
|
|
6612
7182
|
* @param cols - Columns to make distinct
|
|
6613
7183
|
* @returns New query builder instance with the DISTINCT clause
|
|
7184
|
+
* @example
|
|
7185
|
+
* qb.distinct(userTable.columns.email);
|
|
7186
|
+
* @example
|
|
7187
|
+
* qb.distinct(userTable.columns.firstName, userTable.columns.lastName);
|
|
6614
7188
|
*/
|
|
6615
7189
|
distinct(...cols) {
|
|
6616
|
-
return this.clone(this.
|
|
7190
|
+
return this.clone(this.projectionFacet.distinct(this.context, cols));
|
|
6617
7191
|
}
|
|
6618
7192
|
/**
|
|
6619
7193
|
* Adds a LIMIT clause to the query
|
|
6620
7194
|
* @param n - Maximum number of rows to return
|
|
6621
7195
|
* @returns New query builder instance with the LIMIT clause
|
|
7196
|
+
* @example
|
|
7197
|
+
* qb.limit(10);
|
|
7198
|
+
* @example
|
|
7199
|
+
* qb.limit(20).offset(40); // Pagination: page 3 with 20 items per page
|
|
6622
7200
|
*/
|
|
6623
7201
|
limit(n) {
|
|
6624
|
-
const nextContext = this.
|
|
7202
|
+
const nextContext = this.predicateFacet.limit(this.context, n);
|
|
6625
7203
|
return this.clone(nextContext);
|
|
6626
7204
|
}
|
|
6627
7205
|
/**
|
|
6628
7206
|
* Adds an OFFSET clause to the query
|
|
6629
7207
|
* @param n - Number of rows to skip
|
|
6630
7208
|
* @returns New query builder instance with the OFFSET clause
|
|
7209
|
+
* @example
|
|
7210
|
+
* qb.offset(10);
|
|
7211
|
+
* @example
|
|
7212
|
+
* qb.limit(20).offset(40); // Pagination: page 3 with 20 items per page
|
|
6631
7213
|
*/
|
|
6632
7214
|
offset(n) {
|
|
6633
|
-
const nextContext = this.
|
|
7215
|
+
const nextContext = this.predicateFacet.offset(this.context, n);
|
|
6634
7216
|
return this.clone(nextContext);
|
|
6635
7217
|
}
|
|
6636
7218
|
/**
|
|
6637
7219
|
* Combines this query with another using UNION
|
|
6638
7220
|
* @param query - Query to union with
|
|
6639
7221
|
* @returns New query builder instance with the set operation
|
|
7222
|
+
* @example
|
|
7223
|
+
* const activeUsers = new SelectQueryBuilder(userTable)
|
|
7224
|
+
* .where(eq(userTable.columns.active, true));
|
|
7225
|
+
* const inactiveUsers = new SelectQueryBuilder(userTable)
|
|
7226
|
+
* .where(eq(userTable.columns.active, false));
|
|
7227
|
+
* qb.union(activeUsers).union(inactiveUsers);
|
|
6640
7228
|
*/
|
|
6641
7229
|
union(query) {
|
|
6642
7230
|
return this.clone(this.applySetOperation("UNION", query));
|
|
@@ -6645,6 +7233,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6645
7233
|
* Combines this query with another using UNION ALL
|
|
6646
7234
|
* @param query - Query to union with
|
|
6647
7235
|
* @returns New query builder instance with the set operation
|
|
7236
|
+
* @example
|
|
7237
|
+
* const q1 = new SelectQueryBuilder(userTable).where(gt(userTable.columns.score, 80));
|
|
7238
|
+
* const q2 = new SelectQueryBuilder(userTable).where(lt(userTable.columns.score, 20));
|
|
7239
|
+
* qb.unionAll(q1).unionAll(q2);
|
|
6648
7240
|
*/
|
|
6649
7241
|
unionAll(query) {
|
|
6650
7242
|
return this.clone(this.applySetOperation("UNION ALL", query));
|
|
@@ -6653,6 +7245,12 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6653
7245
|
* Combines this query with another using INTERSECT
|
|
6654
7246
|
* @param query - Query to intersect with
|
|
6655
7247
|
* @returns New query builder instance with the set operation
|
|
7248
|
+
* @example
|
|
7249
|
+
* const activeUsers = new SelectQueryBuilder(userTable)
|
|
7250
|
+
* .where(eq(userTable.columns.active, true));
|
|
7251
|
+
* const premiumUsers = new SelectQueryBuilder(userTable)
|
|
7252
|
+
* .where(eq(userTable.columns.premium, true));
|
|
7253
|
+
* qb.intersect(activeUsers).intersect(premiumUsers);
|
|
6656
7254
|
*/
|
|
6657
7255
|
intersect(query) {
|
|
6658
7256
|
return this.clone(this.applySetOperation("INTERSECT", query));
|
|
@@ -6661,6 +7259,11 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6661
7259
|
* Combines this query with another using EXCEPT
|
|
6662
7260
|
* @param query - Query to subtract
|
|
6663
7261
|
* @returns New query builder instance with the set operation
|
|
7262
|
+
* @example
|
|
7263
|
+
* const allUsers = new SelectQueryBuilder(userTable);
|
|
7264
|
+
* const inactiveUsers = new SelectQueryBuilder(userTable)
|
|
7265
|
+
* .where(eq(userTable.columns.active, false));
|
|
7266
|
+
* qb.except(allUsers).except(inactiveUsers); // Only active users
|
|
6664
7267
|
*/
|
|
6665
7268
|
except(query) {
|
|
6666
7269
|
return this.clone(this.applySetOperation("EXCEPT", query));
|
|
@@ -6669,6 +7272,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6669
7272
|
* Adds a WHERE EXISTS condition to the query
|
|
6670
7273
|
* @param subquery - Subquery to check for existence
|
|
6671
7274
|
* @returns New query builder instance with the WHERE EXISTS condition
|
|
7275
|
+
* @example
|
|
7276
|
+
* const postsQuery = new SelectQueryBuilder(postTable)
|
|
7277
|
+
* .where(eq(postTable.columns.userId, col('u.id')));
|
|
7278
|
+
* qb.whereExists(postsQuery);
|
|
6672
7279
|
*/
|
|
6673
7280
|
whereExists(subquery, correlate) {
|
|
6674
7281
|
const subAst = resolveSelectQuery(subquery);
|
|
@@ -6679,6 +7286,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6679
7286
|
* Adds a WHERE NOT EXISTS condition to the query
|
|
6680
7287
|
* @param subquery - Subquery to check for non-existence
|
|
6681
7288
|
* @returns New query builder instance with the WHERE NOT EXISTS condition
|
|
7289
|
+
* @example
|
|
7290
|
+
* const postsQuery = new SelectQueryBuilder(postTable)
|
|
7291
|
+
* .where(eq(postTable.columns.userId, col('u.id')));
|
|
7292
|
+
* qb.whereNotExists(postsQuery); // Users without posts
|
|
6682
7293
|
*/
|
|
6683
7294
|
whereNotExists(subquery, correlate) {
|
|
6684
7295
|
const subAst = resolveSelectQuery(subquery);
|
|
@@ -6690,47 +7301,54 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6690
7301
|
* @param relationName - Name of the relationship to check
|
|
6691
7302
|
* @param callback - Optional callback to modify the relationship query
|
|
6692
7303
|
* @returns New query builder instance with the relationship existence check
|
|
7304
|
+
*
|
|
7305
|
+
* @example
|
|
7306
|
+
* qb.whereHas('posts', postQb => postQb.where(eq(postTable.columns.published, true)));
|
|
6693
7307
|
*/
|
|
6694
7308
|
whereHas(relationName, callbackOrOptions, maybeOptions) {
|
|
6695
|
-
const
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
6707
|
-
return this.where(exists(finalSubAst));
|
|
7309
|
+
const predicate = buildWhereHasPredicate(
|
|
7310
|
+
this.env,
|
|
7311
|
+
this.context,
|
|
7312
|
+
this.relationFacet,
|
|
7313
|
+
(table) => this.createChildBuilder(table),
|
|
7314
|
+
relationName,
|
|
7315
|
+
callbackOrOptions,
|
|
7316
|
+
maybeOptions,
|
|
7317
|
+
false
|
|
7318
|
+
);
|
|
7319
|
+
return this.where(predicate);
|
|
6708
7320
|
}
|
|
6709
7321
|
/**
|
|
6710
7322
|
* Adds a WHERE NOT EXISTS condition based on a relationship
|
|
6711
7323
|
* @param relationName - Name of the relationship to check
|
|
6712
7324
|
* @param callback - Optional callback to modify the relationship query
|
|
6713
7325
|
* @returns New query builder instance with the relationship non-existence check
|
|
7326
|
+
*
|
|
7327
|
+
* @example
|
|
7328
|
+
* qb.whereHasNot('posts', postQb => postQb.where(eq(postTable.columns.published, true)));
|
|
6714
7329
|
*/
|
|
6715
7330
|
whereHasNot(relationName, callbackOrOptions, maybeOptions) {
|
|
6716
|
-
const
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
const finalSubAst = this.relationManager.applyRelationCorrelation(this.context, relationName, subAst, options?.correlate);
|
|
6728
|
-
return this.where(notExists(finalSubAst));
|
|
7331
|
+
const predicate = buildWhereHasPredicate(
|
|
7332
|
+
this.env,
|
|
7333
|
+
this.context,
|
|
7334
|
+
this.relationFacet,
|
|
7335
|
+
(table) => this.createChildBuilder(table),
|
|
7336
|
+
relationName,
|
|
7337
|
+
callbackOrOptions,
|
|
7338
|
+
maybeOptions,
|
|
7339
|
+
true
|
|
7340
|
+
);
|
|
7341
|
+
return this.where(predicate);
|
|
6729
7342
|
}
|
|
6730
7343
|
/**
|
|
6731
7344
|
* Compiles the query to SQL for a specific dialect
|
|
6732
7345
|
* @param dialect - Database dialect to compile for
|
|
6733
7346
|
* @returns Compiled query with SQL and parameters
|
|
7347
|
+
* @example
|
|
7348
|
+
* const compiled = qb.select('id', 'name')
|
|
7349
|
+
* .where(eq(userTable.columns.active, true))
|
|
7350
|
+
* .compile('postgres');
|
|
7351
|
+
* console.log(compiled.sql); // SELECT "id", "name" FROM "users" WHERE "active" = true
|
|
6734
7352
|
*/
|
|
6735
7353
|
compile(dialect) {
|
|
6736
7354
|
const resolved = resolveDialectInput(dialect);
|
|
@@ -6740,6 +7358,11 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6740
7358
|
* Converts the query to SQL string for a specific dialect
|
|
6741
7359
|
* @param dialect - Database dialect to generate SQL for
|
|
6742
7360
|
* @returns SQL string representation of the query
|
|
7361
|
+
* @example
|
|
7362
|
+
* const sql = qb.select('id', 'name')
|
|
7363
|
+
* .where(eq(userTable.columns.active, true))
|
|
7364
|
+
* .toSql('postgres');
|
|
7365
|
+
* console.log(sql); // SELECT "id", "name" FROM "users" WHERE "active" = true
|
|
6743
7366
|
*/
|
|
6744
7367
|
toSql(dialect) {
|
|
6745
7368
|
return this.compile(dialect).sql;
|
|
@@ -6747,6 +7370,9 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6747
7370
|
/**
|
|
6748
7371
|
* Gets the hydration plan for the query
|
|
6749
7372
|
* @returns Hydration plan or undefined if none exists
|
|
7373
|
+
* @example
|
|
7374
|
+
* const plan = qb.include('posts').getHydrationPlan();
|
|
7375
|
+
* console.log(plan?.relations); // Information about included relations
|
|
6750
7376
|
*/
|
|
6751
7377
|
getHydrationPlan() {
|
|
6752
7378
|
return this.context.hydration.getPlan();
|
|
@@ -6754,6 +7380,10 @@ var SelectQueryBuilder = class _SelectQueryBuilder {
|
|
|
6754
7380
|
/**
|
|
6755
7381
|
* Gets the Abstract Syntax Tree (AST) representation of the query
|
|
6756
7382
|
* @returns Query AST with hydration applied
|
|
7383
|
+
* @example
|
|
7384
|
+
* const ast = qb.select('id', 'name').getAST();
|
|
7385
|
+
* console.log(ast.columns); // Array of column nodes
|
|
7386
|
+
* console.log(ast.from); // From clause information
|
|
6757
7387
|
*/
|
|
6758
7388
|
getAST() {
|
|
6759
7389
|
return this.context.hydration.applyToAst(this.context.state.ast);
|
|
@@ -6857,23 +7487,44 @@ var resolveTableTarget = (target, tableMap) => {
|
|
|
6857
7487
|
}
|
|
6858
7488
|
return table;
|
|
6859
7489
|
};
|
|
7490
|
+
var toSnakeCase = (value) => {
|
|
7491
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
|
|
7492
|
+
};
|
|
7493
|
+
var normalizeEntityName = (value) => {
|
|
7494
|
+
const stripped = value.replace(/Entity$/i, "");
|
|
7495
|
+
const normalized = toSnakeCase(stripped || value);
|
|
7496
|
+
return normalized || "unknown";
|
|
7497
|
+
};
|
|
7498
|
+
var getPivotKeyBaseFromTarget = (target) => {
|
|
7499
|
+
const resolved = unwrapTarget(target);
|
|
7500
|
+
if (isTableDef(resolved)) {
|
|
7501
|
+
return toSnakeCase(resolved.name || "unknown");
|
|
7502
|
+
}
|
|
7503
|
+
const ctor = resolved;
|
|
7504
|
+
return normalizeEntityName(ctor.name || "unknown");
|
|
7505
|
+
};
|
|
7506
|
+
var getPivotKeyBaseFromRoot = (meta) => {
|
|
7507
|
+
return normalizeEntityName(meta.target.name || meta.tableName || "unknown");
|
|
7508
|
+
};
|
|
6860
7509
|
var buildRelationDefinitions = (meta, tableMap) => {
|
|
6861
7510
|
const relations = {};
|
|
6862
7511
|
for (const [name, relation] of Object.entries(meta.relations)) {
|
|
6863
7512
|
switch (relation.kind) {
|
|
6864
7513
|
case RelationKinds.HasOne: {
|
|
7514
|
+
const foreignKey = relation.foreignKey ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
|
|
6865
7515
|
relations[name] = hasOne(
|
|
6866
7516
|
resolveTableTarget(relation.target, tableMap),
|
|
6867
|
-
|
|
7517
|
+
foreignKey,
|
|
6868
7518
|
relation.localKey,
|
|
6869
7519
|
relation.cascade
|
|
6870
7520
|
);
|
|
6871
7521
|
break;
|
|
6872
7522
|
}
|
|
6873
7523
|
case RelationKinds.HasMany: {
|
|
7524
|
+
const foreignKey = relation.foreignKey ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
|
|
6874
7525
|
relations[name] = hasMany(
|
|
6875
7526
|
resolveTableTarget(relation.target, tableMap),
|
|
6876
|
-
|
|
7527
|
+
foreignKey,
|
|
6877
7528
|
relation.localKey,
|
|
6878
7529
|
relation.cascade
|
|
6879
7530
|
);
|
|
@@ -6889,12 +7540,14 @@ var buildRelationDefinitions = (meta, tableMap) => {
|
|
|
6889
7540
|
break;
|
|
6890
7541
|
}
|
|
6891
7542
|
case RelationKinds.BelongsToMany: {
|
|
7543
|
+
const pivotForeignKeyToRoot = relation.pivotForeignKeyToRoot ?? `${getPivotKeyBaseFromRoot(meta)}_id`;
|
|
7544
|
+
const pivotForeignKeyToTarget = relation.pivotForeignKeyToTarget ?? `${getPivotKeyBaseFromTarget(relation.target)}_id`;
|
|
6892
7545
|
relations[name] = belongsToMany(
|
|
6893
7546
|
resolveTableTarget(relation.target, tableMap),
|
|
6894
7547
|
resolveTableTarget(relation.pivotTable, tableMap),
|
|
6895
7548
|
{
|
|
6896
|
-
pivotForeignKeyToRoot
|
|
6897
|
-
pivotForeignKeyToTarget
|
|
7549
|
+
pivotForeignKeyToRoot,
|
|
7550
|
+
pivotForeignKeyToTarget,
|
|
6898
7551
|
localKey: relation.localKey,
|
|
6899
7552
|
targetKey: relation.targetKey,
|
|
6900
7553
|
pivotPrimaryKey: relation.pivotPrimaryKey,
|
|
@@ -6975,6 +7628,8 @@ function esel(entity, ...props) {
|
|
|
6975
7628
|
|
|
6976
7629
|
// src/query-builder/insert-query-state.ts
|
|
6977
7630
|
var InsertQueryState = class _InsertQueryState {
|
|
7631
|
+
table;
|
|
7632
|
+
ast;
|
|
6978
7633
|
/**
|
|
6979
7634
|
* Creates a new InsertQueryState instance
|
|
6980
7635
|
* @param table - The table definition for the INSERT query
|
|
@@ -7095,6 +7750,8 @@ var InsertQueryState = class _InsertQueryState {
|
|
|
7095
7750
|
|
|
7096
7751
|
// src/query-builder/insert.ts
|
|
7097
7752
|
var InsertQueryBuilder = class _InsertQueryBuilder {
|
|
7753
|
+
table;
|
|
7754
|
+
state;
|
|
7098
7755
|
/**
|
|
7099
7756
|
* Creates a new InsertQueryBuilder instance
|
|
7100
7757
|
* @param table - The table definition for the INSERT query
|
|
@@ -7194,6 +7851,8 @@ var isUpdateValue = (value) => {
|
|
|
7194
7851
|
}
|
|
7195
7852
|
};
|
|
7196
7853
|
var UpdateQueryState = class _UpdateQueryState {
|
|
7854
|
+
table;
|
|
7855
|
+
ast;
|
|
7197
7856
|
/**
|
|
7198
7857
|
* Creates a new UpdateQueryState instance
|
|
7199
7858
|
* @param table - Table definition for the update
|
|
@@ -7304,6 +7963,8 @@ var UpdateQueryState = class _UpdateQueryState {
|
|
|
7304
7963
|
|
|
7305
7964
|
// src/query-builder/update.ts
|
|
7306
7965
|
var UpdateQueryBuilder = class _UpdateQueryBuilder {
|
|
7966
|
+
table;
|
|
7967
|
+
state;
|
|
7307
7968
|
/**
|
|
7308
7969
|
* Creates a new UpdateQueryBuilder instance
|
|
7309
7970
|
* @param table - The table definition for the UPDATE query
|
|
@@ -7421,6 +8082,8 @@ var isTableSourceNode = (source) => typeof source.type === "string";
|
|
|
7421
8082
|
|
|
7422
8083
|
// src/query-builder/delete-query-state.ts
|
|
7423
8084
|
var DeleteQueryState = class _DeleteQueryState {
|
|
8085
|
+
table;
|
|
8086
|
+
ast;
|
|
7424
8087
|
/**
|
|
7425
8088
|
* Creates a new DeleteQueryState instance
|
|
7426
8089
|
* @param table - The table definition for the DELETE query
|
|
@@ -7499,6 +8162,8 @@ var DeleteQueryState = class _DeleteQueryState {
|
|
|
7499
8162
|
|
|
7500
8163
|
// src/query-builder/delete.ts
|
|
7501
8164
|
var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
8165
|
+
table;
|
|
8166
|
+
state;
|
|
7502
8167
|
/**
|
|
7503
8168
|
* Creates a new DeleteQueryBuilder instance
|
|
7504
8169
|
* @param table - The table definition for the DELETE query
|
|
@@ -7605,6 +8270,39 @@ var DeleteQueryBuilder = class _DeleteQueryBuilder {
|
|
|
7605
8270
|
};
|
|
7606
8271
|
var isTableSourceNode2 = (source) => typeof source.type === "string";
|
|
7607
8272
|
|
|
8273
|
+
// src/query/target.ts
|
|
8274
|
+
var resolveEntityTarget = (ctor) => {
|
|
8275
|
+
const table = getTableDefFromEntity(ctor);
|
|
8276
|
+
if (!table) {
|
|
8277
|
+
throw new Error(`Entity '${ctor.name}' is not registered with decorators`);
|
|
8278
|
+
}
|
|
8279
|
+
return table;
|
|
8280
|
+
};
|
|
8281
|
+
var resolveTable = (target) => {
|
|
8282
|
+
if (isTableDef(target)) {
|
|
8283
|
+
return target;
|
|
8284
|
+
}
|
|
8285
|
+
return resolveEntityTarget(target);
|
|
8286
|
+
};
|
|
8287
|
+
|
|
8288
|
+
// src/query/index.ts
|
|
8289
|
+
var selectFrom = (target) => {
|
|
8290
|
+
const table = resolveTable(target);
|
|
8291
|
+
return new SelectQueryBuilder(table);
|
|
8292
|
+
};
|
|
8293
|
+
var insertInto = (target) => {
|
|
8294
|
+
const table = resolveTable(target);
|
|
8295
|
+
return new InsertQueryBuilder(table);
|
|
8296
|
+
};
|
|
8297
|
+
var update = (target) => {
|
|
8298
|
+
const table = resolveTable(target);
|
|
8299
|
+
return new UpdateQueryBuilder(table);
|
|
8300
|
+
};
|
|
8301
|
+
var deleteFrom = (target) => {
|
|
8302
|
+
const table = resolveTable(target);
|
|
8303
|
+
return new DeleteQueryBuilder(table);
|
|
8304
|
+
};
|
|
8305
|
+
|
|
7608
8306
|
// src/core/ddl/sql-writing.ts
|
|
7609
8307
|
var resolvePrimaryKey = (table) => {
|
|
7610
8308
|
if (Array.isArray(table.primaryKey) && table.primaryKey.length > 0) {
|
|
@@ -7626,7 +8324,8 @@ var renderColumnDefinition = (table, col2, dialect, options = {}) => {
|
|
|
7626
8324
|
if (col2.default !== void 0) {
|
|
7627
8325
|
parts.push(`DEFAULT ${dialect.renderDefault(col2.default, col2)}`);
|
|
7628
8326
|
}
|
|
7629
|
-
|
|
8327
|
+
const autoIncIncludesPrimary = typeof autoInc === "string" && /\bPRIMARY\s+KEY\b/i.test(autoInc);
|
|
8328
|
+
if (options.includePrimary && col2.primary && !autoIncIncludesPrimary) {
|
|
7630
8329
|
parts.push("PRIMARY KEY");
|
|
7631
8330
|
}
|
|
7632
8331
|
if (col2.check) {
|
|
@@ -7684,6 +8383,16 @@ var generateSchemaSql = (tables, dialect) => {
|
|
|
7684
8383
|
});
|
|
7685
8384
|
return statements;
|
|
7686
8385
|
};
|
|
8386
|
+
var generateSchemaSqlFor = (dialect, ...tables) => generateSchemaSql(tables, dialect);
|
|
8387
|
+
var executeSchemaSql = async (executor, tables, dialect) => {
|
|
8388
|
+
const statements = generateSchemaSql(tables, dialect);
|
|
8389
|
+
for (const sql of statements) {
|
|
8390
|
+
await executor.executeSql(sql);
|
|
8391
|
+
}
|
|
8392
|
+
};
|
|
8393
|
+
var executeSchemaSqlFor = async (executor, dialect, ...tables) => {
|
|
8394
|
+
await executeSchemaSql(executor, tables, dialect);
|
|
8395
|
+
};
|
|
7687
8396
|
var orderTablesByDependencies = (tables) => {
|
|
7688
8397
|
const map = /* @__PURE__ */ new Map();
|
|
7689
8398
|
tables.forEach((t) => map.set(t.name, t));
|
|
@@ -9590,6 +10299,7 @@ var arrayAppend = (array, value) => fn7("ARRAY_APPEND", [array, value]);
|
|
|
9590
10299
|
|
|
9591
10300
|
// src/orm/als.ts
|
|
9592
10301
|
var AsyncLocalStorage = class {
|
|
10302
|
+
store;
|
|
9593
10303
|
/**
|
|
9594
10304
|
* Executes a callback function within a context containing the specified store value.
|
|
9595
10305
|
* The store value is only available during the callback's execution and is automatically
|
|
@@ -10086,9 +10796,7 @@ var TypeScriptGenerator = class {
|
|
|
10086
10796
|
|
|
10087
10797
|
// src/orm/identity-map.ts
|
|
10088
10798
|
var IdentityMap = class {
|
|
10089
|
-
|
|
10090
|
-
this.buckets = /* @__PURE__ */ new Map();
|
|
10091
|
-
}
|
|
10799
|
+
buckets = /* @__PURE__ */ new Map();
|
|
10092
10800
|
get bucketsMap() {
|
|
10093
10801
|
return this.buckets;
|
|
10094
10802
|
}
|
|
@@ -10158,8 +10866,8 @@ var UnitOfWork = class {
|
|
|
10158
10866
|
this.executor = executor;
|
|
10159
10867
|
this.identityMap = identityMap;
|
|
10160
10868
|
this.hookContext = hookContext;
|
|
10161
|
-
this.trackedEntities = /* @__PURE__ */ new Map();
|
|
10162
10869
|
}
|
|
10870
|
+
trackedEntities = /* @__PURE__ */ new Map();
|
|
10163
10871
|
/**
|
|
10164
10872
|
* Gets the identity buckets map.
|
|
10165
10873
|
*/
|
|
@@ -10489,12 +11197,12 @@ var UnitOfWork = class {
|
|
|
10489
11197
|
|
|
10490
11198
|
// src/orm/domain-event-bus.ts
|
|
10491
11199
|
var DomainEventBus = class {
|
|
11200
|
+
handlers = /* @__PURE__ */ new Map();
|
|
10492
11201
|
/**
|
|
10493
11202
|
* Creates a new DomainEventBus instance.
|
|
10494
11203
|
* @param initialHandlers - Optional initial event handlers
|
|
10495
11204
|
*/
|
|
10496
11205
|
constructor(initialHandlers) {
|
|
10497
|
-
this.handlers = /* @__PURE__ */ new Map();
|
|
10498
11206
|
if (initialHandlers) {
|
|
10499
11207
|
for (const key in initialHandlers) {
|
|
10500
11208
|
const type = key;
|
|
@@ -10563,8 +11271,8 @@ var RelationChangeProcessor = class {
|
|
|
10563
11271
|
this.unitOfWork = unitOfWork;
|
|
10564
11272
|
this.dialect = dialect;
|
|
10565
11273
|
this.executor = executor;
|
|
10566
|
-
this.relationChanges = [];
|
|
10567
11274
|
}
|
|
11275
|
+
relationChanges = [];
|
|
10568
11276
|
/**
|
|
10569
11277
|
* Registers a relation change for processing.
|
|
10570
11278
|
* @param entry - The relation change entry
|
|
@@ -10998,25 +11706,24 @@ var saveGraphInternal = async (session, entityClass, payload, options = {}) => {
|
|
|
10998
11706
|
|
|
10999
11707
|
// src/orm/orm-session.ts
|
|
11000
11708
|
var OrmSession = class {
|
|
11709
|
+
/** The ORM instance */
|
|
11710
|
+
orm;
|
|
11711
|
+
/** The database executor */
|
|
11712
|
+
executor;
|
|
11713
|
+
/** The identity map for tracking entity instances */
|
|
11714
|
+
identityMap;
|
|
11715
|
+
/** The unit of work for tracking entity changes */
|
|
11716
|
+
unitOfWork;
|
|
11717
|
+
/** The domain event bus */
|
|
11718
|
+
domainEvents;
|
|
11719
|
+
/** The relation change processor */
|
|
11720
|
+
relationChanges;
|
|
11721
|
+
interceptors;
|
|
11001
11722
|
/**
|
|
11002
11723
|
* Creates a new OrmSession instance.
|
|
11003
11724
|
* @param opts - Session options
|
|
11004
11725
|
*/
|
|
11005
11726
|
constructor(opts) {
|
|
11006
|
-
/**
|
|
11007
|
-
* Registers a relation change.
|
|
11008
|
-
* @param root - The root entity
|
|
11009
|
-
* @param relationKey - The relation key
|
|
11010
|
-
* @param rootTable - The root table definition
|
|
11011
|
-
* @param relationName - The relation name
|
|
11012
|
-
* @param relation - The relation definition
|
|
11013
|
-
* @param change - The relation change
|
|
11014
|
-
*/
|
|
11015
|
-
this.registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
|
|
11016
|
-
this.relationChanges.registerChange(
|
|
11017
|
-
buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
|
|
11018
|
-
);
|
|
11019
|
-
};
|
|
11020
11727
|
this.orm = opts.orm;
|
|
11021
11728
|
this.executor = createQueryLoggingExecutor(opts.executor, opts.queryLogger);
|
|
11022
11729
|
this.interceptors = [...opts.interceptors ?? []];
|
|
@@ -11105,6 +11812,20 @@ var OrmSession = class {
|
|
|
11105
11812
|
markRemoved(entity) {
|
|
11106
11813
|
this.unitOfWork.markRemoved(entity);
|
|
11107
11814
|
}
|
|
11815
|
+
/**
|
|
11816
|
+
* Registers a relation change.
|
|
11817
|
+
* @param root - The root entity
|
|
11818
|
+
* @param relationKey - The relation key
|
|
11819
|
+
* @param rootTable - The root table definition
|
|
11820
|
+
* @param relationName - The relation name
|
|
11821
|
+
* @param relation - The relation definition
|
|
11822
|
+
* @param change - The relation change
|
|
11823
|
+
*/
|
|
11824
|
+
registerRelationChange = (root, relationKey, rootTable, relationName, relation, change) => {
|
|
11825
|
+
this.relationChanges.registerChange(
|
|
11826
|
+
buildRelationChangeEntry(root, relationKey, rootTable, relationName, relation, change)
|
|
11827
|
+
);
|
|
11828
|
+
};
|
|
11108
11829
|
/**
|
|
11109
11830
|
* Gets all tracked entities for a specific table.
|
|
11110
11831
|
* @param table - The table definition
|
|
@@ -11310,9 +12031,7 @@ var buildRelationChangeEntry = (root, relationKey, rootTable, relationName, rela
|
|
|
11310
12031
|
|
|
11311
12032
|
// src/orm/interceptor-pipeline.ts
|
|
11312
12033
|
var InterceptorPipeline = class {
|
|
11313
|
-
|
|
11314
|
-
this.interceptors = [];
|
|
11315
|
-
}
|
|
12034
|
+
interceptors = [];
|
|
11316
12035
|
use(interceptor) {
|
|
11317
12036
|
this.interceptors.push(interceptor);
|
|
11318
12037
|
}
|
|
@@ -11331,6 +12050,13 @@ var InterceptorPipeline = class {
|
|
|
11331
12050
|
|
|
11332
12051
|
// src/orm/orm.ts
|
|
11333
12052
|
var Orm = class {
|
|
12053
|
+
/** The database dialect */
|
|
12054
|
+
dialect;
|
|
12055
|
+
/** The interceptors pipeline */
|
|
12056
|
+
interceptors;
|
|
12057
|
+
/** The naming strategy */
|
|
12058
|
+
namingStrategy;
|
|
12059
|
+
executorFactory;
|
|
11334
12060
|
/**
|
|
11335
12061
|
* Creates a new ORM instance.
|
|
11336
12062
|
* @param opts - ORM options
|
|
@@ -11387,17 +12113,13 @@ var jsonify = (value) => {
|
|
|
11387
12113
|
|
|
11388
12114
|
// src/decorators/decorator-metadata.ts
|
|
11389
12115
|
var METADATA_KEY = "metal-orm:decorators";
|
|
11390
|
-
var isStandardDecoratorContext = (value) => {
|
|
11391
|
-
return typeof value === "object" && value !== null && "kind" in value;
|
|
11392
|
-
};
|
|
11393
12116
|
var getOrCreateMetadataBag = (context) => {
|
|
11394
12117
|
const metadata = context.metadata || (context.metadata = {});
|
|
11395
|
-
|
|
11396
|
-
if (
|
|
11397
|
-
|
|
12118
|
+
let bag = metadata[METADATA_KEY];
|
|
12119
|
+
if (!bag) {
|
|
12120
|
+
bag = { columns: [], relations: [] };
|
|
12121
|
+
metadata[METADATA_KEY] = bag;
|
|
11398
12122
|
}
|
|
11399
|
-
const bag = { columns: [], relations: [] };
|
|
11400
|
-
metadata[METADATA_KEY] = bag;
|
|
11401
12123
|
return bag;
|
|
11402
12124
|
};
|
|
11403
12125
|
var readMetadataBag = (context) => {
|
|
@@ -11412,57 +12134,50 @@ var readMetadataBagFromConstructor = (ctor) => {
|
|
|
11412
12134
|
var getDecoratorMetadata = (ctor) => readMetadataBagFromConstructor(ctor);
|
|
11413
12135
|
|
|
11414
12136
|
// src/decorators/entity.ts
|
|
11415
|
-
var
|
|
12137
|
+
var toSnakeCase2 = (value) => {
|
|
11416
12138
|
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9_]+/gi, "_").replace(/__+/g, "_").replace(/^_|_$/g, "").toLowerCase();
|
|
11417
12139
|
};
|
|
11418
12140
|
var deriveTableNameFromConstructor = (ctor) => {
|
|
11419
12141
|
const fallback = "unknown";
|
|
11420
12142
|
const rawName = ctor.name || fallback;
|
|
11421
12143
|
const strippedName = rawName.replace(/Entity$/i, "");
|
|
11422
|
-
const normalized =
|
|
12144
|
+
const normalized = toSnakeCase2(strippedName || rawName);
|
|
11423
12145
|
if (!normalized) {
|
|
11424
12146
|
return fallback;
|
|
11425
12147
|
}
|
|
11426
12148
|
return normalized.endsWith("s") ? normalized : `${normalized}s`;
|
|
11427
12149
|
};
|
|
11428
12150
|
function Entity(options = {}) {
|
|
11429
|
-
|
|
11430
|
-
const tableName = options.tableName ?? deriveTableNameFromConstructor(value);
|
|
11431
|
-
setEntityTableName(value, tableName, options.hooks);
|
|
11432
|
-
return value;
|
|
11433
|
-
};
|
|
11434
|
-
const decoratorWithContext = (value, context) => {
|
|
12151
|
+
return function(value, context) {
|
|
11435
12152
|
const ctor = value;
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
|
|
11445
|
-
|
|
11446
|
-
}
|
|
11447
|
-
addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
|
|
12153
|
+
const tableName = options.tableName ?? deriveTableNameFromConstructor(ctor);
|
|
12154
|
+
setEntityTableName(ctor, tableName, options.hooks);
|
|
12155
|
+
const bag = readMetadataBag(context);
|
|
12156
|
+
if (bag) {
|
|
12157
|
+
const meta = ensureEntityMetadata(ctor);
|
|
12158
|
+
for (const entry of bag.columns) {
|
|
12159
|
+
if (meta.columns[entry.propertyName]) {
|
|
12160
|
+
throw new Error(
|
|
12161
|
+
`Column '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
|
|
12162
|
+
);
|
|
11448
12163
|
}
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
...entry.relation,
|
|
11457
|
-
defaultPivotColumns: entry.relation.defaultPivotColumns ? [...entry.relation.defaultPivotColumns] : void 0
|
|
11458
|
-
} : { ...entry.relation };
|
|
11459
|
-
addRelationMetadata(ctor, entry.propertyName, relationCopy);
|
|
12164
|
+
addColumnMetadata(ctor, entry.propertyName, { ...entry.column });
|
|
12165
|
+
}
|
|
12166
|
+
for (const entry of bag.relations) {
|
|
12167
|
+
if (meta.relations[entry.propertyName]) {
|
|
12168
|
+
throw new Error(
|
|
12169
|
+
`Relation '${entry.propertyName}' is already defined on entity '${ctor.name}'.`
|
|
12170
|
+
);
|
|
11460
12171
|
}
|
|
12172
|
+
const relationCopy = entry.relation.kind === RelationKinds.BelongsToMany ? {
|
|
12173
|
+
...entry.relation,
|
|
12174
|
+
defaultPivotColumns: entry.relation.defaultPivotColumns ? [...entry.relation.defaultPivotColumns] : void 0
|
|
12175
|
+
} : { ...entry.relation };
|
|
12176
|
+
addRelationMetadata(ctor, entry.propertyName, relationCopy);
|
|
11461
12177
|
}
|
|
11462
12178
|
}
|
|
11463
12179
|
return ctor;
|
|
11464
12180
|
};
|
|
11465
|
-
return decoratorWithContext;
|
|
11466
12181
|
}
|
|
11467
12182
|
|
|
11468
12183
|
// src/decorators/column-decorator.ts
|
|
@@ -11495,26 +12210,13 @@ var normalizePropertyName = (name) => {
|
|
|
11495
12210
|
}
|
|
11496
12211
|
return name;
|
|
11497
12212
|
};
|
|
11498
|
-
var resolveConstructor = (target) => {
|
|
11499
|
-
if (typeof target === "function") {
|
|
11500
|
-
return target;
|
|
11501
|
-
}
|
|
11502
|
-
if (target && typeof target.constructor === "function") {
|
|
11503
|
-
return target.constructor;
|
|
11504
|
-
}
|
|
11505
|
-
return void 0;
|
|
11506
|
-
};
|
|
11507
|
-
var registerColumn = (ctor, propertyName, column) => {
|
|
11508
|
-
const meta = ensureEntityMetadata(ctor);
|
|
11509
|
-
if (meta.columns[propertyName]) {
|
|
11510
|
-
return;
|
|
11511
|
-
}
|
|
11512
|
-
addColumnMetadata(ctor, propertyName, column);
|
|
11513
|
-
};
|
|
11514
12213
|
var registerColumnFromContext = (context, column) => {
|
|
11515
12214
|
if (!context.name) {
|
|
11516
12215
|
throw new Error("Column decorator requires a property name");
|
|
11517
12216
|
}
|
|
12217
|
+
if (context.private) {
|
|
12218
|
+
throw new Error("Column decorator does not support private fields");
|
|
12219
|
+
}
|
|
11518
12220
|
const propertyName = normalizePropertyName(context.name);
|
|
11519
12221
|
const bag = getOrCreateMetadataBag(context);
|
|
11520
12222
|
if (!bag.columns.some((entry) => entry.propertyName === propertyName)) {
|
|
@@ -11523,19 +12225,9 @@ var registerColumnFromContext = (context, column) => {
|
|
|
11523
12225
|
};
|
|
11524
12226
|
function Column(definition) {
|
|
11525
12227
|
const normalized = normalizeColumnInput(definition);
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
registerColumnFromContext(propertyKeyOrContext, normalized);
|
|
11529
|
-
return;
|
|
11530
|
-
}
|
|
11531
|
-
const propertyName = normalizePropertyName(propertyKeyOrContext);
|
|
11532
|
-
const ctor = resolveConstructor(targetOrValue);
|
|
11533
|
-
if (!ctor) {
|
|
11534
|
-
throw new Error("Unable to resolve constructor when registering column metadata");
|
|
11535
|
-
}
|
|
11536
|
-
registerColumn(ctor, propertyName, { ...normalized });
|
|
12228
|
+
return function(_value, context) {
|
|
12229
|
+
registerColumnFromContext(context, normalized);
|
|
11537
12230
|
};
|
|
11538
|
-
return decorator;
|
|
11539
12231
|
}
|
|
11540
12232
|
function PrimaryKey(definition) {
|
|
11541
12233
|
const normalized = normalizeColumnInput(definition);
|
|
@@ -11550,41 +12242,21 @@ var normalizePropertyName2 = (name) => {
|
|
|
11550
12242
|
}
|
|
11551
12243
|
return name;
|
|
11552
12244
|
};
|
|
11553
|
-
var resolveConstructor2 = (instanceOrCtor) => {
|
|
11554
|
-
if (typeof instanceOrCtor === "function") {
|
|
11555
|
-
return instanceOrCtor;
|
|
11556
|
-
}
|
|
11557
|
-
if (instanceOrCtor && typeof instanceOrCtor.constructor === "function") {
|
|
11558
|
-
return instanceOrCtor.constructor;
|
|
11559
|
-
}
|
|
11560
|
-
return void 0;
|
|
11561
|
-
};
|
|
11562
|
-
var registerRelation = (ctor, propertyName, metadata) => {
|
|
11563
|
-
addRelationMetadata(ctor, propertyName, metadata);
|
|
11564
|
-
};
|
|
11565
12245
|
var createFieldDecorator = (metadataFactory) => {
|
|
11566
|
-
|
|
11567
|
-
if (
|
|
11568
|
-
|
|
11569
|
-
if (!ctx.name) {
|
|
11570
|
-
throw new Error("Relation decorator requires a property name");
|
|
11571
|
-
}
|
|
11572
|
-
const propertyName2 = normalizePropertyName2(ctx.name);
|
|
11573
|
-
const bag = getOrCreateMetadataBag(ctx);
|
|
11574
|
-
const relationMetadata = metadataFactory(propertyName2);
|
|
11575
|
-
if (!bag.relations.some((entry) => entry.propertyName === propertyName2)) {
|
|
11576
|
-
bag.relations.push({ propertyName: propertyName2, relation: relationMetadata });
|
|
11577
|
-
}
|
|
11578
|
-
return;
|
|
12246
|
+
return function(_value, context) {
|
|
12247
|
+
if (!context.name) {
|
|
12248
|
+
throw new Error("Relation decorator requires a property name");
|
|
11579
12249
|
}
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
12250
|
+
if (context.private) {
|
|
12251
|
+
throw new Error("Relation decorator does not support private fields");
|
|
12252
|
+
}
|
|
12253
|
+
const propertyName = normalizePropertyName2(context.name);
|
|
12254
|
+
const bag = getOrCreateMetadataBag(context);
|
|
12255
|
+
const relationMetadata = metadataFactory(propertyName);
|
|
12256
|
+
if (!bag.relations.some((entry) => entry.propertyName === propertyName)) {
|
|
12257
|
+
bag.relations.push({ propertyName, relation: relationMetadata });
|
|
11584
12258
|
}
|
|
11585
|
-
registerRelation(ctor, propertyName, metadataFactory(propertyName));
|
|
11586
12259
|
};
|
|
11587
|
-
return decorator;
|
|
11588
12260
|
};
|
|
11589
12261
|
function HasMany(options) {
|
|
11590
12262
|
return createFieldDecorator((propertyName) => ({
|
|
@@ -11611,7 +12283,7 @@ function BelongsTo(options) {
|
|
|
11611
12283
|
kind: RelationKinds.BelongsTo,
|
|
11612
12284
|
propertyKey: propertyName,
|
|
11613
12285
|
target: options.target,
|
|
11614
|
-
foreignKey: options.foreignKey
|
|
12286
|
+
foreignKey: options.foreignKey ?? `${propertyName}_id`,
|
|
11615
12287
|
localKey: options.localKey,
|
|
11616
12288
|
cascade: options.cascade
|
|
11617
12289
|
}));
|
|
@@ -11687,13 +12359,15 @@ var deferred = () => {
|
|
|
11687
12359
|
return { promise, resolve, reject };
|
|
11688
12360
|
};
|
|
11689
12361
|
var Pool = class {
|
|
12362
|
+
adapter;
|
|
12363
|
+
options;
|
|
12364
|
+
destroyed = false;
|
|
12365
|
+
creating = 0;
|
|
12366
|
+
leased = 0;
|
|
12367
|
+
idle = [];
|
|
12368
|
+
waiters = [];
|
|
12369
|
+
reapTimer = null;
|
|
11690
12370
|
constructor(adapter, options) {
|
|
11691
|
-
this.destroyed = false;
|
|
11692
|
-
this.creating = 0;
|
|
11693
|
-
this.leased = 0;
|
|
11694
|
-
this.idle = [];
|
|
11695
|
-
this.waiters = [];
|
|
11696
|
-
this.reapTimer = null;
|
|
11697
12371
|
if (!Number.isFinite(options.max) || options.max <= 0) {
|
|
11698
12372
|
throw new Error("Pool options.max must be a positive number");
|
|
11699
12373
|
}
|
|
@@ -12247,6 +12921,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12247
12921
|
dayOfWeek,
|
|
12248
12922
|
defineTable,
|
|
12249
12923
|
degrees,
|
|
12924
|
+
deleteFrom,
|
|
12250
12925
|
denseRank,
|
|
12251
12926
|
diffSchema,
|
|
12252
12927
|
div,
|
|
@@ -12256,6 +12931,8 @@ function createPooledExecutorFactory(opts) {
|
|
|
12256
12931
|
esel,
|
|
12257
12932
|
executeHydrated,
|
|
12258
12933
|
executeHydratedWithContexts,
|
|
12934
|
+
executeSchemaSql,
|
|
12935
|
+
executeSchemaSqlFor,
|
|
12259
12936
|
exists,
|
|
12260
12937
|
exp,
|
|
12261
12938
|
extract,
|
|
@@ -12264,6 +12941,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12264
12941
|
fromUnixTime,
|
|
12265
12942
|
generateCreateTableSql,
|
|
12266
12943
|
generateSchemaSql,
|
|
12944
|
+
generateSchemaSqlFor,
|
|
12267
12945
|
getColumn,
|
|
12268
12946
|
getDecoratorMetadata,
|
|
12269
12947
|
getSchemaIntrospector,
|
|
@@ -12280,6 +12958,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12280
12958
|
inList,
|
|
12281
12959
|
inSubquery,
|
|
12282
12960
|
initcap,
|
|
12961
|
+
insertInto,
|
|
12283
12962
|
instr,
|
|
12284
12963
|
introspectSchema,
|
|
12285
12964
|
isCaseExpressionNode,
|
|
@@ -12368,7 +13047,9 @@ function createPooledExecutorFactory(opts) {
|
|
|
12368
13047
|
rtrim,
|
|
12369
13048
|
second,
|
|
12370
13049
|
sel,
|
|
13050
|
+
selectFrom,
|
|
12371
13051
|
selectFromEntity,
|
|
13052
|
+
setRelations,
|
|
12372
13053
|
sha1,
|
|
12373
13054
|
sha2,
|
|
12374
13055
|
shiftLeft,
|
|
@@ -12390,6 +13071,7 @@ function createPooledExecutorFactory(opts) {
|
|
|
12390
13071
|
trunc,
|
|
12391
13072
|
truncate,
|
|
12392
13073
|
unixTimestamp,
|
|
13074
|
+
update,
|
|
12393
13075
|
upper,
|
|
12394
13076
|
utcNow,
|
|
12395
13077
|
valueToOperand,
|