@danceroutine/tango-orm 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/InternalDialect-ClSaUNso.js +10 -0
- package/dist/InternalDialect-ClSaUNso.js.map +1 -0
- package/dist/PostgresAdapter-CXKdKBG-.js +4 -0
- package/dist/PostgresAdapter-DySFW6vy.js +128 -0
- package/dist/PostgresAdapter-DySFW6vy.js.map +1 -0
- package/dist/{SqliteClient-CjOK9-ki.js → SqliteAdapter-CDdOjRmW.js} +57 -3
- package/dist/SqliteAdapter-CDdOjRmW.js.map +1 -0
- package/dist/SqliteAdapter-mjtXuVTg.js +4 -0
- package/dist/connection/adapters/Adapter.d.ts +32 -1
- package/dist/connection/adapters/dialects/PostgresAdapter.d.ts +5 -6
- package/dist/connection/adapters/dialects/SqliteAdapter.d.ts +4 -6
- package/dist/connection/adapters/index.d.ts +1 -1
- package/dist/connection/index.d.ts +1 -1
- package/dist/connection/index.js +4 -5
- package/dist/{connection-B_K2ZAf7.js → connection-Dmhgx31M.js} +5 -7
- package/dist/{connection-B_K2ZAf7.js.map → connection-Dmhgx31M.js.map} +1 -1
- package/dist/{defaultRuntime-BPK9kWEW.js → defaultRuntime-DzqBQ9Hb.js} +63 -16
- package/dist/defaultRuntime-DzqBQ9Hb.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +11 -12
- package/dist/manager/ManagerLike.d.ts +19 -0
- package/dist/manager/ModelManager.d.ts +45 -2
- package/dist/manager/index.d.ts +6 -0
- package/dist/manager/index.js +8 -7
- package/dist/manager/internal/MutationCompiler.d.ts +14 -6
- package/dist/manager/relations/ManyToManyRelatedManager.d.ts +147 -0
- package/dist/manager/relations/ManyToManyRelatedQuerySet.d.ts +62 -0
- package/dist/manager/relations/MaterializedModelRecord.d.ts +28 -0
- package/dist/manager/relations/index.d.ts +9 -0
- package/dist/manager/relations/internal/ThroughTableManager.d.ts +79 -0
- package/dist/manager-DrDTiCAz.js +24 -0
- package/dist/manager-DrDTiCAz.js.map +1 -0
- package/dist/query/ModelQuerySet.d.ts +20 -0
- package/dist/query/QBuilder.d.ts +3 -3
- package/dist/query/QuerySet.d.ts +58 -18
- package/dist/query/compiler/QueryCompiler.d.ts +13 -4
- package/dist/query/domain/CompiledQuery.d.ts +169 -2
- package/dist/query/domain/FilterInput.d.ts +1 -1
- package/dist/query/domain/FilterKey.d.ts +4 -2
- package/dist/query/domain/QNode.d.ts +4 -4
- package/dist/query/domain/QuerySetState.d.ts +3 -3
- package/dist/query/domain/RelationMeta.d.ts +9 -0
- package/dist/query/domain/RelationTyping.d.ts +47 -0
- package/dist/query/domain/TableMetaFactory.d.ts +1 -14
- package/dist/query/domain/index.d.ts +1 -1
- package/dist/query/domain/internal/InternalPrefetchQueryKind.d.ts +20 -0
- package/dist/query/index.d.ts +1 -0
- package/dist/query/index.js +3 -2
- package/dist/query/internal/isQNodeLike.d.ts +3 -0
- package/dist/query/planning/QueryPlanner.d.ts +1 -1
- package/dist/{query-C6So1r6H.js → query-DUZnBFhf.js} +474 -156
- package/dist/query-DUZnBFhf.js.map +1 -0
- package/dist/registerModelObjects-DxlBfuUN.js +797 -0
- package/dist/registerModelObjects-DxlBfuUN.js.map +1 -0
- package/dist/runtime/TangoRuntime.d.ts +9 -0
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +7 -6
- package/dist/runtime/internal/SqliteDBClientProvider.d.ts +3 -0
- package/dist/{runtime-ByXbpVBS.js → runtime-1H88J3nN.js} +3 -3
- package/dist/runtime-1H88J3nN.js.map +1 -0
- package/dist/transaction/index.js +5 -4
- package/dist/{transaction-Cs0Z9tbW.js → transaction-ZhfDf-f8.js} +2 -2
- package/dist/{transaction-Cs0Z9tbW.js.map → transaction-ZhfDf-f8.js.map} +1 -1
- package/dist/validation/SQLValidationEngine.d.ts +22 -5
- package/dist/validation/SqlValidationPlan.d.ts +5 -4
- package/dist/validation/internal/InternalSqlValidationPlanKind.d.ts +25 -0
- package/dist/validation/internal/InternalValidatedFilterDescriptorKind.d.ts +4 -0
- package/package.json +6 -6
- package/dist/PostgresAdapter-BFdo_nIt.js +0 -4
- package/dist/PostgresAdapter-CMiEpHya.js +0 -49
- package/dist/PostgresAdapter-CMiEpHya.js.map +0 -1
- package/dist/PostgresClient-BQJZfEOT.js +0 -68
- package/dist/PostgresClient-BQJZfEOT.js.map +0 -1
- package/dist/SqliteAdapter-A-P9zUhP.js +0 -4
- package/dist/SqliteAdapter-CeqhyrPC.js +0 -44
- package/dist/SqliteAdapter-CeqhyrPC.js.map +0 -1
- package/dist/SqliteClient-CjOK9-ki.js.map +0 -1
- package/dist/defaultRuntime-BPK9kWEW.js.map +0 -1
- package/dist/manager-C6oJ2tAF.js +0 -13
- package/dist/manager-C6oJ2tAF.js.map +0 -1
- package/dist/query-C6So1r6H.js.map +0 -1
- package/dist/registerModelObjects-BKMpfc4Z.js +0 -263
- package/dist/registerModelObjects-BKMpfc4Z.js.map +0 -1
- package/dist/runtime-ByXbpVBS.js.map +0 -1
|
@@ -1,75 +1,8 @@
|
|
|
1
1
|
import { __export } from "./chunk-DLY2FNSh.js";
|
|
2
|
-
import {
|
|
2
|
+
import { InternalDialect } from "./InternalDialect-ClSaUNso.js";
|
|
3
|
+
import { MultipleObjectsReturned, NotFoundError, SqlSafetyEngine, getLogger, isError } from "@danceroutine/tango-core";
|
|
3
4
|
import { ModelRegistry } from "@danceroutine/tango-schema";
|
|
4
5
|
|
|
5
|
-
//#region src/query/domain/internal/InternalRelationKind.ts
|
|
6
|
-
const InternalRelationKind = {
|
|
7
|
-
HAS_MANY: "hasMany",
|
|
8
|
-
BELONGS_TO: "belongsTo",
|
|
9
|
-
HAS_ONE: "hasOne",
|
|
10
|
-
MANY_TO_MANY: "manyToMany"
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region src/query/domain/TableMetaFactory.ts
|
|
15
|
-
var TableMetaFactory = class TableMetaFactory {
|
|
16
|
-
static create(model) {
|
|
17
|
-
const owner = model.metadata.key ? ModelRegistry.getOwner(model) : undefined;
|
|
18
|
-
const cache = new Map();
|
|
19
|
-
return TableMetaFactory.createWithCache(model, owner, cache);
|
|
20
|
-
}
|
|
21
|
-
static createWithCache(model, owner, cache) {
|
|
22
|
-
if (model.metadata.key) {
|
|
23
|
-
const cached = cache.get(model.metadata.key);
|
|
24
|
-
if (cached) return cached;
|
|
25
|
-
}
|
|
26
|
-
const pkField = model.metadata.fields.find((field) => field.primaryKey);
|
|
27
|
-
if (!pkField) throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);
|
|
28
|
-
const tableMeta = {
|
|
29
|
-
modelKey: model.metadata.key,
|
|
30
|
-
table: model.metadata.table,
|
|
31
|
-
pk: pkField.name,
|
|
32
|
-
columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type]))
|
|
33
|
-
};
|
|
34
|
-
if (model.metadata.key) cache.set(model.metadata.key, tableMeta);
|
|
35
|
-
if (!model.metadata.key || !owner) return tableMeta;
|
|
36
|
-
const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);
|
|
37
|
-
if (!relations || relations.size === 0) return tableMeta;
|
|
38
|
-
tableMeta.relations = Object.fromEntries(Array.from(relations.entries()).filter(([, relation]) => relation.capabilities.queryable && relation.capabilities.hydratable).map(([name, relation]) => {
|
|
39
|
-
const targetModel = owner.getByKey(relation.targetModelKey);
|
|
40
|
-
const targetMeta = TableMetaFactory.createWithCache(targetModel, owner, cache);
|
|
41
|
-
const { queryable, hydratable } = relation.capabilities;
|
|
42
|
-
const isSingleRelation = relation.kind === InternalRelationKind.BELONGS_TO || relation.kind === InternalRelationKind.HAS_ONE;
|
|
43
|
-
const sourceKey = relation.kind === InternalRelationKind.BELONGS_TO ? relation.localFieldName : relation.targetFieldName;
|
|
44
|
-
const targetKey = relation.kind === InternalRelationKind.BELONGS_TO ? relation.targetFieldName : relation.localFieldName;
|
|
45
|
-
const targetColumns = Object.fromEntries(targetModel.metadata.fields.map((field) => [field.name, field.type]));
|
|
46
|
-
const capabilities = {
|
|
47
|
-
queryable,
|
|
48
|
-
hydratable,
|
|
49
|
-
joinable: isSingleRelation && queryable && hydratable,
|
|
50
|
-
prefetchable: queryable && hydratable
|
|
51
|
-
};
|
|
52
|
-
return [name, {
|
|
53
|
-
edgeId: relation.edgeId,
|
|
54
|
-
sourceModelKey: relation.sourceModelKey,
|
|
55
|
-
targetModelKey: relation.targetModelKey,
|
|
56
|
-
kind: relation.kind,
|
|
57
|
-
cardinality: isSingleRelation ? "single" : "many",
|
|
58
|
-
capabilities,
|
|
59
|
-
table: targetModel.metadata.table,
|
|
60
|
-
sourceKey,
|
|
61
|
-
targetKey,
|
|
62
|
-
targetPrimaryKey: targetMeta.pk,
|
|
63
|
-
targetColumns,
|
|
64
|
-
alias: relation.alias,
|
|
65
|
-
targetMeta
|
|
66
|
-
}];
|
|
67
|
-
}));
|
|
68
|
-
return tableMeta;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
//#endregion
|
|
73
6
|
//#region src/query/domain/RelationMeta.ts
|
|
74
7
|
const InternalRelationHydrationLoadMode = {
|
|
75
8
|
JOIN: "join",
|
|
@@ -77,10 +10,10 @@ const InternalRelationHydrationLoadMode = {
|
|
|
77
10
|
};
|
|
78
11
|
|
|
79
12
|
//#endregion
|
|
80
|
-
//#region src/query/domain/internal/
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
13
|
+
//#region src/query/domain/internal/InternalPrefetchQueryKind.ts
|
|
14
|
+
const InternalPrefetchQueryKind = {
|
|
15
|
+
DIRECT: "direct",
|
|
16
|
+
MANY_TO_MANY: "manyToMany"
|
|
84
17
|
};
|
|
85
18
|
|
|
86
19
|
//#endregion
|
|
@@ -110,6 +43,22 @@ const InternalLookupType = {
|
|
|
110
43
|
IENDSWITH: "iendswith"
|
|
111
44
|
};
|
|
112
45
|
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/validation/internal/InternalSqlValidationPlanKind.ts
|
|
48
|
+
const InternalSqlValidationPlanKind = {
|
|
49
|
+
SELECT: "select",
|
|
50
|
+
INSERT: "insert",
|
|
51
|
+
UPDATE: "update",
|
|
52
|
+
DELETE: "delete"
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/validation/internal/InternalValidatedFilterDescriptorKind.ts
|
|
57
|
+
const InternalValidatedFilterDescriptorKind = {
|
|
58
|
+
COLUMN: "column",
|
|
59
|
+
RELATION: "relation"
|
|
60
|
+
};
|
|
61
|
+
|
|
113
62
|
//#endregion
|
|
114
63
|
//#region src/validation/OrmSqlSafetyAdapter.ts
|
|
115
64
|
const ALLOWED_LOOKUPS = Object.values(InternalLookupType);
|
|
@@ -121,35 +70,35 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
121
70
|
}
|
|
122
71
|
validate(plan) {
|
|
123
72
|
switch (plan.kind) {
|
|
124
|
-
case
|
|
73
|
+
case InternalSqlValidationPlanKind.SELECT: {
|
|
125
74
|
const meta = this.validateTableMeta(plan.meta, plan.relationNames ?? []);
|
|
126
75
|
return {
|
|
127
|
-
kind:
|
|
76
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
128
77
|
meta,
|
|
129
78
|
selectFields: Object.fromEntries((plan.selectFields ?? []).map((field) => [field, `${meta.table}.${this.resolveColumn(meta, field)}`])),
|
|
130
|
-
filterKeys: Object.fromEntries((plan.filterKeys ?? []).map((rawKey) => [rawKey, this.validateFilterKey(meta, rawKey)])),
|
|
79
|
+
filterKeys: Object.fromEntries((plan.filterKeys ?? []).map((rawKey) => [rawKey, this.validateFilterKey(meta, plan.meta, rawKey)])),
|
|
131
80
|
orderFields: Object.fromEntries((plan.orderFields ?? []).map((field) => [field, `${meta.table}.${this.resolveColumn(meta, field)}`])),
|
|
132
81
|
relations: Object.fromEntries((plan.relationNames ?? []).map((relationName) => [relationName, this.resolveRelation(meta, relationName)]))
|
|
133
82
|
};
|
|
134
83
|
}
|
|
135
|
-
case
|
|
84
|
+
case InternalSqlValidationPlanKind.INSERT: {
|
|
136
85
|
const meta = this.validateTableMeta(plan.meta);
|
|
137
86
|
return {
|
|
138
|
-
kind:
|
|
87
|
+
kind: InternalSqlValidationPlanKind.INSERT,
|
|
139
88
|
meta,
|
|
140
89
|
writeKeys: plan.writeKeys.map((key) => this.resolveColumn(meta, key))
|
|
141
90
|
};
|
|
142
91
|
}
|
|
143
|
-
case
|
|
92
|
+
case InternalSqlValidationPlanKind.UPDATE: {
|
|
144
93
|
const meta = this.validateTableMeta(plan.meta);
|
|
145
94
|
return {
|
|
146
|
-
kind:
|
|
95
|
+
kind: InternalSqlValidationPlanKind.UPDATE,
|
|
147
96
|
meta,
|
|
148
97
|
writeKeys: plan.writeKeys.map((key) => this.resolveColumn(meta, key))
|
|
149
98
|
};
|
|
150
99
|
}
|
|
151
|
-
case
|
|
152
|
-
kind:
|
|
100
|
+
case InternalSqlValidationPlanKind.DELETE: return {
|
|
101
|
+
kind: InternalSqlValidationPlanKind.DELETE,
|
|
153
102
|
meta: this.validateTableMeta(plan.meta)
|
|
154
103
|
};
|
|
155
104
|
}
|
|
@@ -203,6 +152,26 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
203
152
|
role: "relationTargetPrimaryKey",
|
|
204
153
|
value: relation.targetKey
|
|
205
154
|
},
|
|
155
|
+
{
|
|
156
|
+
key: "targetPrimaryKey",
|
|
157
|
+
role: "relationTargetPrimaryKey",
|
|
158
|
+
value: relation.targetPrimaryKey
|
|
159
|
+
},
|
|
160
|
+
...relation.throughTable ? [{
|
|
161
|
+
key: "throughTable",
|
|
162
|
+
role: "table",
|
|
163
|
+
value: relation.throughTable
|
|
164
|
+
}] : [],
|
|
165
|
+
...relation.throughSourceKey ? [{
|
|
166
|
+
key: "throughSourceKey",
|
|
167
|
+
role: "column",
|
|
168
|
+
value: relation.throughSourceKey
|
|
169
|
+
}] : [],
|
|
170
|
+
...relation.throughTargetKey ? [{
|
|
171
|
+
key: "throughTargetKey",
|
|
172
|
+
role: "column",
|
|
173
|
+
value: relation.throughTargetKey
|
|
174
|
+
}] : [],
|
|
206
175
|
...Object.keys(relation.targetColumns).map((column) => ({
|
|
207
176
|
key: `targetColumn:${column}`,
|
|
208
177
|
role: "column",
|
|
@@ -215,24 +184,62 @@ var OrmSqlSafetyAdapter = class OrmSqlSafetyAdapter {
|
|
|
215
184
|
alias: validated.identifiers.alias.value,
|
|
216
185
|
sourceKey: this.resolveColumn(meta, relation.sourceKey),
|
|
217
186
|
targetKey: validated.identifiers.targetKey.value,
|
|
218
|
-
|
|
187
|
+
targetPrimaryKey: validated.identifiers.targetPrimaryKey.value,
|
|
188
|
+
targetColumns: Object.fromEntries(Object.keys(relation.targetColumns).map((column) => [validated.identifiers[`targetColumn:${column}`].value, relation.targetColumns[column]])),
|
|
189
|
+
throughTable: validated.identifiers.throughTable?.value,
|
|
190
|
+
throughSourceKey: validated.identifiers.throughSourceKey?.value,
|
|
191
|
+
throughTargetKey: validated.identifiers.throughTargetKey?.value
|
|
219
192
|
};
|
|
220
193
|
}
|
|
221
|
-
validateFilterKey(meta, rawKey) {
|
|
194
|
+
validateFilterKey(meta, rawMeta, rawKey) {
|
|
222
195
|
const segments = rawKey.split("__");
|
|
223
|
-
if (segments.length
|
|
224
|
-
const
|
|
225
|
-
const
|
|
196
|
+
if (segments.length === 0 || segments.some((segment) => segment.length === 0)) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
197
|
+
const lookupToken = segments.at(-1);
|
|
198
|
+
const hasExplicitLookup = ALLOWED_LOOKUPS.includes(lookupToken);
|
|
199
|
+
const lookup = hasExplicitLookup ? lookupToken : InternalLookupType.EXACT;
|
|
200
|
+
const pathSegments = hasExplicitLookup ? segments.slice(0, -1) : segments;
|
|
201
|
+
if (pathSegments.length === 0) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
226
202
|
const validated = this.engine.validate({ lookupTokens: [{
|
|
227
203
|
key: rawKey,
|
|
228
204
|
lookup,
|
|
229
205
|
allowed: ALLOWED_LOOKUPS
|
|
230
206
|
}] });
|
|
207
|
+
if (pathSegments.length === 1) {
|
|
208
|
+
const field$1 = pathSegments[0];
|
|
209
|
+
return {
|
|
210
|
+
kind: InternalValidatedFilterDescriptorKind.COLUMN,
|
|
211
|
+
rawKey,
|
|
212
|
+
field: field$1,
|
|
213
|
+
lookup: validated.lookupTokens[rawKey].lookup,
|
|
214
|
+
qualifiedColumn: `${meta.table}.${this.resolveColumn(meta, field$1)}`
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const rootSegment = pathSegments[0];
|
|
218
|
+
const hasRootColumn = rootSegment in rawMeta.columns;
|
|
219
|
+
const hasRootRelation = rootSegment in (rawMeta.relations ?? {});
|
|
220
|
+
if (!hasExplicitLookup && hasRootColumn && !hasRootRelation) throw new Error(`Invalid SQL lookup key: '${rawKey}'.`);
|
|
221
|
+
const field = pathSegments.at(-1);
|
|
222
|
+
const relationSegments = pathSegments.slice(0, -1);
|
|
223
|
+
const relationChain = [];
|
|
224
|
+
let currentValidatedMeta = meta;
|
|
225
|
+
let currentRawMeta = rawMeta;
|
|
226
|
+
for (const relationName of relationSegments) {
|
|
227
|
+
const relation = currentRawMeta.relations?.[relationName];
|
|
228
|
+
if (!relation) throw new Error(`Unknown relation '${relationName}' for table '${currentValidatedMeta.table}'.`);
|
|
229
|
+
if (!relation.targetMeta) throw new Error(`Relation '${relationName}' for table '${currentValidatedMeta.table}' is missing target metadata.`);
|
|
230
|
+
const validatedRelation = this.validateRelationMeta(currentValidatedMeta, relationName, currentRawMeta.relations);
|
|
231
|
+
relationChain.push(validatedRelation);
|
|
232
|
+
currentRawMeta = relation.targetMeta;
|
|
233
|
+
currentValidatedMeta = this.validateTableMeta(currentRawMeta);
|
|
234
|
+
}
|
|
231
235
|
return {
|
|
236
|
+
kind: InternalValidatedFilterDescriptorKind.RELATION,
|
|
232
237
|
rawKey,
|
|
233
238
|
field,
|
|
234
239
|
lookup: validated.lookupTokens[rawKey].lookup,
|
|
235
|
-
|
|
240
|
+
relationPath: relationSegments.join("__"),
|
|
241
|
+
relationChain,
|
|
242
|
+
terminalColumn: this.resolveColumn(currentValidatedMeta, field)
|
|
236
243
|
};
|
|
237
244
|
}
|
|
238
245
|
resolveColumn(meta, field) {
|
|
@@ -287,18 +294,15 @@ var QueryPlanner = class QueryPlanner {
|
|
|
287
294
|
let currentMeta = this.meta;
|
|
288
295
|
let currentChildren = rootChildren;
|
|
289
296
|
let builtPath = "";
|
|
290
|
-
let containsCollection = false;
|
|
291
297
|
for (const segment of segments) {
|
|
292
298
|
const relation = currentMeta.relations?.[segment];
|
|
293
299
|
if (!relation) throw new Error(`Unknown relation path '${relationPath}' for table '${currentMeta.table}'.`);
|
|
294
300
|
if (segment in currentMeta.columns && relation.sourceKey !== segment) throw new Error(`Relation path '${relationPath}' collides with an existing field on table '${currentMeta.table}'.`);
|
|
295
|
-
if (relation.kind === InternalRelationKind.MANY_TO_MANY) throw new Error(`Relation path '${relationPath}' uses unsupported many-to-many hydration.`);
|
|
296
301
|
if (!relation.capabilities.queryable || !relation.capabilities.hydratable) throw new Error(`Relation path '${relationPath}' cannot be hydrated.`);
|
|
297
302
|
if (mode === "select") {
|
|
298
303
|
if (relation.cardinality !== InternalRelationHydrationCardinality.SINGLE || !relation.capabilities.joinable) throw new Error(`Relation path '${relationPath}' cannot be loaded with selectRelated(...).`);
|
|
299
304
|
} else if (relation.cardinality === InternalRelationHydrationCardinality.MANY) {
|
|
300
305
|
if (!relation.capabilities.prefetchable) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
301
|
-
containsCollection = true;
|
|
302
306
|
} else if (!relation.capabilities.joinable) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
303
307
|
const targetMeta = relation.targetMeta;
|
|
304
308
|
if (!targetMeta) throw new Error(`Relation path '${relationPath}' is missing target metadata.`);
|
|
@@ -317,7 +321,6 @@ var QueryPlanner = class QueryPlanner {
|
|
|
317
321
|
currentChildren = nextNode.children;
|
|
318
322
|
currentMeta = targetMeta;
|
|
319
323
|
}
|
|
320
|
-
if (mode === "prefetch" && !containsCollection) throw new Error(`Relation path '${relationPath}' cannot be loaded with prefetchRelated(...).`);
|
|
321
324
|
}
|
|
322
325
|
buildPlannedChildren(children) {
|
|
323
326
|
const joinNodes = [];
|
|
@@ -353,9 +356,11 @@ const sqlSafetyAdapter = new OrmSqlSafetyAdapter();
|
|
|
353
356
|
var QueryCompiler = class QueryCompiler {
|
|
354
357
|
static BRAND = "tango.orm.query_compiler";
|
|
355
358
|
__tangoBrand = QueryCompiler.BRAND;
|
|
356
|
-
|
|
359
|
+
placeholders;
|
|
360
|
+
constructor(meta, adapter) {
|
|
357
361
|
this.meta = meta;
|
|
358
|
-
this.
|
|
362
|
+
this.adapter = adapter;
|
|
363
|
+
this.placeholders = adapter.placeholders;
|
|
359
364
|
}
|
|
360
365
|
static isQueryCompiler(value) {
|
|
361
366
|
return typeof value === "object" && value !== null && value.__tangoBrand === QueryCompiler.BRAND;
|
|
@@ -363,7 +368,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
363
368
|
compile(state) {
|
|
364
369
|
const hydrationPlan = new QueryPlanner(this.meta).plan(state);
|
|
365
370
|
const validatedPlan = sqlSafetyAdapter.validate({
|
|
366
|
-
kind:
|
|
371
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
367
372
|
meta: this.meta,
|
|
368
373
|
selectFields: state.select?.map(String),
|
|
369
374
|
filterKeys: this.collectStateFilterKeys(state),
|
|
@@ -437,7 +442,8 @@ var QueryCompiler = class QueryCompiler {
|
|
|
437
442
|
};
|
|
438
443
|
}
|
|
439
444
|
compilePrefetch(node, sourceValues) {
|
|
440
|
-
|
|
445
|
+
if (node.throughTable && node.throughSourceKey && node.throughTargetKey) return this.compileManyToManyPrefetch(node, sourceValues);
|
|
446
|
+
const placeholders = this.placeholders.list(sourceValues.length);
|
|
441
447
|
const validatedTarget = this.validatePrefetchTarget(node);
|
|
442
448
|
const baseAlias = this.buildPrefetchBaseAlias(node.relationPath);
|
|
443
449
|
const joinCollection = {
|
|
@@ -447,12 +453,58 @@ var QueryCompiler = class QueryCompiler {
|
|
|
447
453
|
for (const joinChild of node.joinChildren) this.collectNestedJoinSql(joinChild, baseAlias, validatedTarget.columns, joinCollection);
|
|
448
454
|
const baseSelects = Object.keys(validatedTarget.columns).map((column) => `${baseAlias}.${column} AS ${column}`);
|
|
449
455
|
return {
|
|
456
|
+
kind: InternalPrefetchQueryKind.DIRECT,
|
|
450
457
|
sql: `SELECT ${[...baseSelects, ...joinCollection.selects].join(", ")} FROM ${validatedTarget.table} ${baseAlias}${joinCollection.joins.length ? ` ${joinCollection.joins.join(" ")}` : ""} WHERE ${baseAlias}.${validatedTarget.targetKey} IN (${placeholders}) ORDER BY ${baseAlias}.${validatedTarget.targetKey} ASC, ${baseAlias}.${validatedTarget.primaryKey} ASC`,
|
|
451
458
|
params: sourceValues,
|
|
452
459
|
targetKey: validatedTarget.targetKey,
|
|
453
460
|
targetColumns: validatedTarget.columns
|
|
454
461
|
};
|
|
455
462
|
}
|
|
463
|
+
compileManyToManyTargets(node, targetIds) {
|
|
464
|
+
const placeholders = this.placeholders.list(targetIds.length);
|
|
465
|
+
const validatedTarget = this.validatePrefetchTarget(node);
|
|
466
|
+
const baseAlias = this.buildPrefetchBaseAlias(node.relationPath);
|
|
467
|
+
const joinCollection = {
|
|
468
|
+
selects: [],
|
|
469
|
+
joins: []
|
|
470
|
+
};
|
|
471
|
+
for (const joinChild of node.joinChildren) this.collectNestedJoinSql(joinChild, baseAlias, validatedTarget.columns, joinCollection);
|
|
472
|
+
const baseSelects = Object.keys(validatedTarget.columns).map((column) => `${baseAlias}.${column} AS ${column}`);
|
|
473
|
+
return {
|
|
474
|
+
sql: `SELECT ${[...baseSelects, ...joinCollection.selects].join(", ")} FROM ${validatedTarget.table} ${baseAlias}${joinCollection.joins.length ? ` ${joinCollection.joins.join(" ")}` : ""} WHERE ${baseAlias}.${validatedTarget.primaryKey} IN (${placeholders}) ORDER BY ${baseAlias}.${validatedTarget.primaryKey} ASC`,
|
|
475
|
+
params: targetIds
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
compileManyToManyPrefetch(node, sourceValues) {
|
|
479
|
+
const placeholders = this.placeholders.list(sourceValues.length);
|
|
480
|
+
const throughValidated = sqlSafetyAdapter.validate({
|
|
481
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
482
|
+
meta: {
|
|
483
|
+
table: node.throughTable,
|
|
484
|
+
pk: node.throughSourceKey,
|
|
485
|
+
columns: {
|
|
486
|
+
[node.throughSourceKey]: node.throughSourceColumnType ?? "int",
|
|
487
|
+
[node.throughTargetKey]: node.throughTargetColumnType ?? "int"
|
|
488
|
+
}
|
|
489
|
+
},
|
|
490
|
+
filterKeys: [node.throughSourceKey, node.throughTargetKey],
|
|
491
|
+
relationNames: []
|
|
492
|
+
});
|
|
493
|
+
const ownerAlias = this.validateInternalAlias("__tango_m2m_owner");
|
|
494
|
+
const targetAlias = this.validateInternalAlias("__tango_m2m_target");
|
|
495
|
+
const throughSourceColumn = throughValidated.filterKeys[node.throughSourceKey].field;
|
|
496
|
+
const throughTargetColumn = throughValidated.filterKeys[node.throughTargetKey].field;
|
|
497
|
+
return {
|
|
498
|
+
kind: InternalPrefetchQueryKind.MANY_TO_MANY,
|
|
499
|
+
throughSql: `SELECT ${throughValidated.meta.table}.${throughSourceColumn} AS ${ownerAlias}, ${throughValidated.meta.table}.${throughTargetColumn} AS ${targetAlias} FROM ${throughValidated.meta.table} WHERE ${throughValidated.meta.table}.${throughSourceColumn} IN (${placeholders}) ORDER BY ${throughValidated.meta.table}.${throughSourceColumn} ASC, ${throughValidated.meta.table}.${throughTargetColumn} ASC`,
|
|
500
|
+
throughParams: sourceValues,
|
|
501
|
+
ownerAlias,
|
|
502
|
+
targetAlias,
|
|
503
|
+
targetTable: node.targetTable,
|
|
504
|
+
targetPrimaryKey: node.targetPrimaryKey,
|
|
505
|
+
targetColumns: node.targetColumns
|
|
506
|
+
};
|
|
507
|
+
}
|
|
456
508
|
compileHydrationNode(node, context) {
|
|
457
509
|
const validatedRelation = this.validateHydrationRelation(context.ownerMeta, node.relationName);
|
|
458
510
|
const targetColumns = validatedRelation.targetColumns;
|
|
@@ -496,6 +548,11 @@ var QueryCompiler = class QueryCompiler {
|
|
|
496
548
|
targetKey: validatedRelation.targetKey,
|
|
497
549
|
targetTable: validatedRelation.table,
|
|
498
550
|
targetPrimaryKey: node.relationEdge.targetPrimaryKey,
|
|
551
|
+
throughTable: node.relationEdge.throughTable,
|
|
552
|
+
throughSourceKey: node.relationEdge.throughSourceKey,
|
|
553
|
+
throughTargetKey: node.relationEdge.throughTargetKey,
|
|
554
|
+
throughSourceColumnType: node.relationEdge.throughSourceColumnType,
|
|
555
|
+
throughTargetColumnType: node.relationEdge.throughTargetColumnType,
|
|
499
556
|
targetColumns,
|
|
500
557
|
provenance: node.provenance,
|
|
501
558
|
joinChildren: compiledJoinChildren,
|
|
@@ -505,7 +562,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
505
562
|
}
|
|
506
563
|
validateHydrationRelation(ownerMeta, relationName) {
|
|
507
564
|
return sqlSafetyAdapter.validate({
|
|
508
|
-
kind:
|
|
565
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
509
566
|
meta: ownerMeta,
|
|
510
567
|
relationNames: [relationName]
|
|
511
568
|
}).relations[relationName];
|
|
@@ -519,7 +576,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
519
576
|
validatePrefetchTarget(node) {
|
|
520
577
|
try {
|
|
521
578
|
const validated = sqlSafetyAdapter.validate({
|
|
522
|
-
kind:
|
|
579
|
+
kind: InternalSqlValidationPlanKind.SELECT,
|
|
523
580
|
meta: {
|
|
524
581
|
table: node.targetTable,
|
|
525
582
|
pk: node.targetPrimaryKey,
|
|
@@ -570,6 +627,9 @@ var QueryCompiler = class QueryCompiler {
|
|
|
570
627
|
buildPrefetchSourceAlias(relationPath, sourceKey) {
|
|
571
628
|
return this.assertInternalAliasDoesNotCollide(`__tango_prefetch_${this.sanitizeRelationPath(relationPath)}_${sourceKey}`);
|
|
572
629
|
}
|
|
630
|
+
buildFilterAlias(relationPath, suffix) {
|
|
631
|
+
return this.assertInternalAliasDoesNotCollide(`__tango_filter_${this.sanitizeRelationPath(relationPath)}_${suffix}`);
|
|
632
|
+
}
|
|
573
633
|
sanitizeRelationPath(relationPath) {
|
|
574
634
|
return relationPath.replace(/[^a-zA-Z0-9]+/g, "_");
|
|
575
635
|
}
|
|
@@ -594,7 +654,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
594
654
|
const { parts, params } = entries.reduce((accumulator, [key, value]) => {
|
|
595
655
|
const descriptor = filterKeys[String(key)];
|
|
596
656
|
const idx = paramIndex + accumulator.params.length;
|
|
597
|
-
const clause = this.lookupToSQL(descriptor.qualifiedColumn, descriptor.lookup, value, idx);
|
|
657
|
+
const clause = descriptor.kind === InternalValidatedFilterDescriptorKind.COLUMN ? this.lookupToSQL(descriptor.qualifiedColumn, descriptor.lookup, value, idx) : this.compileRelationFilter(descriptor, value, idx);
|
|
598
658
|
accumulator.parts.push(clause.sql);
|
|
599
659
|
accumulator.params.push(...clause.params);
|
|
600
660
|
return accumulator;
|
|
@@ -652,8 +712,28 @@ var QueryCompiler = class QueryCompiler {
|
|
|
652
712
|
params: result.params
|
|
653
713
|
};
|
|
654
714
|
}
|
|
715
|
+
compileRelationFilter(descriptor, value, paramIndex) {
|
|
716
|
+
return this.buildRelationFilterExists(this.meta.table, descriptor.relationChain, descriptor.terminalColumn, descriptor.lookup, value, paramIndex, descriptor.relationPath);
|
|
717
|
+
}
|
|
718
|
+
buildRelationFilterExists(ownerAlias, relationChain, terminalColumn, lookup, value, paramIndex, relationPath) {
|
|
719
|
+
const [relation, ...rest] = relationChain;
|
|
720
|
+
if (!relation) throw new Error(`Cannot compile empty relation filter path '${relationPath}'.`);
|
|
721
|
+
const targetAlias = this.buildFilterAlias(relationPath, `target_${relation.alias}_${rest.length}`);
|
|
722
|
+
const targetPredicate = rest.length === 0 ? this.lookupToSQL(`${targetAlias}.${terminalColumn}`, lookup, value, paramIndex) : this.buildRelationFilterExists(targetAlias, rest, terminalColumn, lookup, value, paramIndex, relationPath);
|
|
723
|
+
if (relation.throughTable && relation.throughSourceKey && relation.throughTargetKey) {
|
|
724
|
+
const throughAlias = this.buildFilterAlias(relationPath, `through_${relation.alias}_${rest.length}`);
|
|
725
|
+
return {
|
|
726
|
+
sql: `EXISTS (SELECT 1 FROM ${relation.throughTable} ${throughAlias} INNER JOIN ${relation.table} ${targetAlias} ON ${targetAlias}.${relation.targetKey} = ${throughAlias}.${relation.throughTargetKey} WHERE ${throughAlias}.${relation.throughSourceKey} = ${ownerAlias}.${relation.sourceKey} AND ${targetPredicate.sql})`,
|
|
727
|
+
params: targetPredicate.params
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
sql: `EXISTS (SELECT 1 FROM ${relation.table} ${targetAlias} WHERE ${targetAlias}.${relation.targetKey} = ${ownerAlias}.${relation.sourceKey} AND ${targetPredicate.sql})`,
|
|
732
|
+
params: targetPredicate.params
|
|
733
|
+
};
|
|
734
|
+
}
|
|
655
735
|
lookupToSQL(col, lookup, value, paramIndex) {
|
|
656
|
-
const placeholder = this.
|
|
736
|
+
const placeholder = this.placeholders.at(paramIndex);
|
|
657
737
|
const normalized = this.normalizeParam(value);
|
|
658
738
|
switch (lookup) {
|
|
659
739
|
case InternalLookupType.EXACT:
|
|
@@ -687,7 +767,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
687
767
|
sql: "1=0",
|
|
688
768
|
params: []
|
|
689
769
|
};
|
|
690
|
-
const placeholders = this.
|
|
770
|
+
const placeholders = this.placeholders.listFromOffset(entries.length, paramIndex - 1);
|
|
691
771
|
return {
|
|
692
772
|
sql: `${col} IN (${placeholders})`,
|
|
693
773
|
params: entries
|
|
@@ -702,7 +782,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
702
782
|
params: [`%${value}%`]
|
|
703
783
|
};
|
|
704
784
|
case InternalLookupType.ICONTAINS: {
|
|
705
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
785
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
706
786
|
return {
|
|
707
787
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
708
788
|
params: [`%${String(value).toLowerCase()}%`]
|
|
@@ -713,7 +793,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
713
793
|
params: [`${value}%`]
|
|
714
794
|
};
|
|
715
795
|
case InternalLookupType.ISTARTSWITH: {
|
|
716
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
796
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
717
797
|
return {
|
|
718
798
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
719
799
|
params: [`${String(value).toLowerCase()}%`]
|
|
@@ -724,7 +804,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
724
804
|
params: [`%${value}`]
|
|
725
805
|
};
|
|
726
806
|
case InternalLookupType.IENDSWITH: {
|
|
727
|
-
const lowerCol = this.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
807
|
+
const lowerCol = this.adapter.dialect === InternalDialect.POSTGRES ? `LOWER(${col})` : `${col}`;
|
|
728
808
|
return {
|
|
729
809
|
sql: `${lowerCol} LIKE ${placeholder}`,
|
|
730
810
|
params: [`%${String(value).toLowerCase()}`]
|
|
@@ -734,7 +814,7 @@ var QueryCompiler = class QueryCompiler {
|
|
|
734
814
|
}
|
|
735
815
|
}
|
|
736
816
|
normalizeParam(value) {
|
|
737
|
-
if (this.dialect === InternalDialect.SQLITE && typeof value === "boolean") return value ? 1 : 0;
|
|
817
|
+
if (this.adapter.dialect === InternalDialect.SQLITE && typeof value === "boolean") return value ? 1 : 0;
|
|
738
818
|
return value;
|
|
739
819
|
}
|
|
740
820
|
collectStateFilterKeys(state) {
|
|
@@ -805,6 +885,87 @@ var QueryResult = class QueryResult {
|
|
|
805
885
|
}
|
|
806
886
|
};
|
|
807
887
|
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/query/domain/internal/InternalRelationKind.ts
|
|
890
|
+
const InternalRelationKind = {
|
|
891
|
+
HAS_MANY: "hasMany",
|
|
892
|
+
BELONGS_TO: "belongsTo",
|
|
893
|
+
HAS_ONE: "hasOne",
|
|
894
|
+
MANY_TO_MANY: "manyToMany"
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
//#endregion
|
|
898
|
+
//#region src/query/domain/TableMetaFactory.ts
|
|
899
|
+
var TableMetaFactory = class TableMetaFactory {
|
|
900
|
+
static create(model) {
|
|
901
|
+
const owner = model.metadata.key ? ModelRegistry.getOwner(model) : undefined;
|
|
902
|
+
const cache = new Map();
|
|
903
|
+
return TableMetaFactory.createWithCache(model, owner, cache);
|
|
904
|
+
}
|
|
905
|
+
static createWithCache(model, owner, cache) {
|
|
906
|
+
if (model.metadata.key) {
|
|
907
|
+
const cached = cache.get(model.metadata.key);
|
|
908
|
+
if (cached) return cached;
|
|
909
|
+
}
|
|
910
|
+
const pkField = model.metadata.fields.find((field) => field.primaryKey);
|
|
911
|
+
if (!pkField) throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);
|
|
912
|
+
const tableMeta = {
|
|
913
|
+
modelKey: model.metadata.key,
|
|
914
|
+
table: model.metadata.table,
|
|
915
|
+
pk: pkField.name,
|
|
916
|
+
columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type]))
|
|
917
|
+
};
|
|
918
|
+
if (model.metadata.key) cache.set(model.metadata.key, tableMeta);
|
|
919
|
+
if (!model.metadata.key || !owner) return tableMeta;
|
|
920
|
+
const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);
|
|
921
|
+
if (!relations || relations.size === 0) return tableMeta;
|
|
922
|
+
tableMeta.relations = Object.fromEntries(Array.from(relations.entries()).filter(([, relation]) => relation.capabilities.queryable && relation.capabilities.hydratable).map(([name, relation]) => {
|
|
923
|
+
const targetModel = owner.getByKey(relation.targetModelKey);
|
|
924
|
+
const targetMeta = TableMetaFactory.createWithCache(targetModel, owner, cache);
|
|
925
|
+
const { queryable, hydratable } = relation.capabilities;
|
|
926
|
+
const isSingleRelation = relation.kind === InternalRelationKind.BELONGS_TO || relation.kind === InternalRelationKind.HAS_ONE;
|
|
927
|
+
const targetColumns = Object.fromEntries(targetModel.metadata.fields.map((field) => [field.name, field.type]));
|
|
928
|
+
let throughSourceColumnType;
|
|
929
|
+
let throughTargetColumnType;
|
|
930
|
+
if (relation.kind === InternalRelationKind.MANY_TO_MANY && relation.throughModelKey && relation.throughSourceKey && relation.throughTargetKey) {
|
|
931
|
+
const throughModel = owner.getByKey(relation.throughModelKey);
|
|
932
|
+
throughSourceColumnType = throughModel?.metadata.fields.find((field) => field.name === relation.throughSourceKey)?.type;
|
|
933
|
+
throughTargetColumnType = throughModel?.metadata.fields.find((field) => field.name === relation.throughTargetKey)?.type;
|
|
934
|
+
}
|
|
935
|
+
const capabilities = {
|
|
936
|
+
queryable,
|
|
937
|
+
hydratable,
|
|
938
|
+
joinable: isSingleRelation && queryable && hydratable,
|
|
939
|
+
prefetchable: queryable && hydratable
|
|
940
|
+
};
|
|
941
|
+
const sourceKey = relation.kind === InternalRelationKind.MANY_TO_MANY ? tableMeta.pk : relation.kind === InternalRelationKind.BELONGS_TO ? relation.localFieldName : relation.targetFieldName;
|
|
942
|
+
const targetKey = relation.kind === InternalRelationKind.MANY_TO_MANY ? targetMeta.pk : relation.kind === InternalRelationKind.BELONGS_TO ? relation.targetFieldName : relation.localFieldName;
|
|
943
|
+
return [name, {
|
|
944
|
+
edgeId: relation.edgeId,
|
|
945
|
+
sourceModelKey: relation.sourceModelKey,
|
|
946
|
+
targetModelKey: relation.targetModelKey,
|
|
947
|
+
kind: relation.kind,
|
|
948
|
+
cardinality: isSingleRelation ? "single" : "many",
|
|
949
|
+
capabilities,
|
|
950
|
+
table: targetModel.metadata.table,
|
|
951
|
+
sourceKey,
|
|
952
|
+
targetKey,
|
|
953
|
+
throughTable: relation.throughTable,
|
|
954
|
+
throughModelKey: relation.throughModelKey,
|
|
955
|
+
throughSourceKey: relation.throughSourceKey,
|
|
956
|
+
throughTargetKey: relation.throughTargetKey,
|
|
957
|
+
throughSourceColumnType,
|
|
958
|
+
throughTargetColumnType,
|
|
959
|
+
targetPrimaryKey: targetMeta.pk,
|
|
960
|
+
targetColumns,
|
|
961
|
+
alias: relation.alias,
|
|
962
|
+
targetMeta
|
|
963
|
+
}];
|
|
964
|
+
}));
|
|
965
|
+
return tableMeta;
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
|
|
808
969
|
//#endregion
|
|
809
970
|
//#region src/query/domain/index.ts
|
|
810
971
|
var domain_exports = {};
|
|
@@ -821,6 +982,19 @@ const InternalDirection = {
|
|
|
821
982
|
DESC: "desc"
|
|
822
983
|
};
|
|
823
984
|
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region src/query/internal/isQNodeLike.ts
|
|
987
|
+
function isQNodeLike(value) {
|
|
988
|
+
if (typeof value !== "object" || value === null || "__tangoBrand" in value) return false;
|
|
989
|
+
switch (value.kind) {
|
|
990
|
+
case InternalQNodeType.ATOM: return "where" in value;
|
|
991
|
+
case InternalQNodeType.AND:
|
|
992
|
+
case InternalQNodeType.OR: return Array.isArray(value.nodes);
|
|
993
|
+
case InternalQNodeType.NOT: return "node" in value;
|
|
994
|
+
default: return false;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
824
998
|
//#endregion
|
|
825
999
|
//#region src/query/QBuilder.ts
|
|
826
1000
|
var QBuilder = class QBuilder {
|
|
@@ -860,7 +1034,7 @@ var QBuilder = class QBuilder {
|
|
|
860
1034
|
};
|
|
861
1035
|
}
|
|
862
1036
|
static wrapNode(input) {
|
|
863
|
-
if (input
|
|
1037
|
+
if (isQNodeLike(input)) return input;
|
|
864
1038
|
return {
|
|
865
1039
|
kind: InternalQNodeType.ATOM,
|
|
866
1040
|
where: input
|
|
@@ -885,17 +1059,45 @@ var QuerySet = class QuerySet {
|
|
|
885
1059
|
return typeof value === "object" && value !== null && value.__tangoBrand === QuerySet.BRAND;
|
|
886
1060
|
}
|
|
887
1061
|
/**
|
|
1062
|
+
* Translate user-facing order tokens like `'name'` or `'-createdAt'` into
|
|
1063
|
+
* the internal `OrderSpec` array used by `QuerySetState`.
|
|
1064
|
+
*
|
|
1065
|
+
* Exposed as `protected` so subclasses can compose the same parse logic
|
|
1066
|
+
* when they need to return their own concrete type from `orderBy` without
|
|
1067
|
+
* reaching into a base-class instance's protected state.
|
|
1068
|
+
*/
|
|
1069
|
+
static buildOrderSpecs(tokens) {
|
|
1070
|
+
return tokens.map((t) => {
|
|
1071
|
+
const str = String(t);
|
|
1072
|
+
if (str.startsWith("-")) return {
|
|
1073
|
+
by: str.slice(1),
|
|
1074
|
+
dir: InternalDirection.DESC
|
|
1075
|
+
};
|
|
1076
|
+
return {
|
|
1077
|
+
by: t,
|
|
1078
|
+
dir: InternalDirection.ASC
|
|
1079
|
+
};
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
static invertOrderSpec(order) {
|
|
1083
|
+
if (!order?.length) return [];
|
|
1084
|
+
return order.map((spec) => ({
|
|
1085
|
+
by: spec.by,
|
|
1086
|
+
dir: spec.dir === InternalDirection.ASC ? InternalDirection.DESC : InternalDirection.ASC
|
|
1087
|
+
}));
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
888
1090
|
* Add a filter expression to the query.
|
|
889
1091
|
*
|
|
890
1092
|
* Multiple `filter()` calls are composed with `AND`.
|
|
891
1093
|
*/
|
|
892
1094
|
filter(q) {
|
|
893
|
-
const wrapped = q
|
|
1095
|
+
const wrapped = isQNodeLike(q) ? q : {
|
|
894
1096
|
kind: InternalQNodeType.ATOM,
|
|
895
1097
|
where: q
|
|
896
1098
|
};
|
|
897
1099
|
const merged = this.state.q ? QBuilder.and(this.state.q, wrapped) : wrapped;
|
|
898
|
-
return
|
|
1100
|
+
return this.spawn({
|
|
899
1101
|
...this.state,
|
|
900
1102
|
q: merged
|
|
901
1103
|
});
|
|
@@ -906,12 +1108,12 @@ var QuerySet = class QuerySet {
|
|
|
906
1108
|
* Exclusions are translated to `NOT (...)` predicates.
|
|
907
1109
|
*/
|
|
908
1110
|
exclude(q) {
|
|
909
|
-
const wrapped = q
|
|
1111
|
+
const wrapped = isQNodeLike(q) ? q : {
|
|
910
1112
|
kind: InternalQNodeType.ATOM,
|
|
911
1113
|
where: q
|
|
912
1114
|
};
|
|
913
1115
|
const excludes = [...this.state.excludes ?? [], wrapped];
|
|
914
|
-
return
|
|
1116
|
+
return this.spawn({
|
|
915
1117
|
...this.state,
|
|
916
1118
|
excludes
|
|
917
1119
|
});
|
|
@@ -920,27 +1122,16 @@ var QuerySet = class QuerySet {
|
|
|
920
1122
|
* Apply ordering tokens such as `'name'` or `'-createdAt'`.
|
|
921
1123
|
*/
|
|
922
1124
|
orderBy(...tokens) {
|
|
923
|
-
|
|
924
|
-
const str = String(t);
|
|
925
|
-
if (str.startsWith("-")) return {
|
|
926
|
-
by: str.slice(1),
|
|
927
|
-
dir: InternalDirection.DESC
|
|
928
|
-
};
|
|
929
|
-
return {
|
|
930
|
-
by: t,
|
|
931
|
-
dir: InternalDirection.ASC
|
|
932
|
-
};
|
|
933
|
-
});
|
|
934
|
-
return new QuerySet(this.executor, {
|
|
1125
|
+
return this.spawn({
|
|
935
1126
|
...this.state,
|
|
936
|
-
order
|
|
1127
|
+
order: QuerySet.buildOrderSpecs(tokens)
|
|
937
1128
|
});
|
|
938
1129
|
}
|
|
939
1130
|
/**
|
|
940
1131
|
* Limit the maximum number of rows returned.
|
|
941
1132
|
*/
|
|
942
1133
|
limit(n) {
|
|
943
|
-
return
|
|
1134
|
+
return this.spawn({
|
|
944
1135
|
...this.state,
|
|
945
1136
|
limit: n
|
|
946
1137
|
});
|
|
@@ -949,13 +1140,13 @@ var QuerySet = class QuerySet {
|
|
|
949
1140
|
* Skip the first `n` rows.
|
|
950
1141
|
*/
|
|
951
1142
|
offset(n) {
|
|
952
|
-
return
|
|
1143
|
+
return this.spawn({
|
|
953
1144
|
...this.state,
|
|
954
1145
|
offset: n
|
|
955
1146
|
});
|
|
956
1147
|
}
|
|
957
1148
|
select(fields) {
|
|
958
|
-
return
|
|
1149
|
+
return this.spawn({
|
|
959
1150
|
...this.state,
|
|
960
1151
|
select: [...fields]
|
|
961
1152
|
});
|
|
@@ -970,7 +1161,7 @@ var QuerySet = class QuerySet {
|
|
|
970
1161
|
* path keys for applications that keep the app-local registry current.
|
|
971
1162
|
*/
|
|
972
1163
|
selectRelated(...rels) {
|
|
973
|
-
return
|
|
1164
|
+
return this.spawn({
|
|
974
1165
|
...this.state,
|
|
975
1166
|
selectRelated: [...rels]
|
|
976
1167
|
});
|
|
@@ -984,11 +1175,14 @@ var QuerySet = class QuerySet {
|
|
|
984
1175
|
* app-local registry current.
|
|
985
1176
|
*/
|
|
986
1177
|
prefetchRelated(...rels) {
|
|
987
|
-
return
|
|
1178
|
+
return this.spawn({
|
|
988
1179
|
...this.state,
|
|
989
1180
|
prefetchRelated: [...rels]
|
|
990
1181
|
});
|
|
991
1182
|
}
|
|
1183
|
+
all() {
|
|
1184
|
+
return this.spawn({ ...this.state });
|
|
1185
|
+
}
|
|
992
1186
|
async fetch(shape) {
|
|
993
1187
|
const baseResult = await this.getOrCreateEvaluationCache();
|
|
994
1188
|
if (!shape) return baseResult;
|
|
@@ -998,9 +1192,10 @@ var QuerySet = class QuerySet {
|
|
|
998
1192
|
/**
|
|
999
1193
|
* Async iterable surface for `for await (... of queryset)`.
|
|
1000
1194
|
*
|
|
1001
|
-
* Evaluates this queryset on first use by awaiting
|
|
1002
|
-
* yields each element from that
|
|
1003
|
-
*
|
|
1195
|
+
* Evaluates this queryset on first use by awaiting `fetch()` without
|
|
1196
|
+
* arguments, then yields each element from that materialized result.
|
|
1197
|
+
* Later async iterations over the same queryset instance reuse the cached
|
|
1198
|
+
* materialized result instead of issuing another database round-trip.
|
|
1004
1199
|
*/
|
|
1005
1200
|
async *[Symbol.asyncIterator]() {
|
|
1006
1201
|
const result = await this.fetch();
|
|
@@ -1012,11 +1207,40 @@ var QuerySet = class QuerySet {
|
|
|
1012
1207
|
for (const row of result) return row;
|
|
1013
1208
|
return null;
|
|
1014
1209
|
}
|
|
1210
|
+
async first(shape) {
|
|
1211
|
+
return this.fetchOne(shape);
|
|
1212
|
+
}
|
|
1213
|
+
async last(shape) {
|
|
1214
|
+
if (this.state.limit !== undefined || this.state.offset !== undefined) {
|
|
1215
|
+
const page = await this.fetch();
|
|
1216
|
+
const row = page.at(-1);
|
|
1217
|
+
if (!row) return null;
|
|
1218
|
+
return this.shapeFetchedRow(row, shape);
|
|
1219
|
+
}
|
|
1220
|
+
const invertedOrder = QuerySet.invertOrderSpec(this.state.order);
|
|
1221
|
+
const effectiveOrder = invertedOrder.length > 0 ? invertedOrder : [{
|
|
1222
|
+
by: this.executor.meta.pk,
|
|
1223
|
+
dir: InternalDirection.DESC
|
|
1224
|
+
}];
|
|
1225
|
+
const qs = this.spawn({
|
|
1226
|
+
...this.state,
|
|
1227
|
+
order: effectiveOrder
|
|
1228
|
+
});
|
|
1229
|
+
return qs.limit(1).fetchOne(shape);
|
|
1230
|
+
}
|
|
1231
|
+
async get(q, shape) {
|
|
1232
|
+
const limited = this.filter(q).limit(2);
|
|
1233
|
+
const page = await limited.fetch();
|
|
1234
|
+
const rows = page.items;
|
|
1235
|
+
if (rows.length === 0) throw new NotFoundError(`${this.executor.meta.table}: no matching record`);
|
|
1236
|
+
if (rows.length > 1) throw new MultipleObjectsReturned(`${this.executor.meta.table}: more than one matching record`);
|
|
1237
|
+
return this.shapeFetchedRow(rows[0], shape);
|
|
1238
|
+
}
|
|
1015
1239
|
/**
|
|
1016
1240
|
* Execute a `COUNT(*)` query for the current filtered state.
|
|
1017
1241
|
*/
|
|
1018
1242
|
async count() {
|
|
1019
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1243
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1020
1244
|
const compiled = compiler.compile(this.withoutHydrationState());
|
|
1021
1245
|
const countQuery = `SELECT COUNT(*) as count FROM (${compiled.sql}) AS tango_count_subquery`;
|
|
1022
1246
|
const rows = await this.executor.client.query(countQuery, compiled.params);
|
|
@@ -1029,6 +1253,12 @@ var QuerySet = class QuerySet {
|
|
|
1029
1253
|
const count = await this.count();
|
|
1030
1254
|
return count > 0;
|
|
1031
1255
|
}
|
|
1256
|
+
shapeFetchedRow(row, shape) {
|
|
1257
|
+
if (!shape) return row;
|
|
1258
|
+
if (typeof shape === "function") return shape(row);
|
|
1259
|
+
const normalizedRow = this.normalizeHydratedRowsForParserShape([row])[0] ?? row;
|
|
1260
|
+
return shape.parse(normalizedRow);
|
|
1261
|
+
}
|
|
1032
1262
|
getOrCreateEvaluationCache() {
|
|
1033
1263
|
if (!this.evaluationCache) this.evaluationCache = this.evaluateRows().catch((error) => {
|
|
1034
1264
|
this.evaluationCache = undefined;
|
|
@@ -1037,15 +1267,23 @@ var QuerySet = class QuerySet {
|
|
|
1037
1267
|
return this.evaluationCache;
|
|
1038
1268
|
}
|
|
1039
1269
|
async evaluateRows() {
|
|
1040
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1270
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1041
1271
|
const compiled = compiler.compile(this.state);
|
|
1042
1272
|
const rows = await this.executor.run(compiled);
|
|
1043
|
-
const
|
|
1273
|
+
const normalizedRows = this.normalizeRowsForSchemaParsing(rows);
|
|
1274
|
+
const hydratedRows = await this.hydrateRows(normalizedRows, compiled);
|
|
1275
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1044
1276
|
const projectedRows = hydratedRows;
|
|
1045
1277
|
return new QueryResult(projectedRows);
|
|
1046
1278
|
}
|
|
1279
|
+
normalizeRowsForSchemaParsing(rows) {
|
|
1280
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1281
|
+
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1282
|
+
if (booleanColumns.length === 0) return [...rows];
|
|
1283
|
+
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
1284
|
+
}
|
|
1047
1285
|
normalizeHydratedRowsForParserShape(rows) {
|
|
1048
|
-
if (this.executor.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1286
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1049
1287
|
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1050
1288
|
if (booleanColumns.length === 0) return [...rows];
|
|
1051
1289
|
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
@@ -1053,15 +1291,29 @@ var QuerySet = class QuerySet {
|
|
|
1053
1291
|
async hydrateRows(rows, compiled) {
|
|
1054
1292
|
if (!compiled.hydrationPlan) return rows;
|
|
1055
1293
|
const hydratedRows = rows.map((row) => ({ ...row }));
|
|
1294
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1056
1295
|
const canonicalEntities = new Map();
|
|
1057
1296
|
const queuedJoinPrefetchOwners = new Map();
|
|
1058
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.
|
|
1297
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1059
1298
|
for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1060
1299
|
for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, canonicalEntities, compiler);
|
|
1061
1300
|
for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], canonicalEntities, compiler);
|
|
1062
1301
|
for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
|
|
1063
1302
|
return hydratedRows;
|
|
1064
1303
|
}
|
|
1304
|
+
primeManyToManyOwnerCache(owner, relationName, bucket) {
|
|
1305
|
+
const existing = owner[relationName];
|
|
1306
|
+
if (existing && typeof existing.primePrefetchCache === "function") {
|
|
1307
|
+
existing.primePrefetchCache(bucket);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
owner[relationName] = bucket.slice();
|
|
1311
|
+
}
|
|
1312
|
+
attachRootRecordAccessors(rows) {
|
|
1313
|
+
if (!this.executor.attachPersistedRecordAccessors) return;
|
|
1314
|
+
const sourceModelKey = this.executor.meta.modelKey;
|
|
1315
|
+
for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
|
|
1316
|
+
}
|
|
1065
1317
|
hydrateJoinNodesForOwner(owner, rawRow, nodes, canonicalEntities, queuedJoinPrefetchOwners) {
|
|
1066
1318
|
for (const node of nodes) {
|
|
1067
1319
|
if (!node.join) continue;
|
|
@@ -1094,28 +1346,81 @@ var QuerySet = class QuerySet {
|
|
|
1094
1346
|
if (owners.length === 0) return;
|
|
1095
1347
|
const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
|
|
1096
1348
|
const sourceValues = [...groupedOwners.keys()];
|
|
1097
|
-
|
|
1349
|
+
const isManyToMany = !!node.throughTable;
|
|
1350
|
+
if (!isManyToMany) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
|
|
1098
1351
|
if (sourceValues.length === 0) return;
|
|
1099
|
-
const
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1352
|
+
const sourceChunks = this.chunkValues(sourceValues, 500);
|
|
1353
|
+
const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
|
|
1354
|
+
if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
|
|
1355
|
+
const idsByOwner = new Map();
|
|
1356
|
+
const uniqueTargetIds = new Set();
|
|
1357
|
+
for (const chunk of sourceChunks) {
|
|
1358
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1359
|
+
const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
|
|
1360
|
+
for (const row of throughResult.rows) {
|
|
1361
|
+
const ownerId = row[chunkCompiled.ownerAlias];
|
|
1362
|
+
const targetId = row[chunkCompiled.targetAlias];
|
|
1363
|
+
if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
|
|
1364
|
+
const bucket = idsByOwner.get(ownerId) ?? [];
|
|
1365
|
+
bucket.push(targetId);
|
|
1366
|
+
idsByOwner.set(ownerId, bucket);
|
|
1367
|
+
uniqueTargetIds.add(targetId);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const targets = {};
|
|
1371
|
+
const targetIds = [...uniqueTargetIds.values()];
|
|
1372
|
+
if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
|
|
1373
|
+
const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
|
|
1374
|
+
const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
|
|
1375
|
+
for (const rawTargetRow of targetResult.rows) {
|
|
1376
|
+
const normalized = this.normalizeTargetRow({ targetColumns: compiledPrefetch.targetColumns }, rawTargetRow);
|
|
1377
|
+
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1378
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1379
|
+
const primaryKey = canonical[node.targetPrimaryKey];
|
|
1380
|
+
if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
const canonicalChildren$1 = new Map();
|
|
1384
|
+
const handledOwners = new Set();
|
|
1385
|
+
for (const [ownerId, ids] of idsByOwner.entries()) {
|
|
1386
|
+
const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
|
|
1387
|
+
for (const owner of groupedOwners.get(ownerId) ?? []) {
|
|
1388
|
+
this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
|
|
1389
|
+
handledOwners.add(owner);
|
|
1390
|
+
}
|
|
1391
|
+
for (const child of bucket) canonicalChildren$1.set(child[node.targetPrimaryKey], child);
|
|
1392
|
+
}
|
|
1393
|
+
for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
|
|
1394
|
+
const childOwners$1 = [...canonicalChildren$1.values()];
|
|
1395
|
+
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners$1, canonicalEntities, compiler);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1102
1398
|
const canonicalChildren = new Map();
|
|
1103
|
-
for (const
|
|
1104
|
-
const
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1399
|
+
for (const chunk of sourceChunks) {
|
|
1400
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1401
|
+
const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
|
|
1402
|
+
for (const rawResultRow of result.rows) {
|
|
1403
|
+
const normalized = this.normalizeTargetRow(chunkCompiled, rawResultRow);
|
|
1404
|
+
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1405
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1406
|
+
const key = normalized[chunkCompiled.targetKey];
|
|
1407
|
+
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1408
|
+
for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
|
|
1409
|
+
else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
|
|
1410
|
+
const childPrimaryKey = canonical[node.targetPrimaryKey];
|
|
1411
|
+
if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
|
|
1412
|
+
}
|
|
1114
1413
|
}
|
|
1115
|
-
for (const [sourceValue, grouped] of groupedTargets.entries()) for (const owner of groupedOwners.get(sourceValue) ?? []) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? grouped : grouped[0];
|
|
1116
1414
|
const childOwners = [...canonicalChildren.values()];
|
|
1117
1415
|
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1118
1416
|
}
|
|
1417
|
+
chunkValues(values, size) {
|
|
1418
|
+
if (values.length === 0) return [];
|
|
1419
|
+
if (values.length <= size) return [Array.from(values)];
|
|
1420
|
+
const chunks = [];
|
|
1421
|
+
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
|
1422
|
+
return chunks;
|
|
1423
|
+
}
|
|
1119
1424
|
groupOwnersByAccessor(owners, accessor) {
|
|
1120
1425
|
const grouped = new Map();
|
|
1121
1426
|
for (const owner of owners) {
|
|
@@ -1138,10 +1443,11 @@ var QuerySet = class QuerySet {
|
|
|
1138
1443
|
}
|
|
1139
1444
|
byModel.set(primaryKeyValue, row);
|
|
1140
1445
|
canonicalEntities.set(node.targetModelKey, byModel);
|
|
1446
|
+
this.executor.attachPersistedRecordAccessors?.(row, node.targetModelKey);
|
|
1141
1447
|
return row;
|
|
1142
1448
|
}
|
|
1143
1449
|
normalizeTargetRow(prefetch, row) {
|
|
1144
|
-
if (this.executor.dialect !== InternalDialect.SQLITE) return row;
|
|
1450
|
+
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return row;
|
|
1145
1451
|
let normalized = null;
|
|
1146
1452
|
for (const [column, type] of Object.entries(prefetch.targetColumns)) {
|
|
1147
1453
|
if (!this.isBooleanColumnType(type)) continue;
|
|
@@ -1153,7 +1459,7 @@ var QuerySet = class QuerySet {
|
|
|
1153
1459
|
return normalized ?? row;
|
|
1154
1460
|
}
|
|
1155
1461
|
normalizeColumnValue(columnType, value) {
|
|
1156
|
-
return this.executor.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1462
|
+
return this.executor.adapter.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1157
1463
|
}
|
|
1158
1464
|
isBooleanColumnType(value) {
|
|
1159
1465
|
return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
|
|
@@ -1180,10 +1486,22 @@ var QuerySet = class QuerySet {
|
|
|
1180
1486
|
}
|
|
1181
1487
|
};
|
|
1182
1488
|
|
|
1489
|
+
//#endregion
|
|
1490
|
+
//#region src/query/ModelQuerySet.ts
|
|
1491
|
+
var ModelQuerySet = class ModelQuerySet extends QuerySet {
|
|
1492
|
+
constructor(executor, state = {}) {
|
|
1493
|
+
super(executor, state);
|
|
1494
|
+
}
|
|
1495
|
+
spawn(state) {
|
|
1496
|
+
return new ModelQuerySet(this.executor, state);
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1183
1500
|
//#endregion
|
|
1184
1501
|
//#region src/query/index.ts
|
|
1185
1502
|
var query_exports = {};
|
|
1186
1503
|
__export(query_exports, {
|
|
1504
|
+
ModelQuerySet: () => ModelQuerySet,
|
|
1187
1505
|
Q: () => QBuilder,
|
|
1188
1506
|
QBuilder: () => QBuilder,
|
|
1189
1507
|
QueryCompiler: () => QueryCompiler,
|
|
@@ -1194,5 +1512,5 @@ __export(query_exports, {
|
|
|
1194
1512
|
});
|
|
1195
1513
|
|
|
1196
1514
|
//#endregion
|
|
1197
|
-
export { OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, query_exports };
|
|
1198
|
-
//# sourceMappingURL=query-
|
|
1515
|
+
export { InternalQNodeType, InternalRelationKind, InternalSqlValidationPlanKind, ModelQuerySet, OrmSqlSafetyAdapter, QBuilder, QueryCompiler, QueryResult, QuerySet, TableMetaFactory, compiler_exports, domain_exports, isQNodeLike, query_exports };
|
|
1516
|
+
//# sourceMappingURL=query-DUZnBFhf.js.map
|