@farming-labs/orm-sql 0.0.10 → 0.0.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/index.cjs +300 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +301 -30
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
30
|
var import_node_crypto = require("crypto");
|
|
31
31
|
var import_orm = require("@farming-labs/orm");
|
|
32
|
+
var nativeNodeIdentity = /* @__PURE__ */ Symbol("nativeNodeIdentity");
|
|
32
33
|
var manifestCache = /* @__PURE__ */ new WeakMap();
|
|
33
34
|
function getManifest(schema) {
|
|
34
35
|
const cached = manifestCache.get(schema);
|
|
@@ -77,6 +78,9 @@ function encodeValue(field, dialect, value) {
|
|
|
77
78
|
if (dialect === "postgres") return Boolean(value);
|
|
78
79
|
return value ? 1 : 0;
|
|
79
80
|
}
|
|
81
|
+
if (field.kind === "integer") {
|
|
82
|
+
return Number(value);
|
|
83
|
+
}
|
|
80
84
|
if (field.kind === "datetime") {
|
|
81
85
|
if (value instanceof Date) {
|
|
82
86
|
if (dialect === "mysql") {
|
|
@@ -86,6 +90,12 @@ function encodeValue(field, dialect, value) {
|
|
|
86
90
|
}
|
|
87
91
|
return value;
|
|
88
92
|
}
|
|
93
|
+
if (field.kind === "json") {
|
|
94
|
+
if (dialect === "postgres") {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
return JSON.stringify(value);
|
|
98
|
+
}
|
|
89
99
|
return value;
|
|
90
100
|
}
|
|
91
101
|
function normalizeNaiveSqlDate(value) {
|
|
@@ -129,6 +139,19 @@ function decodeValue(field, dialect, value) {
|
|
|
129
139
|
}
|
|
130
140
|
return new Date(String(value));
|
|
131
141
|
}
|
|
142
|
+
if (field.kind === "integer") {
|
|
143
|
+
return typeof value === "number" ? value : Number(value);
|
|
144
|
+
}
|
|
145
|
+
if (field.kind === "json") {
|
|
146
|
+
if (typeof value === "string") {
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(value);
|
|
149
|
+
} catch {
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
132
155
|
return value;
|
|
133
156
|
}
|
|
134
157
|
function decodeRow(model, dialect, row) {
|
|
@@ -146,39 +169,36 @@ function mergeWhere(...clauses) {
|
|
|
146
169
|
AND: defined
|
|
147
170
|
};
|
|
148
171
|
}
|
|
149
|
-
function
|
|
150
|
-
return !!value && typeof value === "object" && !(value instanceof Date) && !Array.isArray(value);
|
|
151
|
-
}
|
|
152
|
-
function compileFieldFilter(model, fieldName, filter, dialect, state) {
|
|
172
|
+
function compileFieldFilter(model, fieldName, filter, dialect, state, tableAlias = model.table) {
|
|
153
173
|
const field = model.fields[fieldName];
|
|
154
174
|
if (!field) {
|
|
155
175
|
throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
|
|
156
176
|
}
|
|
157
|
-
const column = `${quoteIdentifier(
|
|
158
|
-
|
|
177
|
+
const column = `${quoteIdentifier(tableAlias, dialect)}.${quoteIdentifier(field.column, dialect)}`;
|
|
178
|
+
const createValueExpression = (value) => {
|
|
179
|
+
const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, value));
|
|
180
|
+
if (field.kind === "json" && dialect === "mysql") {
|
|
181
|
+
return `cast(${placeholder} as json)`;
|
|
182
|
+
}
|
|
183
|
+
return placeholder;
|
|
184
|
+
};
|
|
185
|
+
if (!(0, import_orm.isOperatorFilterObject)(filter)) {
|
|
159
186
|
if (filter === null) return `${column} is null`;
|
|
160
|
-
|
|
161
|
-
return `${column} = ${placeholder}`;
|
|
187
|
+
return `${column} = ${createValueExpression(filter)}`;
|
|
162
188
|
}
|
|
163
189
|
const clauses = [];
|
|
164
190
|
if ("eq" in filter) {
|
|
165
191
|
if (filter.eq === null) {
|
|
166
192
|
clauses.push(`${column} is null`);
|
|
167
193
|
} else {
|
|
168
|
-
|
|
169
|
-
clauses.push(`${column} = ${placeholder}`);
|
|
194
|
+
clauses.push(`${column} = ${createValueExpression(filter.eq)}`);
|
|
170
195
|
}
|
|
171
196
|
}
|
|
172
197
|
if ("not" in filter) {
|
|
173
198
|
if (filter.not === null) {
|
|
174
199
|
clauses.push(`${column} is not null`);
|
|
175
200
|
} else {
|
|
176
|
-
|
|
177
|
-
dialect,
|
|
178
|
-
state,
|
|
179
|
-
encodeValue(field, dialect, filter.not)
|
|
180
|
-
);
|
|
181
|
-
clauses.push(`${column} <> ${placeholder}`);
|
|
201
|
+
clauses.push(`${column} <> ${createValueExpression(filter.not)}`);
|
|
182
202
|
}
|
|
183
203
|
}
|
|
184
204
|
if ("in" in filter) {
|
|
@@ -186,9 +206,7 @@ function compileFieldFilter(model, fieldName, filter, dialect, state) {
|
|
|
186
206
|
if (!values.length) {
|
|
187
207
|
clauses.push("1 = 0");
|
|
188
208
|
} else {
|
|
189
|
-
const placeholders = values.map(
|
|
190
|
-
(value) => createPlaceholder(dialect, state, encodeValue(field, dialect, value))
|
|
191
|
-
);
|
|
209
|
+
const placeholders = values.map((value) => createValueExpression(value));
|
|
192
210
|
clauses.push(`${column} in (${placeholders.join(", ")})`);
|
|
193
211
|
}
|
|
194
212
|
}
|
|
@@ -218,39 +236,39 @@ function compileFieldFilter(model, fieldName, filter, dialect, state) {
|
|
|
218
236
|
if (clauses.length === 1) return clauses[0];
|
|
219
237
|
return `(${clauses.join(" and ")})`;
|
|
220
238
|
}
|
|
221
|
-
function compileWhere(model, where, dialect, state) {
|
|
239
|
+
function compileWhere(model, where, dialect, state, tableAlias = model.table) {
|
|
222
240
|
if (!where) return void 0;
|
|
223
241
|
const clauses = [];
|
|
224
242
|
for (const [key, value] of Object.entries(where)) {
|
|
225
243
|
if (key === "AND") {
|
|
226
244
|
const items = Array.isArray(value) ? value : [];
|
|
227
245
|
if (!items.length) continue;
|
|
228
|
-
const nested = items.map((item) => compileWhere(model, item, dialect, state)).filter(Boolean).map((item) => `(${item})`);
|
|
246
|
+
const nested = items.map((item) => compileWhere(model, item, dialect, state, tableAlias)).filter(Boolean).map((item) => `(${item})`);
|
|
229
247
|
if (nested.length) clauses.push(nested.join(" and "));
|
|
230
248
|
continue;
|
|
231
249
|
}
|
|
232
250
|
if (key === "OR") {
|
|
233
251
|
const items = Array.isArray(value) ? value : [];
|
|
234
252
|
if (!items.length) continue;
|
|
235
|
-
const nested = items.map((item) => compileWhere(model, item, dialect, state)).filter(Boolean).map((item) => `(${item})`);
|
|
253
|
+
const nested = items.map((item) => compileWhere(model, item, dialect, state, tableAlias)).filter(Boolean).map((item) => `(${item})`);
|
|
236
254
|
if (nested.length) clauses.push(`(${nested.join(" or ")})`);
|
|
237
255
|
continue;
|
|
238
256
|
}
|
|
239
257
|
if (key === "NOT") {
|
|
240
|
-
const nested = compileWhere(model, value, dialect, state);
|
|
258
|
+
const nested = compileWhere(model, value, dialect, state, tableAlias);
|
|
241
259
|
if (nested) clauses.push(`not (${nested})`);
|
|
242
260
|
continue;
|
|
243
261
|
}
|
|
244
|
-
clauses.push(compileFieldFilter(model, key, value, dialect, state));
|
|
262
|
+
clauses.push(compileFieldFilter(model, key, value, dialect, state, tableAlias));
|
|
245
263
|
}
|
|
246
264
|
if (!clauses.length) return void 0;
|
|
247
265
|
return clauses.join(" and ");
|
|
248
266
|
}
|
|
249
|
-
function compileOrderBy(model, orderBy, dialect) {
|
|
267
|
+
function compileOrderBy(model, orderBy, dialect, tableAlias = model.table) {
|
|
250
268
|
if (!orderBy) return "";
|
|
251
269
|
const parts = Object.entries(orderBy).filter(([fieldName]) => fieldName in model.fields).map(([fieldName, direction]) => {
|
|
252
270
|
const field = model.fields[fieldName];
|
|
253
|
-
return `${quoteIdentifier(
|
|
271
|
+
return `${quoteIdentifier(tableAlias, dialect)}.${quoteIdentifier(field.column, dialect)} ${direction === "desc" ? "desc" : "asc"}`;
|
|
254
272
|
});
|
|
255
273
|
if (!parts.length) return "";
|
|
256
274
|
return ` order by ${parts.join(", ")}`;
|
|
@@ -273,13 +291,14 @@ function compilePagination(dialect, take, skip) {
|
|
|
273
291
|
}
|
|
274
292
|
function buildSelectStatement(model, dialect, args) {
|
|
275
293
|
const state = { params: [] };
|
|
294
|
+
const tableAlias = args.tableAlias ?? model.table;
|
|
276
295
|
const selectList = Object.values(model.fields).map(
|
|
277
|
-
(field) => `${quoteIdentifier(
|
|
296
|
+
(field) => `${quoteIdentifier(tableAlias, dialect)}.${quoteIdentifier(field.column, dialect)} as ${quoteIdentifier(field.name, dialect)}`
|
|
278
297
|
);
|
|
279
|
-
let sql = `select ${selectList.join(", ")} from ${quoteIdentifier(model.table, dialect)}`;
|
|
280
|
-
const where = compileWhere(model, args.where, dialect, state);
|
|
298
|
+
let sql = `select ${selectList.join(", ")} from ${quoteIdentifier(model.table, dialect)} as ${quoteIdentifier(tableAlias, dialect)}`;
|
|
299
|
+
const where = compileWhere(model, args.where, dialect, state, tableAlias);
|
|
281
300
|
if (where) sql += ` where ${where}`;
|
|
282
|
-
sql += compileOrderBy(model, args.orderBy, dialect);
|
|
301
|
+
sql += compileOrderBy(model, args.orderBy, dialect, tableAlias);
|
|
283
302
|
sql += compilePagination(dialect, args.take, args.skip);
|
|
284
303
|
return { sql, params: state.params };
|
|
285
304
|
}
|
|
@@ -581,6 +600,10 @@ function createMysqlPoolAdapter(pool) {
|
|
|
581
600
|
}
|
|
582
601
|
function createSqlDriver(adapter) {
|
|
583
602
|
async function loadRows(schema, modelName, args) {
|
|
603
|
+
const nativeRows = await loadRowsWithNativeJoins(schema, modelName, args);
|
|
604
|
+
if (nativeRows) {
|
|
605
|
+
return nativeRows;
|
|
606
|
+
}
|
|
584
607
|
const manifest = getManifest(schema);
|
|
585
608
|
const model = manifest.models[modelName];
|
|
586
609
|
const statement = buildSelectStatement(model, adapter.dialect, args);
|
|
@@ -606,6 +629,253 @@ function createSqlDriver(adapter) {
|
|
|
606
629
|
const row = result.rows[0];
|
|
607
630
|
return row ? decodeRow(model, adapter.dialect, row) : null;
|
|
608
631
|
}
|
|
632
|
+
function createNativePresenceAlias(model, alias, includeAllScalars, selectedScalarKeys) {
|
|
633
|
+
const occupiedAliases = new Set(
|
|
634
|
+
(includeAllScalars ? Object.keys(model.fields) : selectedScalarKeys).map(
|
|
635
|
+
(fieldName) => `${alias}__${fieldName}`
|
|
636
|
+
)
|
|
637
|
+
);
|
|
638
|
+
let candidate = `${alias}__orm_presence`;
|
|
639
|
+
let suffix = 0;
|
|
640
|
+
while (occupiedAliases.has(candidate)) {
|
|
641
|
+
suffix += 1;
|
|
642
|
+
candidate = `${alias}__orm_presence_${suffix}`;
|
|
643
|
+
}
|
|
644
|
+
return candidate;
|
|
645
|
+
}
|
|
646
|
+
function buildNativeJoinPlan(schema, modelName, select, aliasState) {
|
|
647
|
+
const manifest = getManifest(schema);
|
|
648
|
+
const model = manifest.models[modelName];
|
|
649
|
+
const alias = `t${aliasState.next++}`;
|
|
650
|
+
const entries = select ? Object.entries(select) : [];
|
|
651
|
+
const selectedScalarKeys = select ? entries.filter(([key, value]) => key in model.fields && value === true).map(([key]) => key) : Object.keys(model.fields);
|
|
652
|
+
const node = {
|
|
653
|
+
modelName,
|
|
654
|
+
model,
|
|
655
|
+
alias,
|
|
656
|
+
presenceAlias: createNativePresenceAlias(model, alias, !select, selectedScalarKeys),
|
|
657
|
+
includeAllScalars: !select,
|
|
658
|
+
selectedScalarKeys,
|
|
659
|
+
children: []
|
|
660
|
+
};
|
|
661
|
+
for (const [key, value] of entries) {
|
|
662
|
+
if (value === void 0 || !(key in schema.models[modelName].relations)) continue;
|
|
663
|
+
const relation = schema.models[modelName].relations[key];
|
|
664
|
+
const relationArgs = value === true ? {} : value;
|
|
665
|
+
if (relationArgs.where !== void 0 || relationArgs.orderBy !== void 0 || relationArgs.take !== void 0 || relationArgs.skip !== void 0) {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
const child = buildNativeJoinPlan(
|
|
669
|
+
schema,
|
|
670
|
+
relation.target,
|
|
671
|
+
relationArgs.select,
|
|
672
|
+
aliasState
|
|
673
|
+
);
|
|
674
|
+
if (!child) return null;
|
|
675
|
+
child.relationName = key;
|
|
676
|
+
child.relationKind = relation.kind;
|
|
677
|
+
if (relation.kind === "belongsTo") {
|
|
678
|
+
const sourceField = model.fields[relation.foreignKey];
|
|
679
|
+
if (!sourceField) return null;
|
|
680
|
+
const targetReference = parseReference(sourceField.references);
|
|
681
|
+
const targetFieldName = targetReference?.field ?? identityField(manifest.models[relation.target]).name;
|
|
682
|
+
const targetField = child.model.fields[targetFieldName];
|
|
683
|
+
if (!targetField) return null;
|
|
684
|
+
child.sourceField = sourceField;
|
|
685
|
+
child.targetField = targetField;
|
|
686
|
+
} else if (relation.kind === "hasOne" || relation.kind === "hasMany") {
|
|
687
|
+
const targetForeignField = child.model.fields[relation.foreignKey];
|
|
688
|
+
if (!targetForeignField) return null;
|
|
689
|
+
const sourceReference = parseReference(targetForeignField.references);
|
|
690
|
+
const sourceFieldName = sourceReference?.field ?? identityField(manifest.models[modelName]).name;
|
|
691
|
+
const sourceField = model.fields[sourceFieldName];
|
|
692
|
+
if (!sourceField) return null;
|
|
693
|
+
child.sourceField = sourceField;
|
|
694
|
+
child.targetField = targetForeignField;
|
|
695
|
+
} else {
|
|
696
|
+
const throughModel = manifest.models[relation.through];
|
|
697
|
+
const throughFromField = throughModel.fields[relation.from];
|
|
698
|
+
const throughToField = throughModel.fields[relation.to];
|
|
699
|
+
if (!throughFromField || !throughToField) return null;
|
|
700
|
+
const throughFromReference = parseReference(throughFromField.references);
|
|
701
|
+
const throughToReference = parseReference(throughToField.references);
|
|
702
|
+
const sourceFieldName = throughFromReference?.field ?? identityField(manifest.models[modelName]).name;
|
|
703
|
+
const targetFieldName = throughToReference?.field ?? identityField(child.model).name;
|
|
704
|
+
const sourceField = model.fields[sourceFieldName];
|
|
705
|
+
const targetField = child.model.fields[targetFieldName];
|
|
706
|
+
if (!sourceField || !targetField) return null;
|
|
707
|
+
child.sourceField = sourceField;
|
|
708
|
+
child.targetField = targetField;
|
|
709
|
+
child.throughModel = throughModel;
|
|
710
|
+
child.throughAlias = `t${aliasState.next++}`;
|
|
711
|
+
child.throughFromField = throughFromField;
|
|
712
|
+
child.throughToField = throughToField;
|
|
713
|
+
}
|
|
714
|
+
node.children.push(child);
|
|
715
|
+
}
|
|
716
|
+
return node;
|
|
717
|
+
}
|
|
718
|
+
function hasNativeJoinableRelations(schema, modelName, select) {
|
|
719
|
+
if (!select) return false;
|
|
720
|
+
const plan = buildNativeJoinPlan(schema, modelName, select, { next: 0 });
|
|
721
|
+
return !!plan && plan.children.length > 0;
|
|
722
|
+
}
|
|
723
|
+
function collectNativeJoinSelects(node, selectList) {
|
|
724
|
+
const scalarKeys = node.includeAllScalars ? Object.keys(node.model.fields) : node.selectedScalarKeys;
|
|
725
|
+
for (const fieldName of scalarKeys) {
|
|
726
|
+
const field = node.model.fields[fieldName];
|
|
727
|
+
if (!field) continue;
|
|
728
|
+
selectList.push(
|
|
729
|
+
`${quoteIdentifier(node.alias, adapter.dialect)}.${quoteIdentifier(field.column, adapter.dialect)} as ${quoteIdentifier(`${node.alias}__${field.name}`, adapter.dialect)}`
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
const identity = identityField(node.model);
|
|
733
|
+
selectList.push(
|
|
734
|
+
`${quoteIdentifier(node.alias, adapter.dialect)}.${quoteIdentifier(identity.column, adapter.dialect)} as ${quoteIdentifier(node.presenceAlias, adapter.dialect)}`
|
|
735
|
+
);
|
|
736
|
+
for (const child of node.children) {
|
|
737
|
+
collectNativeJoinSelects(child, selectList);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function collectNativeJoinClauses(node, joins) {
|
|
741
|
+
for (const child of node.children) {
|
|
742
|
+
if (child.relationKind === "manyToMany") {
|
|
743
|
+
joins.push(
|
|
744
|
+
` left join ${quoteIdentifier(child.throughModel.table, adapter.dialect)} as ${quoteIdentifier(child.throughAlias, adapter.dialect)} on ${quoteIdentifier(node.alias, adapter.dialect)}.${quoteIdentifier(child.sourceField.column, adapter.dialect)} = ${quoteIdentifier(child.throughAlias, adapter.dialect)}.${quoteIdentifier(child.throughFromField.column, adapter.dialect)}`
|
|
745
|
+
);
|
|
746
|
+
joins.push(
|
|
747
|
+
` left join ${quoteIdentifier(child.model.table, adapter.dialect)} as ${quoteIdentifier(child.alias, adapter.dialect)} on ${quoteIdentifier(child.throughAlias, adapter.dialect)}.${quoteIdentifier(child.throughToField.column, adapter.dialect)} = ${quoteIdentifier(child.alias, adapter.dialect)}.${quoteIdentifier(child.targetField.column, adapter.dialect)}`
|
|
748
|
+
);
|
|
749
|
+
} else {
|
|
750
|
+
const leftColumn = child.relationKind === "belongsTo" ? `${quoteIdentifier(node.alias, adapter.dialect)}.${quoteIdentifier(child.sourceField.column, adapter.dialect)}` : `${quoteIdentifier(child.alias, adapter.dialect)}.${quoteIdentifier(child.targetField.column, adapter.dialect)}`;
|
|
751
|
+
const rightColumn = child.relationKind === "belongsTo" ? `${quoteIdentifier(child.alias, adapter.dialect)}.${quoteIdentifier(child.targetField.column, adapter.dialect)}` : `${quoteIdentifier(node.alias, adapter.dialect)}.${quoteIdentifier(child.sourceField.column, adapter.dialect)}`;
|
|
752
|
+
joins.push(
|
|
753
|
+
` left join ${quoteIdentifier(child.model.table, adapter.dialect)} as ${quoteIdentifier(child.alias, adapter.dialect)} on ${leftColumn} = ${rightColumn}`
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
collectNativeJoinClauses(child, joins);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
function buildNativeJoinRootSource(root, args) {
|
|
760
|
+
const state = { params: [] };
|
|
761
|
+
const sourceAlias = `${root.alias}__src`;
|
|
762
|
+
const selectList = Object.values(root.model.fields).map(
|
|
763
|
+
(field) => `${quoteIdentifier(sourceAlias, adapter.dialect)}.${quoteIdentifier(field.column, adapter.dialect)} as ${quoteIdentifier(field.column, adapter.dialect)}`
|
|
764
|
+
);
|
|
765
|
+
let sql = `select ${selectList.join(", ")} from ${quoteIdentifier(root.model.table, adapter.dialect)} as ${quoteIdentifier(sourceAlias, adapter.dialect)}`;
|
|
766
|
+
const where = compileWhere(root.model, args.where, adapter.dialect, state, sourceAlias);
|
|
767
|
+
if (where) sql += ` where ${where}`;
|
|
768
|
+
sql += compileOrderBy(root.model, args.orderBy, adapter.dialect, sourceAlias);
|
|
769
|
+
sql += compilePagination(adapter.dialect, args.take, args.skip);
|
|
770
|
+
return {
|
|
771
|
+
sql: `(${sql})`,
|
|
772
|
+
params: state.params
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function buildNativeJoinStatement(root, args) {
|
|
776
|
+
const state = { params: [] };
|
|
777
|
+
const selectList = [];
|
|
778
|
+
const joins = [];
|
|
779
|
+
collectNativeJoinSelects(root, selectList);
|
|
780
|
+
collectNativeJoinClauses(root, joins);
|
|
781
|
+
const rootSource = buildNativeJoinRootSource(root, args);
|
|
782
|
+
state.params.push(...rootSource.params);
|
|
783
|
+
let sql = `select ${selectList.join(", ")} from ${rootSource.sql} as ${quoteIdentifier(root.alias, adapter.dialect)}`;
|
|
784
|
+
if (joins.length) sql += joins.join("");
|
|
785
|
+
sql += compileOrderBy(root.model, args.orderBy, adapter.dialect, root.alias);
|
|
786
|
+
return { sql, params: state.params };
|
|
787
|
+
}
|
|
788
|
+
function nodePresenceValue(node, rawRow) {
|
|
789
|
+
return rawRow[node.presenceAlias];
|
|
790
|
+
}
|
|
791
|
+
function projectNativeJoinNode(node, rawRow) {
|
|
792
|
+
if (nodePresenceValue(node, rawRow) == null) {
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
const output = {};
|
|
796
|
+
Object.defineProperty(output, nativeNodeIdentity, {
|
|
797
|
+
value: rawRow[node.presenceAlias],
|
|
798
|
+
enumerable: false,
|
|
799
|
+
configurable: true
|
|
800
|
+
});
|
|
801
|
+
const scalarKeys = node.includeAllScalars ? Object.keys(node.model.fields) : node.selectedScalarKeys;
|
|
802
|
+
for (const fieldName of scalarKeys) {
|
|
803
|
+
const field = node.model.fields[fieldName];
|
|
804
|
+
if (!field) continue;
|
|
805
|
+
output[field.name] = decodeValue(
|
|
806
|
+
field,
|
|
807
|
+
adapter.dialect,
|
|
808
|
+
rawRow[`${node.alias}__${field.name}`]
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
for (const child of node.children) {
|
|
812
|
+
const childValue = projectNativeJoinNode(child, rawRow);
|
|
813
|
+
output[child.relationName] = child.relationKind === "hasMany" || child.relationKind === "manyToMany" ? childValue ? [childValue] : [] : childValue;
|
|
814
|
+
}
|
|
815
|
+
return output;
|
|
816
|
+
}
|
|
817
|
+
function mergeNativeJoinNode(node, target, next) {
|
|
818
|
+
for (const child of node.children) {
|
|
819
|
+
const relationName = child.relationName;
|
|
820
|
+
if (child.relationKind === "hasMany" || child.relationKind === "manyToMany") {
|
|
821
|
+
const targetRows = Array.isArray(target[relationName]) ? target[relationName] : [];
|
|
822
|
+
const nextRows = Array.isArray(next[relationName]) ? next[relationName] : [];
|
|
823
|
+
if (!Array.isArray(target[relationName])) {
|
|
824
|
+
target[relationName] = targetRows;
|
|
825
|
+
}
|
|
826
|
+
for (const nextRow of nextRows) {
|
|
827
|
+
const identity = nextRow[nativeNodeIdentity];
|
|
828
|
+
const existing2 = targetRows.find((entry) => entry[nativeNodeIdentity] === identity);
|
|
829
|
+
if (existing2) {
|
|
830
|
+
mergeNativeJoinNode(child, existing2, nextRow);
|
|
831
|
+
} else {
|
|
832
|
+
targetRows.push(nextRow);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
const nextValue = next[relationName];
|
|
838
|
+
if (nextValue === void 0) continue;
|
|
839
|
+
if (nextValue === null) {
|
|
840
|
+
if (!(relationName in target)) {
|
|
841
|
+
target[relationName] = null;
|
|
842
|
+
}
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
const existing = target[relationName];
|
|
846
|
+
if (!existing || typeof existing !== "object") {
|
|
847
|
+
target[relationName] = nextValue;
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
mergeNativeJoinNode(child, existing, nextValue);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
async function loadRowsWithNativeJoins(schema, modelName, args) {
|
|
854
|
+
if (!hasNativeJoinableRelations(schema, modelName, args.select)) {
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
const plan = buildNativeJoinPlan(schema, modelName, args.select, { next: 0 });
|
|
858
|
+
if (!plan || !plan.children.length) {
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
const statement = buildNativeJoinStatement(plan, args);
|
|
862
|
+
const result = await adapter.query(statement.sql, statement.params);
|
|
863
|
+
const groupedRows = [];
|
|
864
|
+
const groupedByIdentity = /* @__PURE__ */ new Map();
|
|
865
|
+
for (const row of result.rows) {
|
|
866
|
+
const projected = projectNativeJoinNode(plan, row);
|
|
867
|
+
if (!projected) continue;
|
|
868
|
+
const identity = projected[nativeNodeIdentity];
|
|
869
|
+
const existing = groupedByIdentity.get(identity);
|
|
870
|
+
if (existing) {
|
|
871
|
+
mergeNativeJoinNode(plan, existing, projected);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
groupedByIdentity.set(identity, projected);
|
|
875
|
+
groupedRows.push(projected);
|
|
876
|
+
}
|
|
877
|
+
return groupedRows;
|
|
878
|
+
}
|
|
609
879
|
async function projectRow(schema, modelName, row, select) {
|
|
610
880
|
const manifest = getManifest(schema);
|
|
611
881
|
const model = manifest.models[modelName];
|