@danceroutine/tango-orm 1.11.10 → 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.
- package/dist/{QuerySetState-DRL9-rKQ.d.ts → QuerySetState-D9xr3SCH.d.ts} +4 -16
- package/dist/{index-CJlkeldS.d.ts → index-DIL4gXO6.d.ts} +3 -3
- package/dist/{index-CCvYeJt4.d.ts → index-Db_GZk8M.d.ts} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/manager/index.d.ts +3 -3
- package/dist/manager/index.js +2 -2
- package/dist/{manager-GMmYq4lC.js → manager-BwiDQLup.js} +2 -2
- package/dist/{manager-GMmYq4lC.js.map → manager-BwiDQLup.js.map} +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/{query-B-ooqvSG.js → query-hvcqTb9O.js} +276 -214
- package/dist/query-hvcqTb9O.js.map +1 -0
- package/dist/{registerModelObjects-CEqCRlVY.d.ts → registerModelObjects-DpiMWh5n.d.ts} +2 -2
- package/dist/{registerModelObjects-BKGs2c_m.js → registerModelObjects-Dr1YYKt3.js} +2 -2
- package/dist/{registerModelObjects-BKGs2c_m.js.map → registerModelObjects-Dr1YYKt3.js.map} +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +1 -1
- package/package.json +6 -6
- package/dist/query-B-ooqvSG.js.map +0 -1
|
@@ -463,8 +463,9 @@ var QueryCompiler = class QueryCompiler {
|
|
|
463
463
|
].join(", ");
|
|
464
464
|
const whereSQL = whereParts.length ? ` WHERE ${whereParts.join(" AND ")}` : "";
|
|
465
465
|
const orderSQL = ` ORDER BY ${state.order?.length ? state.order.map((order) => `${validatedPlan.orderFields[String(order.by)]} ${order.dir.toUpperCase()}`).join(", ") : `${table}.${validatedPlan.meta.pk} ASC`}`;
|
|
466
|
-
const
|
|
467
|
-
const
|
|
466
|
+
const hasOffset = state.offset !== void 0;
|
|
467
|
+
const limitSQL = state.limit !== void 0 ? ` LIMIT ${state.limit}` : hasOffset && this.adapter.dialect === InternalDialect.SQLITE ? " LIMIT -1" : "";
|
|
468
|
+
const offsetSQL = state.offset !== void 0 ? ` OFFSET ${state.offset}` : "";
|
|
468
469
|
return {
|
|
469
470
|
sql: `SELECT ${select} FROM ${table}${joinCollection.joins.length ? ` ${joinCollection.joins.join(" ")}` : ""}${whereSQL}${orderSQL}${limitSQL}${offsetSQL}`,
|
|
470
471
|
params,
|
|
@@ -1103,6 +1104,263 @@ var QBuilder = class QBuilder {
|
|
|
1103
1104
|
}
|
|
1104
1105
|
};
|
|
1105
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
|
|
1106
1364
|
//#region src/query/QuerySet.ts
|
|
1107
1365
|
/**
|
|
1108
1366
|
* Django-inspired query builder for constructing and executing database queries.
|
|
@@ -1131,9 +1389,13 @@ var QuerySet = class QuerySet {
|
|
|
1131
1389
|
static BRAND = "tango.orm.query_set";
|
|
1132
1390
|
__tangoBrand = QuerySet.BRAND;
|
|
1133
1391
|
evaluationCache;
|
|
1392
|
+
hydrator;
|
|
1393
|
+
rowNormalizer;
|
|
1134
1394
|
constructor(executor, state = {}) {
|
|
1135
1395
|
this.executor = executor;
|
|
1136
1396
|
this.state = state;
|
|
1397
|
+
this.rowNormalizer = createQueryRowNormalizerStrategy(executor.adapter.dialect);
|
|
1398
|
+
this.hydrator = new QueryHydrator(executor, this.rowNormalizer);
|
|
1137
1399
|
}
|
|
1138
1400
|
/**
|
|
1139
1401
|
* Narrow an unknown value to `QuerySet`.
|
|
@@ -1162,6 +1424,11 @@ var QuerySet = class QuerySet {
|
|
|
1162
1424
|
};
|
|
1163
1425
|
});
|
|
1164
1426
|
}
|
|
1427
|
+
static validateQueryWindowBound(kind, value) {
|
|
1428
|
+
if (typeof value !== "number") throw new TypeError(`QuerySet.${kind}() expects a number.`);
|
|
1429
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`QuerySet.${kind}() expects a non-negative safe integer.`);
|
|
1430
|
+
return value;
|
|
1431
|
+
}
|
|
1165
1432
|
static invertOrderSpec(order) {
|
|
1166
1433
|
if (!order?.length) return [];
|
|
1167
1434
|
return order.map((spec) => ({
|
|
@@ -1216,7 +1483,7 @@ var QuerySet = class QuerySet {
|
|
|
1216
1483
|
limit(n) {
|
|
1217
1484
|
return this.spawn({
|
|
1218
1485
|
...this.state,
|
|
1219
|
-
limit: n
|
|
1486
|
+
limit: QuerySet.validateQueryWindowBound("limit", n)
|
|
1220
1487
|
});
|
|
1221
1488
|
}
|
|
1222
1489
|
/**
|
|
@@ -1225,7 +1492,7 @@ var QuerySet = class QuerySet {
|
|
|
1225
1492
|
offset(n) {
|
|
1226
1493
|
return this.spawn({
|
|
1227
1494
|
...this.state,
|
|
1228
|
-
offset: n
|
|
1495
|
+
offset: QuerySet.validateQueryWindowBound("offset", n)
|
|
1229
1496
|
});
|
|
1230
1497
|
}
|
|
1231
1498
|
select(fields) {
|
|
@@ -1269,7 +1536,7 @@ var QuerySet = class QuerySet {
|
|
|
1269
1536
|
async fetch(shape) {
|
|
1270
1537
|
const baseResult = await this.getOrCreateEvaluationCache();
|
|
1271
1538
|
if (!shape) return baseResult;
|
|
1272
|
-
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)));
|
|
1273
1540
|
}
|
|
1274
1541
|
/**
|
|
1275
1542
|
* Async iterable surface for `for await (... of queryset)`.
|
|
@@ -1333,7 +1600,7 @@ var QuerySet = class QuerySet {
|
|
|
1333
1600
|
shapeFetchedRow(row, shape) {
|
|
1334
1601
|
if (!shape) return row;
|
|
1335
1602
|
if (typeof shape === "function") return shape(row);
|
|
1336
|
-
const normalizedRow = this.normalizeHydratedRowsForParserShape([row])[0]
|
|
1603
|
+
const normalizedRow = this.rowNormalizer.normalizeHydratedRowsForParserShape([row], this.executor.meta.columns)[0];
|
|
1337
1604
|
return shape.parse(normalizedRow);
|
|
1338
1605
|
}
|
|
1339
1606
|
getOrCreateEvaluationCache() {
|
|
@@ -1346,213 +1613,8 @@ var QuerySet = class QuerySet {
|
|
|
1346
1613
|
async evaluateRows() {
|
|
1347
1614
|
const compiled = new QueryCompiler(this.executor.meta, this.executor.adapter).compile(this.state);
|
|
1348
1615
|
const rows = await this.executor.run(compiled);
|
|
1349
|
-
const normalizedRows = this.
|
|
1350
|
-
|
|
1351
|
-
this.attachRootRecordAccessors(hydratedRows);
|
|
1352
|
-
return new QueryResult(hydratedRows);
|
|
1353
|
-
}
|
|
1354
|
-
normalizeRowsForSchemaParsing(rows) {
|
|
1355
|
-
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1356
|
-
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1357
|
-
if (booleanColumns.length === 0) return [...rows];
|
|
1358
|
-
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
1359
|
-
}
|
|
1360
|
-
normalizeHydratedRowsForParserShape(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
|
-
async hydrateRows(rows, compiled) {
|
|
1367
|
-
if (!compiled.hydrationPlan) return rows;
|
|
1368
|
-
const hydratedRows = rows.map((row) => ({ ...row }));
|
|
1369
|
-
this.attachRootRecordAccessors(hydratedRows);
|
|
1370
|
-
const canonicalEntities = /* @__PURE__ */ new Map();
|
|
1371
|
-
const queuedJoinPrefetchOwners = /* @__PURE__ */ new Map();
|
|
1372
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1373
|
-
for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1374
|
-
for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, canonicalEntities, compiler);
|
|
1375
|
-
for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], canonicalEntities, compiler);
|
|
1376
|
-
for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
|
|
1377
|
-
return hydratedRows;
|
|
1378
|
-
}
|
|
1379
|
-
primeManyToManyOwnerCache(owner, relationName, bucket) {
|
|
1380
|
-
const existing = owner[relationName];
|
|
1381
|
-
if (existing && typeof existing.primePrefetchCache === "function") {
|
|
1382
|
-
existing.primePrefetchCache(bucket);
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
owner[relationName] = bucket.slice();
|
|
1386
|
-
}
|
|
1387
|
-
attachRootRecordAccessors(rows) {
|
|
1388
|
-
if (!this.executor.attachPersistedRecordAccessors) return;
|
|
1389
|
-
const sourceModelKey = this.executor.meta.modelKey;
|
|
1390
|
-
for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
|
|
1391
|
-
}
|
|
1392
|
-
hydrateJoinNodesForOwner(owner, rawRow, nodes, canonicalEntities, queuedJoinPrefetchOwners) {
|
|
1393
|
-
for (const node of nodes) {
|
|
1394
|
-
if (!node.join) continue;
|
|
1395
|
-
const target = {};
|
|
1396
|
-
let hasTargetValue = false;
|
|
1397
|
-
for (const [column, alias] of Object.entries(node.join.columns)) {
|
|
1398
|
-
const value = rawRow[alias];
|
|
1399
|
-
delete rawRow[alias];
|
|
1400
|
-
target[column] = this.normalizeColumnValue(node.targetColumns[column], value);
|
|
1401
|
-
if (value !== null && value !== void 0) hasTargetValue = true;
|
|
1402
|
-
}
|
|
1403
|
-
if (!hasTargetValue) {
|
|
1404
|
-
owner[node.relationName] = null;
|
|
1405
|
-
continue;
|
|
1406
|
-
}
|
|
1407
|
-
const canonical = this.canonicalizeEntity(node, target, canonicalEntities);
|
|
1408
|
-
owner[node.relationName] = canonical;
|
|
1409
|
-
for (const childNode of node.prefetchChildren) {
|
|
1410
|
-
const queuedOwners = queuedJoinPrefetchOwners?.get(childNode);
|
|
1411
|
-
if (queuedOwners) {
|
|
1412
|
-
queuedOwners.add(canonical);
|
|
1413
|
-
continue;
|
|
1414
|
-
}
|
|
1415
|
-
queuedJoinPrefetchOwners?.set(childNode, new Set([canonical]));
|
|
1416
|
-
}
|
|
1417
|
-
this.hydrateJoinNodesForOwner(canonical, rawRow, node.joinChildren, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
async hydratePrefetchNode(node, owners, canonicalEntities, compiler) {
|
|
1421
|
-
if (owners.length === 0) return;
|
|
1422
|
-
const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
|
|
1423
|
-
const sourceValues = [...groupedOwners.keys()];
|
|
1424
|
-
if (!!!node.throughTable) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
|
|
1425
|
-
if (sourceValues.length === 0) return;
|
|
1426
|
-
const sourceChunks = this.chunkValues(sourceValues, 500);
|
|
1427
|
-
const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
|
|
1428
|
-
if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
|
|
1429
|
-
const idsByOwner = /* @__PURE__ */ new Map();
|
|
1430
|
-
const uniqueTargetIds = /* @__PURE__ */ new Set();
|
|
1431
|
-
for (const chunk of sourceChunks) {
|
|
1432
|
-
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1433
|
-
const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
|
|
1434
|
-
for (const row of throughResult.rows) {
|
|
1435
|
-
const ownerId = row[chunkCompiled.ownerAlias];
|
|
1436
|
-
const targetId = row[chunkCompiled.targetAlias];
|
|
1437
|
-
if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
|
|
1438
|
-
const bucket = idsByOwner.get(ownerId) ?? [];
|
|
1439
|
-
bucket.push(targetId);
|
|
1440
|
-
idsByOwner.set(ownerId, bucket);
|
|
1441
|
-
uniqueTargetIds.add(targetId);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
const targets = {};
|
|
1445
|
-
const targetIds = [...uniqueTargetIds.values()];
|
|
1446
|
-
if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
|
|
1447
|
-
const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
|
|
1448
|
-
const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
|
|
1449
|
-
for (const rawTargetRow of targetResult.rows) {
|
|
1450
|
-
const normalized = this.normalizeTargetRow({ targetColumns: compiledPrefetch.targetColumns }, rawTargetRow);
|
|
1451
|
-
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1452
|
-
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1453
|
-
const primaryKey = canonical[node.targetPrimaryKey];
|
|
1454
|
-
if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1457
|
-
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1458
|
-
const handledOwners = /* @__PURE__ */ new Set();
|
|
1459
|
-
for (const [ownerId, ids] of idsByOwner.entries()) {
|
|
1460
|
-
const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
|
|
1461
|
-
for (const owner of groupedOwners.get(ownerId) ?? []) {
|
|
1462
|
-
this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
|
|
1463
|
-
handledOwners.add(owner);
|
|
1464
|
-
}
|
|
1465
|
-
for (const child of bucket) canonicalChildren.set(child[node.targetPrimaryKey], child);
|
|
1466
|
-
}
|
|
1467
|
-
for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
|
|
1468
|
-
const childOwners = [...canonicalChildren.values()];
|
|
1469
|
-
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1470
|
-
return;
|
|
1471
|
-
}
|
|
1472
|
-
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1473
|
-
for (const chunk of sourceChunks) {
|
|
1474
|
-
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1475
|
-
const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
|
|
1476
|
-
for (const rawResultRow of result.rows) {
|
|
1477
|
-
const normalized = this.normalizeTargetRow(chunkCompiled, rawResultRow);
|
|
1478
|
-
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1479
|
-
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1480
|
-
const key = normalized[chunkCompiled.targetKey];
|
|
1481
|
-
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1482
|
-
for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
|
|
1483
|
-
else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
|
|
1484
|
-
const childPrimaryKey = canonical[node.targetPrimaryKey];
|
|
1485
|
-
if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
const childOwners = [...canonicalChildren.values()];
|
|
1489
|
-
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1490
|
-
}
|
|
1491
|
-
chunkValues(values, size) {
|
|
1492
|
-
if (values.length === 0) return [];
|
|
1493
|
-
if (values.length <= size) return [Array.from(values)];
|
|
1494
|
-
const chunks = [];
|
|
1495
|
-
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
|
1496
|
-
return chunks;
|
|
1497
|
-
}
|
|
1498
|
-
groupOwnersByAccessor(owners, accessor) {
|
|
1499
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
1500
|
-
for (const owner of owners) {
|
|
1501
|
-
const key = owner[accessor];
|
|
1502
|
-
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1503
|
-
const bucket = grouped.get(key) ?? [];
|
|
1504
|
-
bucket.push(owner);
|
|
1505
|
-
grouped.set(key, bucket);
|
|
1506
|
-
}
|
|
1507
|
-
return grouped;
|
|
1508
|
-
}
|
|
1509
|
-
canonicalizeEntity(node, row, canonicalEntities) {
|
|
1510
|
-
const primaryKeyValue = row[node.targetPrimaryKey];
|
|
1511
|
-
if (typeof primaryKeyValue !== "string" && typeof primaryKeyValue !== "number") return row;
|
|
1512
|
-
const byModel = canonicalEntities.get(node.targetModelKey) ?? /* @__PURE__ */ new Map();
|
|
1513
|
-
const existing = byModel.get(primaryKeyValue);
|
|
1514
|
-
if (existing) {
|
|
1515
|
-
Object.assign(existing, row);
|
|
1516
|
-
return existing;
|
|
1517
|
-
}
|
|
1518
|
-
byModel.set(primaryKeyValue, row);
|
|
1519
|
-
canonicalEntities.set(node.targetModelKey, byModel);
|
|
1520
|
-
this.executor.attachPersistedRecordAccessors?.(row, node.targetModelKey);
|
|
1521
|
-
return row;
|
|
1522
|
-
}
|
|
1523
|
-
normalizeTargetRow(prefetch, row) {
|
|
1524
|
-
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return row;
|
|
1525
|
-
let normalized = null;
|
|
1526
|
-
for (const [column, type] of Object.entries(prefetch.targetColumns)) {
|
|
1527
|
-
if (!this.isBooleanColumnType(type)) continue;
|
|
1528
|
-
const next = this.normalizeSqliteBoolean(row[column]);
|
|
1529
|
-
if (next === row[column]) continue;
|
|
1530
|
-
normalized ??= { ...row };
|
|
1531
|
-
normalized[column] = next;
|
|
1532
|
-
}
|
|
1533
|
-
return normalized ?? row;
|
|
1534
|
-
}
|
|
1535
|
-
normalizeColumnValue(columnType, value) {
|
|
1536
|
-
return this.executor.adapter.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1537
|
-
}
|
|
1538
|
-
isBooleanColumnType(value) {
|
|
1539
|
-
return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
|
|
1540
|
-
}
|
|
1541
|
-
normalizeSqliteBoolean(value) {
|
|
1542
|
-
if (value === 0 || value === "0") return false;
|
|
1543
|
-
if (value === 1 || value === "1") return true;
|
|
1544
|
-
return value;
|
|
1545
|
-
}
|
|
1546
|
-
normalizeBooleanColumns(row, columns) {
|
|
1547
|
-
let normalized = null;
|
|
1548
|
-
for (const column of columns) {
|
|
1549
|
-
const current = row[column];
|
|
1550
|
-
const next = this.normalizeSqliteBoolean(current);
|
|
1551
|
-
if (next === current) continue;
|
|
1552
|
-
if (!normalized) normalized = { ...row };
|
|
1553
|
-
normalized[column] = next;
|
|
1554
|
-
}
|
|
1555
|
-
return normalized ?? row;
|
|
1616
|
+
const normalizedRows = this.rowNormalizer.normalizeRootRows(rows, this.executor.meta.columns);
|
|
1617
|
+
return new QueryResult(await this.hydrator.materializeRows(normalizedRows, compiled));
|
|
1556
1618
|
}
|
|
1557
1619
|
withoutHydrationState() {
|
|
1558
1620
|
const { selectRelated: _selectRelated, prefetchRelated: _prefetchRelated, ...rest } = this.state;
|
|
@@ -1590,4 +1652,4 @@ var query_exports = /* @__PURE__ */ __exportAll({
|
|
|
1590
1652
|
//#endregion
|
|
1591
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 };
|
|
1592
1654
|
|
|
1593
|
-
//# sourceMappingURL=query-
|
|
1655
|
+
//# sourceMappingURL=query-hvcqTb9O.js.map
|