@danceroutine/tango-orm 1.11.11 → 1.11.12

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.
@@ -1104,6 +1104,263 @@ var QBuilder = class QBuilder {
1104
1104
  }
1105
1105
  };
1106
1106
  //#endregion
1107
+ //#region src/query/hydration/HydrationEntityRegistry.ts
1108
+ /**
1109
+ * Tracks hydrated related records so repeated rows point at one canonical
1110
+ * object per model primary key.
1111
+ */
1112
+ var HydrationEntityRegistry = class HydrationEntityRegistry {
1113
+ attachPersistedRecordAccessors;
1114
+ static BRAND = "tango.orm.hydration_entity_registry";
1115
+ __tangoBrand = HydrationEntityRegistry.BRAND;
1116
+ recordsByModel = /* @__PURE__ */ new Map();
1117
+ constructor(attachPersistedRecordAccessors) {
1118
+ this.attachPersistedRecordAccessors = attachPersistedRecordAccessors;
1119
+ }
1120
+ static isHydrationEntityRegistry(value) {
1121
+ return typeof value === "object" && value !== null && value.__tangoBrand === HydrationEntityRegistry.BRAND;
1122
+ }
1123
+ canonicalize(node, row) {
1124
+ const primaryKeyValue = row[node.targetPrimaryKey];
1125
+ if (typeof primaryKeyValue !== "string" && typeof primaryKeyValue !== "number") return row;
1126
+ const byModel = this.recordsByModel.get(node.targetModelKey) ?? /* @__PURE__ */ new Map();
1127
+ const existing = byModel.get(primaryKeyValue);
1128
+ if (existing) {
1129
+ Object.assign(existing, row);
1130
+ return existing;
1131
+ }
1132
+ byModel.set(primaryKeyValue, row);
1133
+ this.recordsByModel.set(node.targetModelKey, byModel);
1134
+ this.attachPersistedRecordAccessors?.(row, node.targetModelKey);
1135
+ return row;
1136
+ }
1137
+ };
1138
+ //#endregion
1139
+ //#region src/query/hydration/QueryRowNormalizerStrategy.ts
1140
+ var PassthroughQueryRowNormalizerStrategy = class {
1141
+ normalizeRootRows(rows, _columns) {
1142
+ return [...rows];
1143
+ }
1144
+ normalizeHydratedRowsForParserShape(rows, _columns) {
1145
+ return [...rows];
1146
+ }
1147
+ normalizeTargetRow(row, _targetColumns) {
1148
+ return row;
1149
+ }
1150
+ normalizeColumnValue(_columnType, value) {
1151
+ return value;
1152
+ }
1153
+ };
1154
+ function createQueryRowNormalizerStrategy(dialect) {
1155
+ return dialect === InternalDialect.SQLITE ? new SqliteRowNormalizerStrategy() : new PassthroughQueryRowNormalizerStrategy();
1156
+ }
1157
+ var SqliteRowNormalizerStrategy = class {
1158
+ normalizeRootRows(rows, columns) {
1159
+ return this.normalizeRows(rows, columns);
1160
+ }
1161
+ normalizeHydratedRowsForParserShape(rows, columns) {
1162
+ return this.normalizeRows(rows, columns);
1163
+ }
1164
+ normalizeTargetRow(row, targetColumns) {
1165
+ return this.normalizeRow(row, this.booleanColumns(targetColumns));
1166
+ }
1167
+ normalizeColumnValue(columnType, value) {
1168
+ return this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
1169
+ }
1170
+ normalizeRows(rows, columns) {
1171
+ const booleanColumns = this.booleanColumns(columns);
1172
+ if (booleanColumns.length === 0) return [...rows];
1173
+ return rows.map((row) => this.normalizeRow(row, booleanColumns));
1174
+ }
1175
+ normalizeRow(row, columns) {
1176
+ let normalized = null;
1177
+ for (const column of columns) {
1178
+ const current = row[column];
1179
+ const next = this.normalizeSqliteBoolean(current);
1180
+ if (next === current) continue;
1181
+ normalized ??= { ...row };
1182
+ normalized[column] = next;
1183
+ }
1184
+ return normalized ?? row;
1185
+ }
1186
+ booleanColumns(columns) {
1187
+ return Object.entries(columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
1188
+ }
1189
+ isBooleanColumnType(value) {
1190
+ return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
1191
+ }
1192
+ normalizeSqliteBoolean(value) {
1193
+ if (value === 0 || value === "0") return false;
1194
+ if (value === 1 || value === "1") return true;
1195
+ return value;
1196
+ }
1197
+ };
1198
+ //#endregion
1199
+ //#region src/query/hydration/QueryHydrator.ts
1200
+ /**
1201
+ * Coordinates compiled relation hydration for `QuerySet` evaluation.
1202
+ */
1203
+ var QueryHydrator = class QueryHydrator {
1204
+ executor;
1205
+ static BRAND = "tango.orm.query_hydrator";
1206
+ __tangoBrand = QueryHydrator.BRAND;
1207
+ normalizer;
1208
+ constructor(executor, normalizer) {
1209
+ this.executor = executor;
1210
+ this.normalizer = normalizer ?? createQueryRowNormalizerStrategy(executor.adapter.dialect);
1211
+ }
1212
+ static isQueryHydrator(value) {
1213
+ return typeof value === "object" && value !== null && value.__tangoBrand === QueryHydrator.BRAND;
1214
+ }
1215
+ async materializeRows(rows, compiled) {
1216
+ const hydratedRows = await this.hydrateRows(rows, compiled);
1217
+ this.attachRootRecordAccessors(hydratedRows);
1218
+ return hydratedRows;
1219
+ }
1220
+ async hydrateRows(rows, compiled) {
1221
+ if (!compiled.hydrationPlan) return rows;
1222
+ const hydratedRows = rows.map((row) => ({ ...row }));
1223
+ this.attachRootRecordAccessors(hydratedRows);
1224
+ const registry = new HydrationEntityRegistry(this.executor.attachPersistedRecordAccessors);
1225
+ const queuedJoinPrefetchOwners = /* @__PURE__ */ new Map();
1226
+ const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
1227
+ for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, registry, queuedJoinPrefetchOwners);
1228
+ for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, registry, compiler);
1229
+ for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], registry, compiler);
1230
+ for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
1231
+ return hydratedRows;
1232
+ }
1233
+ attachRootRecordAccessors(rows) {
1234
+ if (!this.executor.attachPersistedRecordAccessors) return;
1235
+ const sourceModelKey = this.executor.meta.modelKey;
1236
+ for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
1237
+ }
1238
+ hydrateJoinNodesForOwner(owner, rawRow, nodes, registry, queuedJoinPrefetchOwners) {
1239
+ for (const node of nodes) {
1240
+ if (!node.join) continue;
1241
+ const target = {};
1242
+ let hasTargetValue = false;
1243
+ for (const [column, alias] of Object.entries(node.join.columns)) {
1244
+ const value = rawRow[alias];
1245
+ delete rawRow[alias];
1246
+ target[column] = this.normalizer.normalizeColumnValue(node.targetColumns[column], value);
1247
+ if (value !== null && value !== void 0) hasTargetValue = true;
1248
+ }
1249
+ if (!hasTargetValue) {
1250
+ owner[node.relationName] = null;
1251
+ continue;
1252
+ }
1253
+ const canonical = registry.canonicalize(node, target);
1254
+ owner[node.relationName] = canonical;
1255
+ for (const childNode of node.prefetchChildren) {
1256
+ const queuedOwners = queuedJoinPrefetchOwners?.get(childNode);
1257
+ if (queuedOwners) {
1258
+ queuedOwners.add(canonical);
1259
+ continue;
1260
+ }
1261
+ queuedJoinPrefetchOwners?.set(childNode, new Set([canonical]));
1262
+ }
1263
+ this.hydrateJoinNodesForOwner(canonical, rawRow, node.joinChildren, registry, queuedJoinPrefetchOwners);
1264
+ }
1265
+ }
1266
+ async hydratePrefetchNode(node, owners, registry, compiler) {
1267
+ if (owners.length === 0) return;
1268
+ const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
1269
+ const sourceValues = [...groupedOwners.keys()];
1270
+ if (!!!node.throughTable) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
1271
+ if (sourceValues.length === 0) return;
1272
+ const sourceChunks = this.chunkValues(sourceValues, 500);
1273
+ const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
1274
+ if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
1275
+ const idsByOwner = /* @__PURE__ */ new Map();
1276
+ const uniqueTargetIds = /* @__PURE__ */ new Set();
1277
+ for (const chunk of sourceChunks) {
1278
+ const chunkCompiled = compiler.compilePrefetch(node, chunk);
1279
+ const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
1280
+ for (const row of throughResult.rows) {
1281
+ const ownerId = row[chunkCompiled.ownerAlias];
1282
+ const targetId = row[chunkCompiled.targetAlias];
1283
+ if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
1284
+ const bucket = idsByOwner.get(ownerId) ?? [];
1285
+ bucket.push(targetId);
1286
+ idsByOwner.set(ownerId, bucket);
1287
+ uniqueTargetIds.add(targetId);
1288
+ }
1289
+ }
1290
+ const targets = {};
1291
+ const targetIds = [...uniqueTargetIds.values()];
1292
+ if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
1293
+ const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
1294
+ const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
1295
+ for (const rawTargetRow of targetResult.rows) {
1296
+ const normalized = this.normalizer.normalizeTargetRow(rawTargetRow, compiledPrefetch.targetColumns);
1297
+ const canonical = registry.canonicalize(node, normalized);
1298
+ this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, registry);
1299
+ const primaryKey = canonical[node.targetPrimaryKey];
1300
+ if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
1301
+ }
1302
+ }
1303
+ const canonicalChildren = /* @__PURE__ */ new Map();
1304
+ const handledOwners = /* @__PURE__ */ new Set();
1305
+ for (const [ownerId, ids] of idsByOwner.entries()) {
1306
+ const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
1307
+ for (const owner of groupedOwners.get(ownerId) ?? []) {
1308
+ this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
1309
+ handledOwners.add(owner);
1310
+ }
1311
+ for (const child of bucket) canonicalChildren.set(child[node.targetPrimaryKey], child);
1312
+ }
1313
+ for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
1314
+ const childOwners = [...canonicalChildren.values()];
1315
+ for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, registry, compiler);
1316
+ return;
1317
+ }
1318
+ const canonicalChildren = /* @__PURE__ */ new Map();
1319
+ for (const chunk of sourceChunks) {
1320
+ const chunkCompiled = compiler.compilePrefetch(node, chunk);
1321
+ const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
1322
+ for (const rawResultRow of result.rows) {
1323
+ const normalized = this.normalizer.normalizeTargetRow(rawResultRow, chunkCompiled.targetColumns);
1324
+ const canonical = registry.canonicalize(node, normalized);
1325
+ this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, registry);
1326
+ const key = normalized[chunkCompiled.targetKey];
1327
+ if (typeof key !== "string" && typeof key !== "number") continue;
1328
+ for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
1329
+ else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
1330
+ const childPrimaryKey = canonical[node.targetPrimaryKey];
1331
+ if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
1332
+ }
1333
+ }
1334
+ const childOwners = [...canonicalChildren.values()];
1335
+ for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, registry, compiler);
1336
+ }
1337
+ primeManyToManyOwnerCache(owner, relationName, bucket) {
1338
+ const existing = owner[relationName];
1339
+ if (existing && typeof existing.primePrefetchCache === "function") {
1340
+ existing.primePrefetchCache(bucket);
1341
+ return;
1342
+ }
1343
+ owner[relationName] = bucket.slice();
1344
+ }
1345
+ chunkValues(values, size) {
1346
+ if (values.length <= size) return [Array.from(values)];
1347
+ const chunks = [];
1348
+ for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
1349
+ return chunks;
1350
+ }
1351
+ groupOwnersByAccessor(owners, accessor) {
1352
+ const grouped = /* @__PURE__ */ new Map();
1353
+ for (const owner of owners) {
1354
+ const key = owner[accessor];
1355
+ if (typeof key !== "string" && typeof key !== "number") continue;
1356
+ const bucket = grouped.get(key) ?? [];
1357
+ bucket.push(owner);
1358
+ grouped.set(key, bucket);
1359
+ }
1360
+ return grouped;
1361
+ }
1362
+ };
1363
+ //#endregion
1107
1364
  //#region src/query/QuerySet.ts
1108
1365
  /**
1109
1366
  * Django-inspired query builder for constructing and executing database queries.
@@ -1132,9 +1389,13 @@ var QuerySet = class QuerySet {
1132
1389
  static BRAND = "tango.orm.query_set";
1133
1390
  __tangoBrand = QuerySet.BRAND;
1134
1391
  evaluationCache;
1392
+ hydrator;
1393
+ rowNormalizer;
1135
1394
  constructor(executor, state = {}) {
1136
1395
  this.executor = executor;
1137
1396
  this.state = state;
1397
+ this.rowNormalizer = createQueryRowNormalizerStrategy(executor.adapter.dialect);
1398
+ this.hydrator = new QueryHydrator(executor, this.rowNormalizer);
1138
1399
  }
1139
1400
  /**
1140
1401
  * Narrow an unknown value to `QuerySet`.
@@ -1275,7 +1536,7 @@ var QuerySet = class QuerySet {
1275
1536
  async fetch(shape) {
1276
1537
  const baseResult = await this.getOrCreateEvaluationCache();
1277
1538
  if (!shape) return baseResult;
1278
- return new QueryResult(typeof shape === "function" ? baseResult.items.map(shape) : this.normalizeHydratedRowsForParserShape(baseResult.items).map((row) => shape.parse(row)));
1539
+ return new QueryResult(typeof shape === "function" ? baseResult.items.map(shape) : this.rowNormalizer.normalizeHydratedRowsForParserShape(baseResult.items, this.executor.meta.columns).map((row) => shape.parse(row)));
1279
1540
  }
1280
1541
  /**
1281
1542
  * Async iterable surface for `for await (... of queryset)`.
@@ -1339,7 +1600,7 @@ var QuerySet = class QuerySet {
1339
1600
  shapeFetchedRow(row, shape) {
1340
1601
  if (!shape) return row;
1341
1602
  if (typeof shape === "function") return shape(row);
1342
- const normalizedRow = this.normalizeHydratedRowsForParserShape([row])[0] ?? row;
1603
+ const normalizedRow = this.rowNormalizer.normalizeHydratedRowsForParserShape([row], this.executor.meta.columns)[0];
1343
1604
  return shape.parse(normalizedRow);
1344
1605
  }
1345
1606
  getOrCreateEvaluationCache() {
@@ -1352,213 +1613,8 @@ var QuerySet = class QuerySet {
1352
1613
  async evaluateRows() {
1353
1614
  const compiled = new QueryCompiler(this.executor.meta, this.executor.adapter).compile(this.state);
1354
1615
  const rows = await this.executor.run(compiled);
1355
- const normalizedRows = this.normalizeRowsForSchemaParsing(rows);
1356
- const hydratedRows = await this.hydrateRows(normalizedRows, compiled);
1357
- this.attachRootRecordAccessors(hydratedRows);
1358
- return new QueryResult(hydratedRows);
1359
- }
1360
- normalizeRowsForSchemaParsing(rows) {
1361
- if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
1362
- const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
1363
- if (booleanColumns.length === 0) return [...rows];
1364
- return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
1365
- }
1366
- normalizeHydratedRowsForParserShape(rows) {
1367
- if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
1368
- const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
1369
- if (booleanColumns.length === 0) return [...rows];
1370
- return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
1371
- }
1372
- async hydrateRows(rows, compiled) {
1373
- if (!compiled.hydrationPlan) return rows;
1374
- const hydratedRows = rows.map((row) => ({ ...row }));
1375
- this.attachRootRecordAccessors(hydratedRows);
1376
- const canonicalEntities = /* @__PURE__ */ new Map();
1377
- const queuedJoinPrefetchOwners = /* @__PURE__ */ new Map();
1378
- const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
1379
- for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, canonicalEntities, queuedJoinPrefetchOwners);
1380
- for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, canonicalEntities, compiler);
1381
- for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], canonicalEntities, compiler);
1382
- for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
1383
- return hydratedRows;
1384
- }
1385
- primeManyToManyOwnerCache(owner, relationName, bucket) {
1386
- const existing = owner[relationName];
1387
- if (existing && typeof existing.primePrefetchCache === "function") {
1388
- existing.primePrefetchCache(bucket);
1389
- return;
1390
- }
1391
- owner[relationName] = bucket.slice();
1392
- }
1393
- attachRootRecordAccessors(rows) {
1394
- if (!this.executor.attachPersistedRecordAccessors) return;
1395
- const sourceModelKey = this.executor.meta.modelKey;
1396
- for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
1397
- }
1398
- hydrateJoinNodesForOwner(owner, rawRow, nodes, canonicalEntities, queuedJoinPrefetchOwners) {
1399
- for (const node of nodes) {
1400
- if (!node.join) continue;
1401
- const target = {};
1402
- let hasTargetValue = false;
1403
- for (const [column, alias] of Object.entries(node.join.columns)) {
1404
- const value = rawRow[alias];
1405
- delete rawRow[alias];
1406
- target[column] = this.normalizeColumnValue(node.targetColumns[column], value);
1407
- if (value !== null && value !== void 0) hasTargetValue = true;
1408
- }
1409
- if (!hasTargetValue) {
1410
- owner[node.relationName] = null;
1411
- continue;
1412
- }
1413
- const canonical = this.canonicalizeEntity(node, target, canonicalEntities);
1414
- owner[node.relationName] = canonical;
1415
- for (const childNode of node.prefetchChildren) {
1416
- const queuedOwners = queuedJoinPrefetchOwners?.get(childNode);
1417
- if (queuedOwners) {
1418
- queuedOwners.add(canonical);
1419
- continue;
1420
- }
1421
- queuedJoinPrefetchOwners?.set(childNode, new Set([canonical]));
1422
- }
1423
- this.hydrateJoinNodesForOwner(canonical, rawRow, node.joinChildren, canonicalEntities, queuedJoinPrefetchOwners);
1424
- }
1425
- }
1426
- async hydratePrefetchNode(node, owners, canonicalEntities, compiler) {
1427
- if (owners.length === 0) return;
1428
- const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
1429
- const sourceValues = [...groupedOwners.keys()];
1430
- if (!!!node.throughTable) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
1431
- if (sourceValues.length === 0) return;
1432
- const sourceChunks = this.chunkValues(sourceValues, 500);
1433
- const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
1434
- if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
1435
- const idsByOwner = /* @__PURE__ */ new Map();
1436
- const uniqueTargetIds = /* @__PURE__ */ new Set();
1437
- for (const chunk of sourceChunks) {
1438
- const chunkCompiled = compiler.compilePrefetch(node, chunk);
1439
- const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
1440
- for (const row of throughResult.rows) {
1441
- const ownerId = row[chunkCompiled.ownerAlias];
1442
- const targetId = row[chunkCompiled.targetAlias];
1443
- if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
1444
- const bucket = idsByOwner.get(ownerId) ?? [];
1445
- bucket.push(targetId);
1446
- idsByOwner.set(ownerId, bucket);
1447
- uniqueTargetIds.add(targetId);
1448
- }
1449
- }
1450
- const targets = {};
1451
- const targetIds = [...uniqueTargetIds.values()];
1452
- if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
1453
- const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
1454
- const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
1455
- for (const rawTargetRow of targetResult.rows) {
1456
- const normalized = this.normalizeTargetRow({ targetColumns: compiledPrefetch.targetColumns }, rawTargetRow);
1457
- const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
1458
- this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
1459
- const primaryKey = canonical[node.targetPrimaryKey];
1460
- if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
1461
- }
1462
- }
1463
- const canonicalChildren = /* @__PURE__ */ new Map();
1464
- const handledOwners = /* @__PURE__ */ new Set();
1465
- for (const [ownerId, ids] of idsByOwner.entries()) {
1466
- const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
1467
- for (const owner of groupedOwners.get(ownerId) ?? []) {
1468
- this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
1469
- handledOwners.add(owner);
1470
- }
1471
- for (const child of bucket) canonicalChildren.set(child[node.targetPrimaryKey], child);
1472
- }
1473
- for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
1474
- const childOwners = [...canonicalChildren.values()];
1475
- for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
1476
- return;
1477
- }
1478
- const canonicalChildren = /* @__PURE__ */ new Map();
1479
- for (const chunk of sourceChunks) {
1480
- const chunkCompiled = compiler.compilePrefetch(node, chunk);
1481
- const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
1482
- for (const rawResultRow of result.rows) {
1483
- const normalized = this.normalizeTargetRow(chunkCompiled, rawResultRow);
1484
- const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
1485
- this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
1486
- const key = normalized[chunkCompiled.targetKey];
1487
- if (typeof key !== "string" && typeof key !== "number") continue;
1488
- for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
1489
- else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
1490
- const childPrimaryKey = canonical[node.targetPrimaryKey];
1491
- if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
1492
- }
1493
- }
1494
- const childOwners = [...canonicalChildren.values()];
1495
- for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
1496
- }
1497
- chunkValues(values, size) {
1498
- if (values.length === 0) return [];
1499
- if (values.length <= size) return [Array.from(values)];
1500
- const chunks = [];
1501
- for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
1502
- return chunks;
1503
- }
1504
- groupOwnersByAccessor(owners, accessor) {
1505
- const grouped = /* @__PURE__ */ new Map();
1506
- for (const owner of owners) {
1507
- const key = owner[accessor];
1508
- if (typeof key !== "string" && typeof key !== "number") continue;
1509
- const bucket = grouped.get(key) ?? [];
1510
- bucket.push(owner);
1511
- grouped.set(key, bucket);
1512
- }
1513
- return grouped;
1514
- }
1515
- canonicalizeEntity(node, row, canonicalEntities) {
1516
- const primaryKeyValue = row[node.targetPrimaryKey];
1517
- if (typeof primaryKeyValue !== "string" && typeof primaryKeyValue !== "number") return row;
1518
- const byModel = canonicalEntities.get(node.targetModelKey) ?? /* @__PURE__ */ new Map();
1519
- const existing = byModel.get(primaryKeyValue);
1520
- if (existing) {
1521
- Object.assign(existing, row);
1522
- return existing;
1523
- }
1524
- byModel.set(primaryKeyValue, row);
1525
- canonicalEntities.set(node.targetModelKey, byModel);
1526
- this.executor.attachPersistedRecordAccessors?.(row, node.targetModelKey);
1527
- return row;
1528
- }
1529
- normalizeTargetRow(prefetch, row) {
1530
- if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return row;
1531
- let normalized = null;
1532
- for (const [column, type] of Object.entries(prefetch.targetColumns)) {
1533
- if (!this.isBooleanColumnType(type)) continue;
1534
- const next = this.normalizeSqliteBoolean(row[column]);
1535
- if (next === row[column]) continue;
1536
- normalized ??= { ...row };
1537
- normalized[column] = next;
1538
- }
1539
- return normalized ?? row;
1540
- }
1541
- normalizeColumnValue(columnType, value) {
1542
- return this.executor.adapter.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
1543
- }
1544
- isBooleanColumnType(value) {
1545
- return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
1546
- }
1547
- normalizeSqliteBoolean(value) {
1548
- if (value === 0 || value === "0") return false;
1549
- if (value === 1 || value === "1") return true;
1550
- return value;
1551
- }
1552
- normalizeBooleanColumns(row, columns) {
1553
- let normalized = null;
1554
- for (const column of columns) {
1555
- const current = row[column];
1556
- const next = this.normalizeSqliteBoolean(current);
1557
- if (next === current) continue;
1558
- if (!normalized) normalized = { ...row };
1559
- normalized[column] = next;
1560
- }
1561
- return normalized ?? row;
1616
+ const normalizedRows = this.rowNormalizer.normalizeRootRows(rows, this.executor.meta.columns);
1617
+ return new QueryResult(await this.hydrator.materializeRows(normalizedRows, compiled));
1562
1618
  }
1563
1619
  withoutHydrationState() {
1564
1620
  const { selectRelated: _selectRelated, prefetchRelated: _prefetchRelated, ...rest } = this.state;
@@ -1596,4 +1652,4 @@ var query_exports = /* @__PURE__ */ __exportAll({
1596
1652
  //#endregion
1597
1653
  export { isQNodeLike as a, InternalRelationKind as c, QueryCompiler as d, OrmSqlSafetyAdapter as f, QBuilder as i, QueryResult as l, InternalQNodeType as m, ModelQuerySet as n, domain_exports as o, InternalSqlValidationPlanKind as p, QuerySet as r, TableMetaFactory as s, query_exports as t, compiler_exports as u };
1598
1654
 
1599
- //# sourceMappingURL=query-D_93uktq.js.map
1655
+ //# sourceMappingURL=query-hvcqTb9O.js.map