prisma-sql 1.64.0 → 1.65.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/generator.cjs +1119 -239
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1119 -239
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1410 -282
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +32 -2
- package/dist/index.d.ts +32 -2
- package/dist/index.js +1409 -283
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +1 -1
package/dist/index.cjs
CHANGED
|
@@ -652,81 +652,6 @@ function needsQuoting(identifier) {
|
|
|
652
652
|
return false;
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
-
// src/builder/shared/model-field-cache.ts
|
|
656
|
-
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
657
|
-
function quoteIdent(id) {
|
|
658
|
-
if (typeof id !== "string" || id.trim().length === 0) {
|
|
659
|
-
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
660
|
-
}
|
|
661
|
-
for (let i = 0; i < id.length; i++) {
|
|
662
|
-
const code = id.charCodeAt(i);
|
|
663
|
-
if (code >= 0 && code <= 31 || code === 127) {
|
|
664
|
-
throw new Error(
|
|
665
|
-
`quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
if (needsQuoting(id)) {
|
|
670
|
-
return `"${id.replace(/"/g, '""')}"`;
|
|
671
|
-
}
|
|
672
|
-
return id;
|
|
673
|
-
}
|
|
674
|
-
function ensureFullCache(model) {
|
|
675
|
-
let cache = MODEL_CACHE.get(model);
|
|
676
|
-
if (!cache) {
|
|
677
|
-
const fieldInfo = /* @__PURE__ */ new Map();
|
|
678
|
-
const scalarFields = /* @__PURE__ */ new Set();
|
|
679
|
-
const relationFields = /* @__PURE__ */ new Set();
|
|
680
|
-
const columnMap = /* @__PURE__ */ new Map();
|
|
681
|
-
const fieldByName = /* @__PURE__ */ new Map();
|
|
682
|
-
const quotedColumns = /* @__PURE__ */ new Map();
|
|
683
|
-
for (const f of model.fields) {
|
|
684
|
-
const info = {
|
|
685
|
-
name: f.name,
|
|
686
|
-
dbName: f.dbName || f.name,
|
|
687
|
-
type: f.type,
|
|
688
|
-
isRelation: !!f.isRelation,
|
|
689
|
-
isRequired: !!f.isRequired
|
|
690
|
-
};
|
|
691
|
-
fieldInfo.set(f.name, info);
|
|
692
|
-
fieldByName.set(f.name, f);
|
|
693
|
-
if (info.isRelation) {
|
|
694
|
-
relationFields.add(f.name);
|
|
695
|
-
} else {
|
|
696
|
-
scalarFields.add(f.name);
|
|
697
|
-
const dbName = info.dbName;
|
|
698
|
-
columnMap.set(f.name, dbName);
|
|
699
|
-
quotedColumns.set(f.name, quoteIdent(dbName));
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
cache = {
|
|
703
|
-
fieldInfo,
|
|
704
|
-
scalarFields,
|
|
705
|
-
relationFields,
|
|
706
|
-
columnMap,
|
|
707
|
-
fieldByName,
|
|
708
|
-
quotedColumns
|
|
709
|
-
};
|
|
710
|
-
MODEL_CACHE.set(model, cache);
|
|
711
|
-
}
|
|
712
|
-
return cache;
|
|
713
|
-
}
|
|
714
|
-
function getFieldInfo(model, fieldName) {
|
|
715
|
-
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
716
|
-
}
|
|
717
|
-
function getScalarFieldSet(model) {
|
|
718
|
-
return ensureFullCache(model).scalarFields;
|
|
719
|
-
}
|
|
720
|
-
function getRelationFieldSet(model) {
|
|
721
|
-
return ensureFullCache(model).relationFields;
|
|
722
|
-
}
|
|
723
|
-
function getColumnMap(model) {
|
|
724
|
-
return ensureFullCache(model).columnMap;
|
|
725
|
-
}
|
|
726
|
-
function getQuotedColumn(model, fieldName) {
|
|
727
|
-
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
655
|
// src/builder/shared/sql-utils.ts
|
|
731
656
|
function containsControlChars(s) {
|
|
732
657
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -1028,6 +953,70 @@ function normalizeKeyList(input) {
|
|
|
1028
953
|
return s.length > 0 ? [s] : [];
|
|
1029
954
|
}
|
|
1030
955
|
|
|
956
|
+
// src/builder/shared/model-field-cache.ts
|
|
957
|
+
var SCALAR_FIELD_CACHE = /* @__PURE__ */ new WeakMap();
|
|
958
|
+
var RELATION_FIELD_CACHE = /* @__PURE__ */ new WeakMap();
|
|
959
|
+
var COLUMN_MAP_CACHE = /* @__PURE__ */ new WeakMap();
|
|
960
|
+
var QUOTED_COLUMN_CACHE = /* @__PURE__ */ new WeakMap();
|
|
961
|
+
var FIELD_BY_NAME_CACHE = /* @__PURE__ */ new WeakMap();
|
|
962
|
+
function getScalarFieldSet(model) {
|
|
963
|
+
let cached = SCALAR_FIELD_CACHE.get(model);
|
|
964
|
+
if (cached) return cached;
|
|
965
|
+
const set = /* @__PURE__ */ new Set();
|
|
966
|
+
for (const f of model.fields) {
|
|
967
|
+
if (!f.isRelation) set.add(f.name);
|
|
968
|
+
}
|
|
969
|
+
SCALAR_FIELD_CACHE.set(model, set);
|
|
970
|
+
return set;
|
|
971
|
+
}
|
|
972
|
+
function getRelationFieldSet(model) {
|
|
973
|
+
let cached = RELATION_FIELD_CACHE.get(model);
|
|
974
|
+
if (cached) return cached;
|
|
975
|
+
const set = /* @__PURE__ */ new Set();
|
|
976
|
+
for (const f of model.fields) {
|
|
977
|
+
if (f.isRelation) set.add(f.name);
|
|
978
|
+
}
|
|
979
|
+
RELATION_FIELD_CACHE.set(model, set);
|
|
980
|
+
return set;
|
|
981
|
+
}
|
|
982
|
+
function getColumnMap(model) {
|
|
983
|
+
let cached = COLUMN_MAP_CACHE.get(model);
|
|
984
|
+
if (cached) return cached;
|
|
985
|
+
const map = /* @__PURE__ */ new Map();
|
|
986
|
+
for (const f of model.fields) {
|
|
987
|
+
if (f.dbName && f.dbName !== f.name) {
|
|
988
|
+
map.set(f.name, f.dbName);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
COLUMN_MAP_CACHE.set(model, map);
|
|
992
|
+
return map;
|
|
993
|
+
}
|
|
994
|
+
function getQuotedColumn(model, fieldName) {
|
|
995
|
+
let cache = QUOTED_COLUMN_CACHE.get(model);
|
|
996
|
+
if (!cache) {
|
|
997
|
+
cache = /* @__PURE__ */ new Map();
|
|
998
|
+
QUOTED_COLUMN_CACHE.set(model, cache);
|
|
999
|
+
}
|
|
1000
|
+
const cached = cache.get(fieldName);
|
|
1001
|
+
if (cached !== void 0) return cached;
|
|
1002
|
+
const columnMap = getColumnMap(model);
|
|
1003
|
+
const columnName = columnMap.get(fieldName) || fieldName;
|
|
1004
|
+
const quoted = quote(columnName);
|
|
1005
|
+
cache.set(fieldName, quoted);
|
|
1006
|
+
return quoted;
|
|
1007
|
+
}
|
|
1008
|
+
function getFieldByName(model, fieldName) {
|
|
1009
|
+
let cache = FIELD_BY_NAME_CACHE.get(model);
|
|
1010
|
+
if (!cache) {
|
|
1011
|
+
cache = /* @__PURE__ */ new Map();
|
|
1012
|
+
for (const field of model.fields) {
|
|
1013
|
+
cache.set(field.name, field);
|
|
1014
|
+
}
|
|
1015
|
+
FIELD_BY_NAME_CACHE.set(model, cache);
|
|
1016
|
+
}
|
|
1017
|
+
return cache.get(fieldName);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1031
1020
|
// src/builder/joins.ts
|
|
1032
1021
|
function isRelationField(fieldName, model) {
|
|
1033
1022
|
return getRelationFieldSet(model).has(fieldName);
|
|
@@ -1141,11 +1130,18 @@ var getNextSort = (sortRaw) => {
|
|
|
1141
1130
|
return sortRaw;
|
|
1142
1131
|
};
|
|
1143
1132
|
var flipObjectSort = (obj) => {
|
|
1144
|
-
const
|
|
1145
|
-
const
|
|
1146
|
-
const
|
|
1147
|
-
if (
|
|
1148
|
-
out.
|
|
1133
|
+
const out = __spreadValues({}, obj);
|
|
1134
|
+
const hasSort = Object.prototype.hasOwnProperty.call(obj, "sort");
|
|
1135
|
+
const hasDirection = Object.prototype.hasOwnProperty.call(obj, "direction");
|
|
1136
|
+
if (hasSort) {
|
|
1137
|
+
out.sort = getNextSort(obj.sort);
|
|
1138
|
+
} else if (hasDirection) {
|
|
1139
|
+
out.direction = getNextSort(obj.direction);
|
|
1140
|
+
} else {
|
|
1141
|
+
out.sort = getNextSort(obj.sort);
|
|
1142
|
+
}
|
|
1143
|
+
if (typeof obj.nulls === "string") {
|
|
1144
|
+
out.nulls = flipNulls(obj.nulls);
|
|
1149
1145
|
}
|
|
1150
1146
|
return out;
|
|
1151
1147
|
};
|
|
@@ -1191,7 +1187,7 @@ var normalizePairs = (pairs, parseValue) => {
|
|
|
1191
1187
|
return pairs.map(([field, rawValue]) => {
|
|
1192
1188
|
const parsed = parseValue(rawValue, field);
|
|
1193
1189
|
return {
|
|
1194
|
-
[field]: parsed.nulls !== void 0 ? {
|
|
1190
|
+
[field]: parsed.nulls !== void 0 ? { direction: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
1195
1191
|
};
|
|
1196
1192
|
});
|
|
1197
1193
|
};
|
|
@@ -1240,37 +1236,113 @@ function ensureDeterministicOrderByInput(args) {
|
|
|
1240
1236
|
}
|
|
1241
1237
|
|
|
1242
1238
|
// src/builder/shared/validators/field-assertions.ts
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
const field = getFieldInfo(model, fieldName);
|
|
1239
|
+
function assertFieldExists(fieldName, model, path) {
|
|
1240
|
+
const field = getFieldByName(model, fieldName);
|
|
1246
1241
|
if (!field) {
|
|
1247
1242
|
throw createError(
|
|
1248
|
-
|
|
1243
|
+
`Field '${fieldName}' does not exist on model ${model.name}`,
|
|
1249
1244
|
{
|
|
1250
1245
|
field: fieldName,
|
|
1251
1246
|
modelName: model.name,
|
|
1252
|
-
|
|
1247
|
+
path
|
|
1253
1248
|
}
|
|
1254
1249
|
);
|
|
1255
1250
|
}
|
|
1251
|
+
return field;
|
|
1252
|
+
}
|
|
1253
|
+
function assertScalarField(model, fieldName, context) {
|
|
1254
|
+
const field = getFieldByName(model, fieldName);
|
|
1255
|
+
if (!field) {
|
|
1256
|
+
throw new Error(
|
|
1257
|
+
`${context}: field '${fieldName}' does not exist on model '${model.name}'`
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1256
1260
|
if (field.isRelation) {
|
|
1257
|
-
throw
|
|
1258
|
-
`${context}
|
|
1259
|
-
{ field: fieldName, modelName: model.name }
|
|
1261
|
+
throw new Error(
|
|
1262
|
+
`${context}: field '${fieldName}' is a relation field, expected scalar field`
|
|
1260
1263
|
);
|
|
1261
1264
|
}
|
|
1262
|
-
return field;
|
|
1263
1265
|
}
|
|
1264
1266
|
function assertNumericField(model, fieldName, context) {
|
|
1265
|
-
|
|
1266
|
-
const
|
|
1267
|
-
if (!
|
|
1267
|
+
assertScalarField(model, fieldName, context);
|
|
1268
|
+
const field = getFieldByName(model, fieldName);
|
|
1269
|
+
if (!field) return;
|
|
1270
|
+
const numericTypes = /* @__PURE__ */ new Set([
|
|
1271
|
+
"Int",
|
|
1272
|
+
"BigInt",
|
|
1273
|
+
"Float",
|
|
1274
|
+
"Decimal",
|
|
1275
|
+
"Int?",
|
|
1276
|
+
"BigInt?",
|
|
1277
|
+
"Float?",
|
|
1278
|
+
"Decimal?"
|
|
1279
|
+
]);
|
|
1280
|
+
if (!numericTypes.has(field.type)) {
|
|
1281
|
+
throw new Error(
|
|
1282
|
+
`${context}: field '${fieldName}' must be numeric (Int, BigInt, Float, Decimal), got '${field.type}'`
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function assertValidOperator(fieldName, operator, fieldType, path, modelName) {
|
|
1287
|
+
const stringOps = /* @__PURE__ */ new Set([
|
|
1288
|
+
"equals",
|
|
1289
|
+
"not",
|
|
1290
|
+
"in",
|
|
1291
|
+
"notIn",
|
|
1292
|
+
"lt",
|
|
1293
|
+
"lte",
|
|
1294
|
+
"gt",
|
|
1295
|
+
"gte",
|
|
1296
|
+
"contains",
|
|
1297
|
+
"startsWith",
|
|
1298
|
+
"endsWith",
|
|
1299
|
+
"mode",
|
|
1300
|
+
"search"
|
|
1301
|
+
]);
|
|
1302
|
+
const numericOps = /* @__PURE__ */ new Set([
|
|
1303
|
+
"equals",
|
|
1304
|
+
"not",
|
|
1305
|
+
"in",
|
|
1306
|
+
"notIn",
|
|
1307
|
+
"lt",
|
|
1308
|
+
"lte",
|
|
1309
|
+
"gt",
|
|
1310
|
+
"gte"
|
|
1311
|
+
]);
|
|
1312
|
+
const jsonOps = /* @__PURE__ */ new Set([
|
|
1313
|
+
"equals",
|
|
1314
|
+
"not",
|
|
1315
|
+
"path",
|
|
1316
|
+
"string_contains",
|
|
1317
|
+
"string_starts_with",
|
|
1318
|
+
"string_ends_with",
|
|
1319
|
+
"array_contains",
|
|
1320
|
+
"array_starts_with",
|
|
1321
|
+
"array_ends_with"
|
|
1322
|
+
]);
|
|
1323
|
+
const isString = fieldType === "String" || fieldType === "String?";
|
|
1324
|
+
const isNumeric = ["Int", "BigInt", "Float", "Decimal"].some(
|
|
1325
|
+
(t) => fieldType === t || fieldType === `${t}?`
|
|
1326
|
+
);
|
|
1327
|
+
const isJson = fieldType === "Json" || fieldType === "Json?";
|
|
1328
|
+
if (isString && !stringOps.has(operator)) {
|
|
1268
1329
|
throw createError(
|
|
1269
|
-
|
|
1270
|
-
{ field: fieldName, modelName
|
|
1330
|
+
`Operator '${operator}' is not valid for String field '${fieldName}'`,
|
|
1331
|
+
{ field: fieldName, modelName, path, operator }
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
if (isNumeric && !numericOps.has(operator)) {
|
|
1335
|
+
throw createError(
|
|
1336
|
+
`Operator '${operator}' is not valid for numeric field '${fieldName}'`,
|
|
1337
|
+
{ field: fieldName, modelName, path, operator }
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
if (isJson && !jsonOps.has(operator)) {
|
|
1341
|
+
throw createError(
|
|
1342
|
+
`Operator '${operator}' is not valid for Json field '${fieldName}'`,
|
|
1343
|
+
{ field: fieldName, modelName, path, operator }
|
|
1271
1344
|
);
|
|
1272
1345
|
}
|
|
1273
|
-
return field;
|
|
1274
1346
|
}
|
|
1275
1347
|
|
|
1276
1348
|
// src/builder/pagination.ts
|
|
@@ -1784,6 +1856,14 @@ function handleInOperator(expr, op, val, params, dialect) {
|
|
|
1784
1856
|
if (val.length === 0) {
|
|
1785
1857
|
return op === Ops.IN ? "0=1" : "1=1";
|
|
1786
1858
|
}
|
|
1859
|
+
if (dialect === "sqlite" && val.length <= 30) {
|
|
1860
|
+
const placeholders = [];
|
|
1861
|
+
for (const item of val) {
|
|
1862
|
+
placeholders.push(params.add(item));
|
|
1863
|
+
}
|
|
1864
|
+
const list = placeholders.join(", ");
|
|
1865
|
+
return op === Ops.IN ? `${expr} IN (${list})` : `${expr} NOT IN (${list})`;
|
|
1866
|
+
}
|
|
1787
1867
|
const paramValue = prepareArrayParam(val, dialect);
|
|
1788
1868
|
const placeholder = params.add(paramValue);
|
|
1789
1869
|
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
@@ -2319,57 +2399,6 @@ function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
|
2319
2399
|
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
2320
2400
|
}
|
|
2321
2401
|
|
|
2322
|
-
// src/builder/shared/validators/field-validators.ts
|
|
2323
|
-
function assertFieldExists(name, model, path) {
|
|
2324
|
-
const field = model.fields.find((f) => f.name === name);
|
|
2325
|
-
if (!isNotNullish(field)) {
|
|
2326
|
-
throw createError(`Field '${name}' does not exist on '${model.name}'`, {
|
|
2327
|
-
field: name,
|
|
2328
|
-
path,
|
|
2329
|
-
modelName: model.name,
|
|
2330
|
-
availableFields: model.fields.map((f) => f.name)
|
|
2331
|
-
});
|
|
2332
|
-
}
|
|
2333
|
-
return field;
|
|
2334
|
-
}
|
|
2335
|
-
function assertValidOperator(fieldName, op, fieldType, path, modelName) {
|
|
2336
|
-
if (!isNotNullish(fieldType)) return;
|
|
2337
|
-
const ARRAY_OPS = /* @__PURE__ */ new Set([
|
|
2338
|
-
Ops.HAS,
|
|
2339
|
-
Ops.HAS_SOME,
|
|
2340
|
-
Ops.HAS_EVERY,
|
|
2341
|
-
Ops.IS_EMPTY
|
|
2342
|
-
]);
|
|
2343
|
-
const JSON_OPS2 = /* @__PURE__ */ new Set([
|
|
2344
|
-
Ops.PATH,
|
|
2345
|
-
Ops.STRING_CONTAINS,
|
|
2346
|
-
Ops.STRING_STARTS_WITH,
|
|
2347
|
-
Ops.STRING_ENDS_WITH
|
|
2348
|
-
]);
|
|
2349
|
-
const isArrayOp = ARRAY_OPS.has(op);
|
|
2350
|
-
const isFieldArray = isArrayType(fieldType);
|
|
2351
|
-
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
2352
|
-
if (arrayOpMismatch) {
|
|
2353
|
-
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
2354
|
-
operator: op,
|
|
2355
|
-
field: fieldName,
|
|
2356
|
-
path,
|
|
2357
|
-
modelName
|
|
2358
|
-
});
|
|
2359
|
-
}
|
|
2360
|
-
const isJsonOp = JSON_OPS2.has(op);
|
|
2361
|
-
const isFieldJson = isJsonType(fieldType);
|
|
2362
|
-
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
2363
|
-
if (jsonOpMismatch) {
|
|
2364
|
-
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
2365
|
-
operator: op,
|
|
2366
|
-
field: fieldName,
|
|
2367
|
-
path,
|
|
2368
|
-
modelName
|
|
2369
|
-
});
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
2402
|
// src/builder/where/builder.ts
|
|
2374
2403
|
var MAX_QUERY_DEPTH = 50;
|
|
2375
2404
|
var EMPTY_JOINS = Object.freeze([]);
|
|
@@ -2426,6 +2455,23 @@ function buildRelationFilter(fieldName, value, ctx, builder) {
|
|
|
2426
2455
|
}
|
|
2427
2456
|
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
2428
2457
|
}
|
|
2458
|
+
function buildSimpleEquality(expr, value, ctx) {
|
|
2459
|
+
if (value === null) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
2460
|
+
const placeholder = ctx.params.addAuto(value);
|
|
2461
|
+
return `${expr} = ${placeholder}`;
|
|
2462
|
+
}
|
|
2463
|
+
function isSimpleWhere(where) {
|
|
2464
|
+
const keys = Object.keys(where);
|
|
2465
|
+
if (keys.length !== 1) return false;
|
|
2466
|
+
const key = keys[0];
|
|
2467
|
+
const value = where[key];
|
|
2468
|
+
if (value === null) return true;
|
|
2469
|
+
if (value === void 0) return false;
|
|
2470
|
+
if (typeof value === "string") return true;
|
|
2471
|
+
if (typeof value === "number") return true;
|
|
2472
|
+
if (typeof value === "boolean") return true;
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2429
2475
|
function buildWhereEntry(key, value, ctx, builder) {
|
|
2430
2476
|
const op = asLogicalOperator(key);
|
|
2431
2477
|
if (op) return buildLogical(op, value, ctx, builder);
|
|
@@ -2452,6 +2498,14 @@ function buildWhereInternal(where, ctx, builder) {
|
|
|
2452
2498
|
if (isEmptyWhere(where)) {
|
|
2453
2499
|
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2454
2500
|
}
|
|
2501
|
+
if (isSimpleWhere(where)) {
|
|
2502
|
+
const key = Object.keys(where)[0];
|
|
2503
|
+
const value = where[key];
|
|
2504
|
+
assertFieldExists(key, ctx.model, ctx.path);
|
|
2505
|
+
const expr = col(ctx.alias, key, ctx.model);
|
|
2506
|
+
const clause = buildSimpleEquality(expr, value, ctx);
|
|
2507
|
+
return freezeResult(clause, EMPTY_JOINS);
|
|
2508
|
+
}
|
|
2455
2509
|
const allJoins = [];
|
|
2456
2510
|
const clauses = [];
|
|
2457
2511
|
for (const key in where) {
|
|
@@ -2578,10 +2632,16 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2578
2632
|
}
|
|
2579
2633
|
|
|
2580
2634
|
// src/builder/shared/alias-generator.ts
|
|
2635
|
+
var SAFE_IDENTIFIER_CACHE = /* @__PURE__ */ new Map();
|
|
2581
2636
|
function toSafeSqlIdentifier(input) {
|
|
2637
|
+
const cached = SAFE_IDENTIFIER_CACHE.get(input);
|
|
2638
|
+
if (cached !== void 0) return cached;
|
|
2582
2639
|
const raw = String(input);
|
|
2583
2640
|
const n = raw.length;
|
|
2584
|
-
if (n === 0)
|
|
2641
|
+
if (n === 0) {
|
|
2642
|
+
SAFE_IDENTIFIER_CACHE.set(input, "_t");
|
|
2643
|
+
return "_t";
|
|
2644
|
+
}
|
|
2585
2645
|
let out = "";
|
|
2586
2646
|
for (let i = 0; i < n; i++) {
|
|
2587
2647
|
const c = raw.charCodeAt(i);
|
|
@@ -2593,7 +2653,11 @@ function toSafeSqlIdentifier(input) {
|
|
|
2593
2653
|
const c0 = out.charCodeAt(0);
|
|
2594
2654
|
const startsOk = c0 >= 65 && c0 <= 90 || c0 >= 97 && c0 <= 122 || c0 === 95;
|
|
2595
2655
|
const lowered = (startsOk ? out : `_${out}`).toLowerCase();
|
|
2596
|
-
|
|
2656
|
+
const result = ALIAS_FORBIDDEN_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
2657
|
+
if (SAFE_IDENTIFIER_CACHE.size < 1e3) {
|
|
2658
|
+
SAFE_IDENTIFIER_CACHE.set(input, result);
|
|
2659
|
+
}
|
|
2660
|
+
return result;
|
|
2597
2661
|
}
|
|
2598
2662
|
function createAliasGenerator(maxAliases = 1e4) {
|
|
2599
2663
|
let counter = 0;
|
|
@@ -2704,9 +2768,17 @@ function assertCanAddParam(currentIndex) {
|
|
|
2704
2768
|
);
|
|
2705
2769
|
}
|
|
2706
2770
|
}
|
|
2707
|
-
|
|
2771
|
+
var POSTGRES_POSITION_CACHE = new Array(100);
|
|
2772
|
+
for (let i = 0; i < 100; i++) {
|
|
2773
|
+
POSTGRES_POSITION_CACHE[i] = `$${i + 1}`;
|
|
2774
|
+
}
|
|
2775
|
+
function formatPositionPostgres(position) {
|
|
2776
|
+
if (position <= 100) return POSTGRES_POSITION_CACHE[position - 1];
|
|
2708
2777
|
return `$${position}`;
|
|
2709
2778
|
}
|
|
2779
|
+
function formatPositionSqlite(_position) {
|
|
2780
|
+
return "?";
|
|
2781
|
+
}
|
|
2710
2782
|
function validateDynamicName(dynamicName) {
|
|
2711
2783
|
const dn = dynamicName.trim();
|
|
2712
2784
|
if (dn.length === 0) {
|
|
@@ -2714,13 +2786,14 @@ function validateDynamicName(dynamicName) {
|
|
|
2714
2786
|
}
|
|
2715
2787
|
return dn;
|
|
2716
2788
|
}
|
|
2717
|
-
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2789
|
+
function createStoreInternal(startIndex, dialect, initialParams = [], initialMappings = []) {
|
|
2718
2790
|
let index = startIndex;
|
|
2719
2791
|
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2720
2792
|
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2721
2793
|
const dynamicNameToIndex = buildDynamicNameIndex(mappings);
|
|
2722
2794
|
let dirty = true;
|
|
2723
2795
|
let cachedSnapshot = null;
|
|
2796
|
+
const formatPosition = dialect === "sqlite" ? formatPositionSqlite : formatPositionPostgres;
|
|
2724
2797
|
function addDynamic(dynamicName) {
|
|
2725
2798
|
const dn = validateDynamicName(dynamicName);
|
|
2726
2799
|
const existing = dynamicNameToIndex.get(dn);
|
|
@@ -2770,10 +2843,13 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2770
2843
|
snapshot,
|
|
2771
2844
|
get index() {
|
|
2772
2845
|
return index;
|
|
2846
|
+
},
|
|
2847
|
+
get dialect() {
|
|
2848
|
+
return dialect;
|
|
2773
2849
|
}
|
|
2774
2850
|
};
|
|
2775
2851
|
}
|
|
2776
|
-
function createParamStore(startIndex = 1) {
|
|
2852
|
+
function createParamStore(startIndex = 1, dialect = "postgres") {
|
|
2777
2853
|
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2778
2854
|
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
2779
2855
|
}
|
|
@@ -2782,12 +2858,13 @@ function createParamStore(startIndex = 1) {
|
|
|
2782
2858
|
`Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
|
|
2783
2859
|
);
|
|
2784
2860
|
}
|
|
2785
|
-
return createStoreInternal(startIndex);
|
|
2861
|
+
return createStoreInternal(startIndex, dialect);
|
|
2786
2862
|
}
|
|
2787
|
-
function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
|
|
2863
|
+
function createParamStoreFrom(existingParams, existingMappings, nextIndex, dialect = "postgres") {
|
|
2788
2864
|
validateState(existingParams, existingMappings, nextIndex);
|
|
2789
2865
|
return createStoreInternal(
|
|
2790
2866
|
nextIndex,
|
|
2867
|
+
dialect,
|
|
2791
2868
|
existingParams.slice(),
|
|
2792
2869
|
existingMappings.slice()
|
|
2793
2870
|
);
|
|
@@ -2828,6 +2905,7 @@ function buildWhereClause(where, options) {
|
|
|
2828
2905
|
}
|
|
2829
2906
|
|
|
2830
2907
|
// src/builder/select/fields.ts
|
|
2908
|
+
var DEFAULT_SELECT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
2831
2909
|
function toSelectEntries(select) {
|
|
2832
2910
|
const out = [];
|
|
2833
2911
|
for (const [k, v] of Object.entries(select)) {
|
|
@@ -2875,13 +2953,29 @@ function buildDefaultScalarFields(model, alias) {
|
|
|
2875
2953
|
}
|
|
2876
2954
|
return out;
|
|
2877
2955
|
}
|
|
2956
|
+
function getDefaultSelectCached(model, alias) {
|
|
2957
|
+
var _a;
|
|
2958
|
+
return (_a = DEFAULT_SELECT_CACHE.get(model)) == null ? void 0 : _a.get(alias);
|
|
2959
|
+
}
|
|
2960
|
+
function cacheDefaultSelect(model, alias, sql) {
|
|
2961
|
+
let cache = DEFAULT_SELECT_CACHE.get(model);
|
|
2962
|
+
if (!cache) {
|
|
2963
|
+
cache = /* @__PURE__ */ new Map();
|
|
2964
|
+
DEFAULT_SELECT_CACHE.set(model, cache);
|
|
2965
|
+
}
|
|
2966
|
+
cache.set(alias, sql);
|
|
2967
|
+
}
|
|
2878
2968
|
function buildSelectFields(args, model, alias) {
|
|
2879
2969
|
const scalarSet = getScalarFieldSet(model);
|
|
2880
2970
|
const relationSet = getRelationFieldSet(model);
|
|
2881
2971
|
if (!isNotNullish(args.select)) {
|
|
2882
|
-
|
|
2972
|
+
const cached = getDefaultSelectCached(model, alias);
|
|
2973
|
+
if (cached) return cached;
|
|
2974
|
+
const result = buildDefaultScalarFields(model, alias).join(
|
|
2883
2975
|
SQL_SEPARATORS.FIELD_LIST
|
|
2884
2976
|
);
|
|
2977
|
+
cacheDefaultSelect(model, alias, result);
|
|
2978
|
+
return result;
|
|
2885
2979
|
}
|
|
2886
2980
|
const entries = toSelectEntries(args.select);
|
|
2887
2981
|
validateSelectKeys(entries, scalarSet, relationSet);
|
|
@@ -2959,8 +3053,6 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
|
|
|
2959
3053
|
}
|
|
2960
3054
|
return buildAllScalarParts(relModel, relAlias).join(SQL_SEPARATORS.FIELD_LIST);
|
|
2961
3055
|
}
|
|
2962
|
-
|
|
2963
|
-
// src/builder/select/includes.ts
|
|
2964
3056
|
var MAX_INCLUDE_DEPTH = 10;
|
|
2965
3057
|
var MAX_TOTAL_SUBQUERIES = 100;
|
|
2966
3058
|
var MAX_TOTAL_INCLUDES = 50;
|
|
@@ -3148,8 +3240,12 @@ function buildWhereParts(whereInput, relModel, relAlias, ctx) {
|
|
|
3148
3240
|
dialect: ctx.dialect
|
|
3149
3241
|
});
|
|
3150
3242
|
const joins = whereResult.joins.join(" ");
|
|
3151
|
-
const
|
|
3152
|
-
return {
|
|
3243
|
+
const hasClause = isValidWhereClause(whereResult.clause);
|
|
3244
|
+
return {
|
|
3245
|
+
joins,
|
|
3246
|
+
whereClause: hasClause ? ` ${SQL_TEMPLATES.AND} ${whereResult.clause}` : "",
|
|
3247
|
+
rawClause: hasClause ? whereResult.clause : ""
|
|
3248
|
+
};
|
|
3153
3249
|
}
|
|
3154
3250
|
function limitOneSql(sql, params, skipVal, scope) {
|
|
3155
3251
|
if (isNotNullish(skipVal)) {
|
|
@@ -3194,8 +3290,10 @@ function buildListIncludeSpec(args) {
|
|
|
3194
3290
|
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
3195
3291
|
const noTake = !isNotNullish(args.takeVal);
|
|
3196
3292
|
const noSkip = !isNotNullish(args.skipVal);
|
|
3293
|
+
const emptyJson = args.ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3197
3294
|
if (args.ctx.dialect === "postgres" && noTake && noSkip) {
|
|
3198
|
-
const
|
|
3295
|
+
const rawAgg = args.orderBySql ? `json_agg(${rowExpr} ORDER BY ${args.orderBySql})` : `json_agg(${rowExpr})`;
|
|
3296
|
+
const selectExpr2 = `COALESCE(${rawAgg}, ${emptyJson})`;
|
|
3199
3297
|
const sql2 = buildBaseSql({
|
|
3200
3298
|
selectExpr: selectExpr2,
|
|
3201
3299
|
relTable: args.relTable,
|
|
@@ -3225,10 +3323,154 @@ function buildListIncludeSpec(args) {
|
|
|
3225
3323
|
args.skipVal,
|
|
3226
3324
|
scopeBase
|
|
3227
3325
|
);
|
|
3228
|
-
const
|
|
3326
|
+
const agg = jsonAgg("row", args.ctx.dialect);
|
|
3327
|
+
const selectExpr = `COALESCE(${agg}, ${emptyJson})`;
|
|
3229
3328
|
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
3230
3329
|
return Object.freeze({ name: args.relName, sql, isOneToOne: false });
|
|
3231
3330
|
}
|
|
3331
|
+
function resolveIncludeKeyPairs(field) {
|
|
3332
|
+
const fkFields = normalizeKeyList(field.foreignKey);
|
|
3333
|
+
if (fkFields.length === 0) {
|
|
3334
|
+
throw new Error(
|
|
3335
|
+
`Relation '${field.name}' is missing foreignKey for join-based include`
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
const refs = normalizeKeyList(field.references);
|
|
3339
|
+
const refFields = refs.length > 0 ? refs : fkFields.length === 1 ? ["id"] : [];
|
|
3340
|
+
if (refFields.length !== fkFields.length) {
|
|
3341
|
+
throw new Error(
|
|
3342
|
+
`Relation '${field.name}' references count doesn't match foreignKey count`
|
|
3343
|
+
);
|
|
3344
|
+
}
|
|
3345
|
+
return {
|
|
3346
|
+
relKeyFields: field.isForeignKeyLocal ? refFields : fkFields,
|
|
3347
|
+
parentKeyFields: field.isForeignKeyLocal ? fkFields : refFields
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
function buildFkSelectList(relAlias, relModel, relKeyFields) {
|
|
3351
|
+
return relKeyFields.map((f, i) => `${relAlias}.${quoteColumn(relModel, f)} AS "__fk${i}"`).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3352
|
+
}
|
|
3353
|
+
function buildFkGroupByUnqualified(relKeyFields) {
|
|
3354
|
+
return relKeyFields.map((_, i) => `"__fk${i}"`).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3355
|
+
}
|
|
3356
|
+
function buildJoinOnCondition(joinAlias, parentAlias, parentModel, parentKeyFields) {
|
|
3357
|
+
const parts = parentKeyFields.map(
|
|
3358
|
+
(f, i) => `${joinAlias}."__fk${i}" = ${parentAlias}.${quoteColumn(parentModel, f)}`
|
|
3359
|
+
);
|
|
3360
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
3361
|
+
}
|
|
3362
|
+
function buildPartitionBy(relAlias, relModel, relKeyFields) {
|
|
3363
|
+
return relKeyFields.map((f) => `${relAlias}.${quoteColumn(relModel, f)}`).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3364
|
+
}
|
|
3365
|
+
function hasNestedRelationInArgs(relArgs, relModel) {
|
|
3366
|
+
if (!isPlainObject(relArgs)) return false;
|
|
3367
|
+
const relationSet = getRelationFieldSet(relModel);
|
|
3368
|
+
const checkSource = (src) => {
|
|
3369
|
+
if (!isPlainObject(src)) return false;
|
|
3370
|
+
for (const k of Object.keys(src)) {
|
|
3371
|
+
if (relationSet.has(k) && src[k] !== false)
|
|
3372
|
+
return true;
|
|
3373
|
+
}
|
|
3374
|
+
return false;
|
|
3375
|
+
};
|
|
3376
|
+
if (checkSource(relArgs.include)) return true;
|
|
3377
|
+
if (checkSource(relArgs.select)) return true;
|
|
3378
|
+
return false;
|
|
3379
|
+
}
|
|
3380
|
+
function canUseJoinInclude(dialect, isList, takeVal, skipVal, depth, outerHasLimit, hasNestedIncludes2) {
|
|
3381
|
+
if (dialect !== "postgres") return false;
|
|
3382
|
+
if (!isList) return false;
|
|
3383
|
+
if (depth > 0) return false;
|
|
3384
|
+
if (outerHasLimit) return false;
|
|
3385
|
+
if (hasNestedIncludes2) return false;
|
|
3386
|
+
if (schemaParser.isDynamicParameter(takeVal) || schemaParser.isDynamicParameter(skipVal)) return false;
|
|
3387
|
+
return true;
|
|
3388
|
+
}
|
|
3389
|
+
function buildJoinBasedNonPaginated(args) {
|
|
3390
|
+
const { relKeyFields, parentKeyFields } = resolveIncludeKeyPairs(args.field);
|
|
3391
|
+
const joinAlias = args.ctx.aliasGen.next(`__inc_${args.relName}`);
|
|
3392
|
+
const fkSelect = buildFkSelectList(args.relAlias, args.relModel, relKeyFields);
|
|
3393
|
+
const fkGroupBy = buildPartitionBy(args.relAlias, args.relModel, relKeyFields);
|
|
3394
|
+
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
3395
|
+
const aggExpr = args.orderBySql ? `json_agg(${rowExpr} ORDER BY ${args.orderBySql})` : `json_agg(${rowExpr})`;
|
|
3396
|
+
const joinsPart = args.whereJoins ? ` ${args.whereJoins}` : "";
|
|
3397
|
+
const wherePart = args.rawWhereClause ? ` ${SQL_TEMPLATES.WHERE} ${args.rawWhereClause}` : "";
|
|
3398
|
+
const subquery = `SELECT ${fkSelect}${SQL_SEPARATORS.FIELD_LIST}${aggExpr} AS __agg FROM ${args.relTable} ${args.relAlias}${joinsPart}${wherePart} GROUP BY ${fkGroupBy}`;
|
|
3399
|
+
const onCondition = buildJoinOnCondition(
|
|
3400
|
+
joinAlias,
|
|
3401
|
+
args.ctx.parentAlias,
|
|
3402
|
+
args.ctx.model,
|
|
3403
|
+
parentKeyFields
|
|
3404
|
+
);
|
|
3405
|
+
const joinSql = `LEFT JOIN (${subquery}) ${joinAlias} ON ${onCondition}`;
|
|
3406
|
+
const selectExpr = `COALESCE(${joinAlias}.__agg, '[]'::json) AS ${quote(args.relName)}`;
|
|
3407
|
+
return Object.freeze({
|
|
3408
|
+
name: args.relName,
|
|
3409
|
+
sql: "",
|
|
3410
|
+
isOneToOne: false,
|
|
3411
|
+
joinSql,
|
|
3412
|
+
selectExpr
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
function buildJoinBasedPaginated(args) {
|
|
3416
|
+
const { relKeyFields, parentKeyFields } = resolveIncludeKeyPairs(args.field);
|
|
3417
|
+
const joinAlias = args.ctx.aliasGen.next(`__inc_${args.relName}`);
|
|
3418
|
+
const rankedAlias = args.ctx.aliasGen.next(`__ranked_${args.relName}`);
|
|
3419
|
+
const fkSelect = buildFkSelectList(args.relAlias, args.relModel, relKeyFields);
|
|
3420
|
+
const partitionBy = buildPartitionBy(
|
|
3421
|
+
args.relAlias,
|
|
3422
|
+
args.relModel,
|
|
3423
|
+
relKeyFields
|
|
3424
|
+
);
|
|
3425
|
+
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
3426
|
+
const orderExpr = args.orderBySql || `${args.relAlias}.${quoteColumn(args.relModel, "id")} ASC`;
|
|
3427
|
+
const joinsPart = args.whereJoins ? ` ${args.whereJoins}` : "";
|
|
3428
|
+
const wherePart = args.rawWhereClause ? ` ${SQL_TEMPLATES.WHERE} ${args.rawWhereClause}` : "";
|
|
3429
|
+
const innerSql = `SELECT ${fkSelect}${SQL_SEPARATORS.FIELD_LIST}${rowExpr} AS __row${SQL_SEPARATORS.FIELD_LIST}ROW_NUMBER() OVER (PARTITION BY ${partitionBy} ORDER BY ${orderExpr}) AS __rn FROM ${args.relTable} ${args.relAlias}${joinsPart}${wherePart}`;
|
|
3430
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3431
|
+
const rnFilterParts = [];
|
|
3432
|
+
if (isNotNullish(args.skipVal) && args.skipVal > 0) {
|
|
3433
|
+
const skipPh = addAutoScoped(
|
|
3434
|
+
args.ctx.params,
|
|
3435
|
+
args.skipVal,
|
|
3436
|
+
`${scopeBase}.skip`
|
|
3437
|
+
);
|
|
3438
|
+
rnFilterParts.push(`__rn > ${skipPh}`);
|
|
3439
|
+
if (isNotNullish(args.takeVal)) {
|
|
3440
|
+
const takePh = addAutoScoped(
|
|
3441
|
+
args.ctx.params,
|
|
3442
|
+
args.takeVal,
|
|
3443
|
+
`${scopeBase}.take`
|
|
3444
|
+
);
|
|
3445
|
+
rnFilterParts.push(`__rn <= (${skipPh} + ${takePh})`);
|
|
3446
|
+
}
|
|
3447
|
+
} else if (isNotNullish(args.takeVal)) {
|
|
3448
|
+
const takePh = addAutoScoped(
|
|
3449
|
+
args.ctx.params,
|
|
3450
|
+
args.takeVal,
|
|
3451
|
+
`${scopeBase}.take`
|
|
3452
|
+
);
|
|
3453
|
+
rnFilterParts.push(`__rn <= ${takePh}`);
|
|
3454
|
+
}
|
|
3455
|
+
const rnFilter = rnFilterParts.length > 0 ? ` ${SQL_TEMPLATES.WHERE} ${rnFilterParts.join(SQL_SEPARATORS.CONDITION_AND)}` : "";
|
|
3456
|
+
const fkGroupByOuter = buildFkGroupByUnqualified(relKeyFields);
|
|
3457
|
+
const outerSql = `SELECT ${fkGroupByOuter}${SQL_SEPARATORS.FIELD_LIST}json_agg(__row ORDER BY __rn) AS __agg FROM (${innerSql}) ${rankedAlias}${rnFilter} GROUP BY ${fkGroupByOuter}`;
|
|
3458
|
+
const onCondition = buildJoinOnCondition(
|
|
3459
|
+
joinAlias,
|
|
3460
|
+
args.ctx.parentAlias,
|
|
3461
|
+
args.ctx.model,
|
|
3462
|
+
parentKeyFields
|
|
3463
|
+
);
|
|
3464
|
+
const joinSql = `LEFT JOIN (${outerSql}) ${joinAlias} ON ${onCondition}`;
|
|
3465
|
+
const selectExpr = `COALESCE(${joinAlias}.__agg, '[]'::json) AS ${quote(args.relName)}`;
|
|
3466
|
+
return Object.freeze({
|
|
3467
|
+
name: args.relName,
|
|
3468
|
+
sql: "",
|
|
3469
|
+
isOneToOne: false,
|
|
3470
|
+
joinSql,
|
|
3471
|
+
selectExpr
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3232
3474
|
function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
3233
3475
|
const relTable = getRelationTableReference(relModel, ctx.dialect);
|
|
3234
3476
|
const relAlias = ctx.aliasGen.next(relName);
|
|
@@ -3285,6 +3527,48 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3285
3527
|
});
|
|
3286
3528
|
return Object.freeze({ name: relName, sql, isOneToOne: true });
|
|
3287
3529
|
}
|
|
3530
|
+
const depth = ctx.depth || 0;
|
|
3531
|
+
const outerHasLimit = ctx.outerHasLimit === true;
|
|
3532
|
+
const nestedIncludes = hasNestedRelationInArgs(relArgs, relModel);
|
|
3533
|
+
if (canUseJoinInclude(
|
|
3534
|
+
ctx.dialect,
|
|
3535
|
+
isList,
|
|
3536
|
+
adjusted.takeVal,
|
|
3537
|
+
paginationConfig.skipVal,
|
|
3538
|
+
depth,
|
|
3539
|
+
outerHasLimit,
|
|
3540
|
+
nestedIncludes
|
|
3541
|
+
)) {
|
|
3542
|
+
const hasTakeOrSkip = isNotNullish(adjusted.takeVal) || isNotNullish(paginationConfig.skipVal);
|
|
3543
|
+
if (!hasTakeOrSkip) {
|
|
3544
|
+
return buildJoinBasedNonPaginated({
|
|
3545
|
+
relName,
|
|
3546
|
+
relTable,
|
|
3547
|
+
relAlias,
|
|
3548
|
+
relModel,
|
|
3549
|
+
field,
|
|
3550
|
+
whereJoins: whereParts.joins,
|
|
3551
|
+
rawWhereClause: whereParts.rawClause,
|
|
3552
|
+
orderBySql,
|
|
3553
|
+
relSelect,
|
|
3554
|
+
ctx
|
|
3555
|
+
});
|
|
3556
|
+
}
|
|
3557
|
+
return buildJoinBasedPaginated({
|
|
3558
|
+
relName,
|
|
3559
|
+
relTable,
|
|
3560
|
+
relAlias,
|
|
3561
|
+
relModel,
|
|
3562
|
+
field,
|
|
3563
|
+
whereJoins: whereParts.joins,
|
|
3564
|
+
rawWhereClause: whereParts.rawClause,
|
|
3565
|
+
orderBySql,
|
|
3566
|
+
relSelect,
|
|
3567
|
+
takeVal: adjusted.takeVal,
|
|
3568
|
+
skipVal: paginationConfig.skipVal,
|
|
3569
|
+
ctx
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3288
3572
|
return buildListIncludeSpec({
|
|
3289
3573
|
relName,
|
|
3290
3574
|
relTable,
|
|
@@ -3354,14 +3638,14 @@ function buildIncludeSqlInternal(args, ctx) {
|
|
|
3354
3638
|
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
|
|
3355
3639
|
includePath: nextIncludePath,
|
|
3356
3640
|
visitPath: currentPath,
|
|
3357
|
-
depth
|
|
3641
|
+
depth,
|
|
3358
3642
|
stats
|
|
3359
3643
|
}))
|
|
3360
3644
|
);
|
|
3361
3645
|
}
|
|
3362
3646
|
return includes;
|
|
3363
3647
|
}
|
|
3364
|
-
function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
3648
|
+
function buildIncludeSql(args, model, schemas, parentAlias, params, dialect, outerHasLimit = true) {
|
|
3365
3649
|
const aliasGen = createAliasGenerator();
|
|
3366
3650
|
const stats = {
|
|
3367
3651
|
totalIncludes: 0,
|
|
@@ -3381,7 +3665,8 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3381
3665
|
includePath: [],
|
|
3382
3666
|
visitPath: [],
|
|
3383
3667
|
depth: 0,
|
|
3384
|
-
stats
|
|
3668
|
+
stats,
|
|
3669
|
+
outerHasLimit
|
|
3385
3670
|
});
|
|
3386
3671
|
}
|
|
3387
3672
|
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
@@ -3419,7 +3704,7 @@ function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
|
3419
3704
|
function defaultReferencesForCount(fkCount) {
|
|
3420
3705
|
if (fkCount === 1) return ["id"];
|
|
3421
3706
|
throw new Error(
|
|
3422
|
-
"Relation count for composite keys requires explicit references matching
|
|
3707
|
+
"Relation count for composite keys requires explicit references matching..."
|
|
3423
3708
|
);
|
|
3424
3709
|
}
|
|
3425
3710
|
function resolveCountKeyPairs(field) {
|
|
@@ -3528,6 +3813,307 @@ function joinNonEmpty(parts, sep) {
|
|
|
3528
3813
|
}
|
|
3529
3814
|
return result;
|
|
3530
3815
|
}
|
|
3816
|
+
|
|
3817
|
+
// src/builder/select/flat-join.ts
|
|
3818
|
+
function getPrimaryKeyField(model) {
|
|
3819
|
+
const scalarSet = getScalarFieldSet(model);
|
|
3820
|
+
for (const f of model.fields) {
|
|
3821
|
+
if (f.isId && !f.isRelation && scalarSet.has(f.name)) {
|
|
3822
|
+
return f.name;
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
if (scalarSet.has("id")) return "id";
|
|
3826
|
+
throw new Error(
|
|
3827
|
+
`Model ${model.name} has no primary key field. Models must have either a field with isId=true or a field named 'id'.`
|
|
3828
|
+
);
|
|
3829
|
+
}
|
|
3830
|
+
function findPrimaryKeyFields(model) {
|
|
3831
|
+
const pkFields = model.fields.filter((f) => f.isId && !f.isRelation);
|
|
3832
|
+
if (pkFields.length > 0) return pkFields.map((f) => f.name);
|
|
3833
|
+
const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
|
|
3834
|
+
if (idField) return ["id"];
|
|
3835
|
+
throw new Error(`Model ${model.name} has no primary key field`);
|
|
3836
|
+
}
|
|
3837
|
+
function getRelationModel(parentModel, relationName, schemas) {
|
|
3838
|
+
const field = parentModel.fields.find((f) => f.name === relationName);
|
|
3839
|
+
if (!(field == null ? void 0 : field.isRelation) || !field.relatedModel) {
|
|
3840
|
+
throw new Error(`Invalid relation ${relationName} on ${parentModel.name}`);
|
|
3841
|
+
}
|
|
3842
|
+
const relModel = schemas.find((m) => m.name === field.relatedModel);
|
|
3843
|
+
if (!relModel) {
|
|
3844
|
+
throw new Error(`Related model ${field.relatedModel} not found`);
|
|
3845
|
+
}
|
|
3846
|
+
return relModel;
|
|
3847
|
+
}
|
|
3848
|
+
function extractIncludeSpecFromArgs(args, model) {
|
|
3849
|
+
const includeSpec = {};
|
|
3850
|
+
const relationSet = getRelationFieldSet(model);
|
|
3851
|
+
if (args.include && isPlainObject(args.include)) {
|
|
3852
|
+
for (const [key, value] of Object.entries(args.include)) {
|
|
3853
|
+
if (value !== false) includeSpec[key] = value;
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
if (args.select && isPlainObject(args.select)) {
|
|
3857
|
+
for (const [key, value] of Object.entries(args.select)) {
|
|
3858
|
+
if (!relationSet.has(key)) continue;
|
|
3859
|
+
if (value === false) continue;
|
|
3860
|
+
if (value === true) {
|
|
3861
|
+
includeSpec[key] = true;
|
|
3862
|
+
continue;
|
|
3863
|
+
}
|
|
3864
|
+
if (isPlainObject(value)) {
|
|
3865
|
+
const v = value;
|
|
3866
|
+
if (isPlainObject(v.include) || isPlainObject(v.select)) {
|
|
3867
|
+
includeSpec[key] = value;
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
return includeSpec;
|
|
3873
|
+
}
|
|
3874
|
+
function hasChildPagination(relArgs) {
|
|
3875
|
+
if (!isPlainObject(relArgs)) return false;
|
|
3876
|
+
const args = relArgs;
|
|
3877
|
+
if (args.take !== void 0 && args.take !== null) return true;
|
|
3878
|
+
if (args.skip !== void 0 && args.skip !== null) return true;
|
|
3879
|
+
return false;
|
|
3880
|
+
}
|
|
3881
|
+
function extractNestedIncludeSpec(relArgs, relModel) {
|
|
3882
|
+
const relationSet = getRelationFieldSet(relModel);
|
|
3883
|
+
const out = {};
|
|
3884
|
+
if (!isPlainObject(relArgs)) return out;
|
|
3885
|
+
const obj = relArgs;
|
|
3886
|
+
if (isPlainObject(obj.include)) {
|
|
3887
|
+
for (const [k, v] of Object.entries(
|
|
3888
|
+
obj.include
|
|
3889
|
+
)) {
|
|
3890
|
+
if (!relationSet.has(k)) continue;
|
|
3891
|
+
if (v === false) continue;
|
|
3892
|
+
out[k] = v;
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
if (isPlainObject(obj.select)) {
|
|
3896
|
+
for (const [k, v] of Object.entries(
|
|
3897
|
+
obj.select
|
|
3898
|
+
)) {
|
|
3899
|
+
if (!relationSet.has(k)) continue;
|
|
3900
|
+
if (v === false) continue;
|
|
3901
|
+
if (v === true) {
|
|
3902
|
+
out[k] = true;
|
|
3903
|
+
continue;
|
|
3904
|
+
}
|
|
3905
|
+
if (isPlainObject(v)) {
|
|
3906
|
+
const vv = v;
|
|
3907
|
+
if (isPlainObject(vv.include) || isPlainObject(vv.select)) {
|
|
3908
|
+
out[k] = v;
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
return out;
|
|
3914
|
+
}
|
|
3915
|
+
function extractSelectedScalarFields(relArgs, relModel) {
|
|
3916
|
+
const scalarFields = relModel.fields.filter((f) => !f.isRelation).map((f) => f.name);
|
|
3917
|
+
const scalarSet = new Set(scalarFields);
|
|
3918
|
+
if (relArgs === true || !isPlainObject(relArgs)) {
|
|
3919
|
+
return { includeAllScalars: true, selected: scalarFields };
|
|
3920
|
+
}
|
|
3921
|
+
const obj = relArgs;
|
|
3922
|
+
if (!isPlainObject(obj.select)) {
|
|
3923
|
+
return { includeAllScalars: true, selected: scalarFields };
|
|
3924
|
+
}
|
|
3925
|
+
const sel = obj.select;
|
|
3926
|
+
const selected = [];
|
|
3927
|
+
for (const [k, v] of Object.entries(sel)) {
|
|
3928
|
+
if (!scalarSet.has(k)) continue;
|
|
3929
|
+
if (v === true) selected.push(k);
|
|
3930
|
+
}
|
|
3931
|
+
return { includeAllScalars: false, selected };
|
|
3932
|
+
}
|
|
3933
|
+
function uniqPreserveOrder(items) {
|
|
3934
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3935
|
+
const out = [];
|
|
3936
|
+
for (const it of items) {
|
|
3937
|
+
if (seen.has(it)) continue;
|
|
3938
|
+
seen.add(it);
|
|
3939
|
+
out.push(it);
|
|
3940
|
+
}
|
|
3941
|
+
return out;
|
|
3942
|
+
}
|
|
3943
|
+
function buildChildColumns(args) {
|
|
3944
|
+
const { relModel, relationName, childAlias, prefix, relArgs } = args;
|
|
3945
|
+
const fullPrefix = prefix ? `${prefix}.${relationName}` : relationName;
|
|
3946
|
+
const pkFields = findPrimaryKeyFields(relModel);
|
|
3947
|
+
const scalarSelection = extractSelectedScalarFields(relArgs, relModel);
|
|
3948
|
+
const selectedScalar = scalarSelection.selected;
|
|
3949
|
+
const required = uniqPreserveOrder([...pkFields, ...selectedScalar]);
|
|
3950
|
+
const columns = [];
|
|
3951
|
+
for (const fieldName of required) {
|
|
3952
|
+
const field = relModel.fields.find(
|
|
3953
|
+
(f) => f.name === fieldName && !f.isRelation
|
|
3954
|
+
);
|
|
3955
|
+
if (!field) continue;
|
|
3956
|
+
const colName = field.dbName || field.name;
|
|
3957
|
+
const quotedCol = quote(colName);
|
|
3958
|
+
columns.push(`${childAlias}.${quotedCol} AS "${fullPrefix}.${field.name}"`);
|
|
3959
|
+
}
|
|
3960
|
+
return columns;
|
|
3961
|
+
}
|
|
3962
|
+
function canUseNestedFlatJoin(relArgs, depth) {
|
|
3963
|
+
if (depth > 10) return false;
|
|
3964
|
+
if (!isPlainObject(relArgs)) return true;
|
|
3965
|
+
if (hasChildPagination(relArgs)) return false;
|
|
3966
|
+
const obj = relArgs;
|
|
3967
|
+
if (obj.include && isPlainObject(obj.include)) {
|
|
3968
|
+
for (const childValue of Object.values(
|
|
3969
|
+
obj.include
|
|
3970
|
+
)) {
|
|
3971
|
+
if (childValue !== false && !canUseNestedFlatJoin(childValue, depth + 1))
|
|
3972
|
+
return false;
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
if (obj.select && isPlainObject(obj.select)) {
|
|
3976
|
+
for (const childValue of Object.values(
|
|
3977
|
+
obj.select
|
|
3978
|
+
)) {
|
|
3979
|
+
if (childValue !== false && !canUseNestedFlatJoin(childValue, depth + 1))
|
|
3980
|
+
return false;
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
return true;
|
|
3984
|
+
}
|
|
3985
|
+
function canUseFlatJoinForAll(includeSpec) {
|
|
3986
|
+
for (const value of Object.values(includeSpec)) {
|
|
3987
|
+
if (value === false) continue;
|
|
3988
|
+
if (!canUseNestedFlatJoin(value, 0)) return false;
|
|
3989
|
+
}
|
|
3990
|
+
return true;
|
|
3991
|
+
}
|
|
3992
|
+
function buildNestedJoins(parentModel, parentAlias, includeSpec, schemas, dialect, prefix, aliasCounter, depth = 0) {
|
|
3993
|
+
if (depth > 10) {
|
|
3994
|
+
throw new Error(
|
|
3995
|
+
`Nested joins exceeded maximum depth of 10 at prefix '${prefix}'`
|
|
3996
|
+
);
|
|
3997
|
+
}
|
|
3998
|
+
const joins = [];
|
|
3999
|
+
const selects = [];
|
|
4000
|
+
const orderBy = [];
|
|
4001
|
+
for (const [relName, relValue] of Object.entries(includeSpec)) {
|
|
4002
|
+
if (relValue === false) continue;
|
|
4003
|
+
const field = parentModel.fields.find((f) => f.name === relName);
|
|
4004
|
+
if (!isValidRelationField(field)) continue;
|
|
4005
|
+
const relModel = getRelationModel(parentModel, relName, schemas);
|
|
4006
|
+
const relTable = buildTableReference(
|
|
4007
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
4008
|
+
relModel.tableName,
|
|
4009
|
+
dialect
|
|
4010
|
+
);
|
|
4011
|
+
const childAlias = `fj_${aliasCounter.count++}`;
|
|
4012
|
+
const joinCond = joinCondition(
|
|
4013
|
+
field,
|
|
4014
|
+
parentModel,
|
|
4015
|
+
relModel,
|
|
4016
|
+
parentAlias,
|
|
4017
|
+
childAlias
|
|
4018
|
+
);
|
|
4019
|
+
joins.push(`LEFT JOIN ${relTable} ${childAlias} ON ${joinCond}`);
|
|
4020
|
+
selects.push(
|
|
4021
|
+
...buildChildColumns({
|
|
4022
|
+
relModel,
|
|
4023
|
+
relationName: relName,
|
|
4024
|
+
childAlias,
|
|
4025
|
+
prefix,
|
|
4026
|
+
relArgs: relValue
|
|
4027
|
+
})
|
|
4028
|
+
);
|
|
4029
|
+
const childPkFields = findPrimaryKeyFields(relModel);
|
|
4030
|
+
for (const pkField of childPkFields) {
|
|
4031
|
+
orderBy.push(
|
|
4032
|
+
`${childAlias}.${quoteColumn(relModel, pkField)} ASC NULLS LAST`
|
|
4033
|
+
);
|
|
4034
|
+
}
|
|
4035
|
+
const nested = extractNestedIncludeSpec(relValue, relModel);
|
|
4036
|
+
if (Object.keys(nested).length > 0) {
|
|
4037
|
+
const nestedPrefix = prefix ? `${prefix}.${relName}` : relName;
|
|
4038
|
+
const deeper = buildNestedJoins(
|
|
4039
|
+
relModel,
|
|
4040
|
+
childAlias,
|
|
4041
|
+
nested,
|
|
4042
|
+
schemas,
|
|
4043
|
+
dialect,
|
|
4044
|
+
nestedPrefix,
|
|
4045
|
+
aliasCounter,
|
|
4046
|
+
depth + 1
|
|
4047
|
+
);
|
|
4048
|
+
joins.push(...deeper.joins);
|
|
4049
|
+
selects.push(...deeper.selects);
|
|
4050
|
+
orderBy.push(...deeper.orderBy);
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
return { joins, selects, orderBy };
|
|
4054
|
+
}
|
|
4055
|
+
function buildFlatJoinSql(spec) {
|
|
4056
|
+
const {
|
|
4057
|
+
select,
|
|
4058
|
+
from,
|
|
4059
|
+
whereClause,
|
|
4060
|
+
whereJoins,
|
|
4061
|
+
orderBy,
|
|
4062
|
+
dialect,
|
|
4063
|
+
model,
|
|
4064
|
+
schemas,
|
|
4065
|
+
args
|
|
4066
|
+
} = spec;
|
|
4067
|
+
const includeSpec = extractIncludeSpecFromArgs(args, model);
|
|
4068
|
+
if (Object.keys(includeSpec).length === 0) {
|
|
4069
|
+
return { sql: "", requiresReduction: false, includeSpec: {} };
|
|
4070
|
+
}
|
|
4071
|
+
if (!canUseFlatJoinForAll(includeSpec)) {
|
|
4072
|
+
return { sql: "", requiresReduction: false, includeSpec: {} };
|
|
4073
|
+
}
|
|
4074
|
+
const baseJoins = whereJoins.length > 0 ? whereJoins.join(" ") : "";
|
|
4075
|
+
const baseWhere = whereClause && whereClause !== "1=1" ? `WHERE ${whereClause}` : "";
|
|
4076
|
+
const baseOrderBy = orderBy ? `ORDER BY ${orderBy}` : "";
|
|
4077
|
+
let baseSubquery = `
|
|
4078
|
+
SELECT * FROM ${from.table} ${from.alias}
|
|
4079
|
+
${baseJoins}
|
|
4080
|
+
${baseWhere}
|
|
4081
|
+
${baseOrderBy}
|
|
4082
|
+
`.trim();
|
|
4083
|
+
baseSubquery = appendPagination(baseSubquery, spec);
|
|
4084
|
+
const aliasCounter = { count: 0 };
|
|
4085
|
+
const built = buildNestedJoins(
|
|
4086
|
+
model,
|
|
4087
|
+
from.alias,
|
|
4088
|
+
includeSpec,
|
|
4089
|
+
schemas,
|
|
4090
|
+
dialect,
|
|
4091
|
+
"",
|
|
4092
|
+
aliasCounter,
|
|
4093
|
+
0
|
|
4094
|
+
);
|
|
4095
|
+
if (built.joins.length === 0) {
|
|
4096
|
+
return { sql: "", requiresReduction: false, includeSpec: {} };
|
|
4097
|
+
}
|
|
4098
|
+
const baseSelect = (select != null ? select : "").trim();
|
|
4099
|
+
const allSelects = [baseSelect, ...built.selects].filter((s) => s && s.trim().length > 0).join(SQL_SEPARATORS.FIELD_LIST);
|
|
4100
|
+
if (!allSelects) {
|
|
4101
|
+
throw new Error("Flat-join SELECT requires at least one selected field");
|
|
4102
|
+
}
|
|
4103
|
+
const pkField = getPrimaryKeyField(model);
|
|
4104
|
+
const orderByParts = [];
|
|
4105
|
+
if (orderBy) orderByParts.push(orderBy);
|
|
4106
|
+
orderByParts.push(`${from.alias}.${quoteColumn(model, pkField)} ASC`);
|
|
4107
|
+
orderByParts.push(...built.orderBy);
|
|
4108
|
+
const finalOrderBy = orderByParts.join(", ");
|
|
4109
|
+
const sql = `
|
|
4110
|
+
SELECT ${allSelects}
|
|
4111
|
+
FROM (${baseSubquery}) ${from.alias}
|
|
4112
|
+
${built.joins.join(" ")}
|
|
4113
|
+
ORDER BY ${finalOrderBy}
|
|
4114
|
+
`.trim();
|
|
4115
|
+
return { sql, requiresReduction: true, includeSpec };
|
|
4116
|
+
}
|
|
3531
4117
|
function buildWhereSql(conditions) {
|
|
3532
4118
|
if (!isNonEmptyArray(conditions)) return "";
|
|
3533
4119
|
return " " + SQL_TEMPLATES.WHERE + " " + conditions.join(SQL_SEPARATORS.CONDITION_AND);
|
|
@@ -3562,9 +4148,6 @@ function finalizeSql(sql, params, dialect) {
|
|
|
3562
4148
|
paramMappings: snapshot.mappings
|
|
3563
4149
|
};
|
|
3564
4150
|
}
|
|
3565
|
-
function isIdent(s) {
|
|
3566
|
-
return /^[A-Za-z_]\w*$/.test(s);
|
|
3567
|
-
}
|
|
3568
4151
|
function skipSpaces(s, i) {
|
|
3569
4152
|
while (i < s.length) {
|
|
3570
4153
|
const c = s.charCodeAt(i);
|
|
@@ -3658,11 +4241,6 @@ function parseSelectField(p, fromAlias) {
|
|
|
3658
4241
|
i = skipSpaces(p, i);
|
|
3659
4242
|
const a = readIdentOrQuoted(p, i);
|
|
3660
4243
|
const actualAlias = a.text.toLowerCase();
|
|
3661
|
-
if (!isIdent(a.text)) {
|
|
3662
|
-
throw new Error(
|
|
3663
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3664
|
-
);
|
|
3665
|
-
}
|
|
3666
4244
|
if (actualAlias !== fromLower) {
|
|
3667
4245
|
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3668
4246
|
}
|
|
@@ -3731,10 +4309,10 @@ function extractDistinctOrderEntries(spec) {
|
|
|
3731
4309
|
const value = item[field];
|
|
3732
4310
|
if (typeof value === "string") {
|
|
3733
4311
|
entries.push({ field, direction: value });
|
|
3734
|
-
|
|
3735
|
-
const obj = value;
|
|
3736
|
-
entries.push({ field, direction: obj.sort, nulls: obj.nulls });
|
|
4312
|
+
continue;
|
|
3737
4313
|
}
|
|
4314
|
+
const obj = value;
|
|
4315
|
+
entries.push({ field, direction: obj.direction, nulls: obj.nulls });
|
|
3738
4316
|
}
|
|
3739
4317
|
}
|
|
3740
4318
|
if (entries.length > 0) return entries;
|
|
@@ -3868,19 +4446,43 @@ function buildIncludeColumns(spec) {
|
|
|
3868
4446
|
const hasIncludes = isNonEmptyArray(includes);
|
|
3869
4447
|
const hasCountCols = isNonEmptyString(countCols);
|
|
3870
4448
|
if (!hasIncludes && !hasCountCols) {
|
|
3871
|
-
return {
|
|
4449
|
+
return {
|
|
4450
|
+
includeCols: "",
|
|
4451
|
+
selectWithIncludes: baseSelect,
|
|
4452
|
+
countJoins: [],
|
|
4453
|
+
includeJoins: []
|
|
4454
|
+
};
|
|
3872
4455
|
}
|
|
3873
4456
|
const emptyJson = dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3874
|
-
const
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
4457
|
+
const correlatedParts = [];
|
|
4458
|
+
const joinIncludeJoins = [];
|
|
4459
|
+
const joinIncludeSelects = [];
|
|
4460
|
+
if (hasIncludes) {
|
|
4461
|
+
for (const inc of includes) {
|
|
4462
|
+
if (inc.joinSql && inc.selectExpr) {
|
|
4463
|
+
joinIncludeJoins.push(inc.joinSql);
|
|
4464
|
+
joinIncludeSelects.push(inc.selectExpr);
|
|
4465
|
+
} else {
|
|
4466
|
+
const expr = inc.isOneToOne ? "(" + inc.sql + ")" : "COALESCE((" + inc.sql + "), " + emptyJson + ")";
|
|
4467
|
+
correlatedParts.push(
|
|
4468
|
+
expr + " " + SQL_TEMPLATES.AS + " " + quote(inc.name)
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
const correlatedCols = correlatedParts.join(SQL_SEPARATORS.FIELD_LIST);
|
|
4474
|
+
const joinSelectCols = joinIncludeSelects.join(SQL_SEPARATORS.FIELD_LIST);
|
|
4475
|
+
const allIncludeCols = joinNonEmpty(
|
|
4476
|
+
[correlatedCols, joinSelectCols, countCols],
|
|
3880
4477
|
SQL_SEPARATORS.FIELD_LIST
|
|
3881
4478
|
);
|
|
3882
|
-
const selectWithIncludes = buildSelectList(baseSelect,
|
|
3883
|
-
return {
|
|
4479
|
+
const selectWithIncludes = buildSelectList(baseSelect, allIncludeCols);
|
|
4480
|
+
return {
|
|
4481
|
+
includeCols: allIncludeCols,
|
|
4482
|
+
selectWithIncludes,
|
|
4483
|
+
countJoins,
|
|
4484
|
+
includeJoins: joinIncludeJoins
|
|
4485
|
+
};
|
|
3884
4486
|
}
|
|
3885
4487
|
function appendPagination(sql, spec) {
|
|
3886
4488
|
const { method, pagination, params } = spec;
|
|
@@ -3918,9 +4520,13 @@ function appendPagination(sql, spec) {
|
|
|
3918
4520
|
return parts.join(" ");
|
|
3919
4521
|
}
|
|
3920
4522
|
function hasWindowDistinct(spec) {
|
|
4523
|
+
if (spec.dialect !== "sqlite") return false;
|
|
3921
4524
|
const d = spec.distinct;
|
|
3922
4525
|
return isNotNullish(d) && isNonEmptyArray(d);
|
|
3923
4526
|
}
|
|
4527
|
+
function hasAnyDistinct(spec) {
|
|
4528
|
+
return isNotNullish(spec.distinct) && isNonEmptyArray(spec.distinct);
|
|
4529
|
+
}
|
|
3924
4530
|
function assertDistinctAllowed(method, enabled) {
|
|
3925
4531
|
if (enabled && method !== "findMany") {
|
|
3926
4532
|
throw new Error(
|
|
@@ -3928,11 +4534,6 @@ function assertDistinctAllowed(method, enabled) {
|
|
|
3928
4534
|
);
|
|
3929
4535
|
}
|
|
3930
4536
|
}
|
|
3931
|
-
function assertHasSelectFields(baseSelect, includeCols) {
|
|
3932
|
-
if (!isNonEmptyString(baseSelect) && !isNonEmptyString(includeCols)) {
|
|
3933
|
-
throw new Error("SELECT requires at least one selected field or include");
|
|
3934
|
-
}
|
|
3935
|
-
}
|
|
3936
4537
|
function withCountJoins(spec, countJoins, whereJoins) {
|
|
3937
4538
|
return __spreadProps(__spreadValues({}, spec), {
|
|
3938
4539
|
whereJoins: [...whereJoins || [], ...countJoins || []]
|
|
@@ -3959,6 +4560,63 @@ function pushWhere(parts, conditions) {
|
|
|
3959
4560
|
if (!isNonEmptyArray(conditions)) return;
|
|
3960
4561
|
parts.push(SQL_TEMPLATES.WHERE, conditions.join(SQL_SEPARATORS.CONDITION_AND));
|
|
3961
4562
|
}
|
|
4563
|
+
function extractIncludeSpec(args) {
|
|
4564
|
+
const includeSpec = {};
|
|
4565
|
+
if (args.include && isPlainObject(args.include)) {
|
|
4566
|
+
for (const [key, value] of Object.entries(args.include)) {
|
|
4567
|
+
if (value !== false) {
|
|
4568
|
+
includeSpec[key] = value;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
if (args.select && isPlainObject(args.select)) {
|
|
4573
|
+
for (const [key, value] of Object.entries(args.select)) {
|
|
4574
|
+
if (value !== false && value !== true && isPlainObject(value)) {
|
|
4575
|
+
const selectVal = value;
|
|
4576
|
+
if (selectVal.include || selectVal.select) {
|
|
4577
|
+
includeSpec[key] = value;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return includeSpec;
|
|
4583
|
+
}
|
|
4584
|
+
function hasNestedIncludes(includeSpec) {
|
|
4585
|
+
return Object.keys(includeSpec).length > 0;
|
|
4586
|
+
}
|
|
4587
|
+
function splitOrderByTerms(orderBy) {
|
|
4588
|
+
const raw = orderBy.trim();
|
|
4589
|
+
if (raw.length === 0) return [];
|
|
4590
|
+
return raw.split(SQL_SEPARATORS.ORDER_BY).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
4591
|
+
}
|
|
4592
|
+
function hasIdInOrderBy(orderBy, fromAlias) {
|
|
4593
|
+
const aliasLower = String(fromAlias).toLowerCase();
|
|
4594
|
+
const terms = splitOrderByTerms(orderBy).map((t) => t.toLowerCase());
|
|
4595
|
+
return terms.some(
|
|
4596
|
+
(t) => t.startsWith(aliasLower + ".id ") || t.startsWith(aliasLower + '."id" ') || t === aliasLower + ".id" || t === aliasLower + '."id"'
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
4599
|
+
function ensureIdTiebreakerOrderBy(orderBy, fromAlias, model) {
|
|
4600
|
+
var _a, _b;
|
|
4601
|
+
const idField = (_b = (_a = model == null ? void 0 : model.fields) == null ? void 0 : _a.find) == null ? void 0 : _b.call(
|
|
4602
|
+
_a,
|
|
4603
|
+
(f) => f.name === "id" && !f.isRelation
|
|
4604
|
+
);
|
|
4605
|
+
if (!idField) return orderBy;
|
|
4606
|
+
if (hasIdInOrderBy(orderBy, fromAlias)) return orderBy;
|
|
4607
|
+
const t = col(fromAlias, "id", model) + " ASC";
|
|
4608
|
+
return isNonEmptyString(orderBy) ? orderBy + ", " + t : t;
|
|
4609
|
+
}
|
|
4610
|
+
function ensurePostgresDistinctOrderBy(args) {
|
|
4611
|
+
const { orderBy, distinct, fromAlias, model } = args;
|
|
4612
|
+
const distinctTerms = distinct.map((f) => col(fromAlias, f, model) + " ASC");
|
|
4613
|
+
const existing = splitOrderByTerms(orderBy);
|
|
4614
|
+
const canKeepAsIs = existing.length >= distinctTerms.length && distinctTerms.every(
|
|
4615
|
+
(term, i) => existing[i].toLowerCase().startsWith(term.split(" ASC")[0].toLowerCase())
|
|
4616
|
+
);
|
|
4617
|
+
const merged = canKeepAsIs ? orderBy : [...distinctTerms, ...existing].join(SQL_SEPARATORS.ORDER_BY);
|
|
4618
|
+
return ensureIdTiebreakerOrderBy(merged, fromAlias, model);
|
|
4619
|
+
}
|
|
3962
4620
|
function constructFinalSql(spec) {
|
|
3963
4621
|
const {
|
|
3964
4622
|
select,
|
|
@@ -3972,15 +4630,36 @@ function constructFinalSql(spec) {
|
|
|
3972
4630
|
cursorClause,
|
|
3973
4631
|
params,
|
|
3974
4632
|
dialect,
|
|
3975
|
-
model
|
|
4633
|
+
model,
|
|
4634
|
+
includes,
|
|
4635
|
+
schemas,
|
|
4636
|
+
pagination,
|
|
4637
|
+
args
|
|
3976
4638
|
} = spec;
|
|
3977
4639
|
const useWindowDistinct = hasWindowDistinct(spec);
|
|
3978
4640
|
assertDistinctAllowed(method, useWindowDistinct);
|
|
3979
|
-
const
|
|
4641
|
+
const hasDistinct = hasAnyDistinct(spec);
|
|
4642
|
+
assertDistinctAllowed(method, hasDistinct);
|
|
4643
|
+
const includeSpec = extractIncludeSpec(args);
|
|
4644
|
+
const hasIncludes = hasNestedIncludes(includeSpec);
|
|
4645
|
+
const shouldUseFlatJoin = dialect === "postgres" && hasIncludes && canUseFlatJoinForAll(includeSpec);
|
|
4646
|
+
if (shouldUseFlatJoin) {
|
|
4647
|
+
const flatResult = buildFlatJoinSql(spec);
|
|
4648
|
+
if (flatResult.sql) {
|
|
4649
|
+
const baseSqlResult = finalizeSql(flatResult.sql, params, dialect);
|
|
4650
|
+
return {
|
|
4651
|
+
sql: baseSqlResult.sql,
|
|
4652
|
+
params: baseSqlResult.params,
|
|
4653
|
+
paramMappings: baseSqlResult.paramMappings,
|
|
4654
|
+
requiresReduction: true,
|
|
4655
|
+
includeSpec: flatResult.includeSpec
|
|
4656
|
+
};
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
const { includeCols, selectWithIncludes, countJoins, includeJoins } = buildIncludeColumns(spec);
|
|
3980
4660
|
if (useWindowDistinct) {
|
|
3981
|
-
const
|
|
3982
|
-
|
|
3983
|
-
const spec2 = withCountJoins(spec, countJoins, whereJoins);
|
|
4661
|
+
const allExtraJoins = [...countJoins, ...includeJoins];
|
|
4662
|
+
const spec2 = withCountJoins(spec, allExtraJoins, whereJoins);
|
|
3984
4663
|
let sql2 = buildSqliteDistinctQuery(spec2, selectWithIncludes).trim();
|
|
3985
4664
|
sql2 = appendPagination(sql2, spec);
|
|
3986
4665
|
return finalizeSql(sql2, params, dialect);
|
|
@@ -4002,9 +4681,22 @@ function constructFinalSql(spec) {
|
|
|
4002
4681
|
parts.push("CROSS JOIN", cteName);
|
|
4003
4682
|
}
|
|
4004
4683
|
pushJoinGroups(parts, whereJoins, countJoins);
|
|
4684
|
+
if (isNonEmptyArray(includeJoins)) {
|
|
4685
|
+
parts.push(includeJoins.join(" "));
|
|
4686
|
+
}
|
|
4005
4687
|
const conditions = buildConditions(whereClause, cursorClause);
|
|
4006
4688
|
pushWhere(parts, conditions);
|
|
4007
|
-
|
|
4689
|
+
let finalOrderBy = orderBy;
|
|
4690
|
+
if (dialect === "postgres" && isNonEmptyArray(distinct)) {
|
|
4691
|
+
finalOrderBy = ensurePostgresDistinctOrderBy({
|
|
4692
|
+
orderBy: orderBy || "",
|
|
4693
|
+
distinct: [...distinct],
|
|
4694
|
+
fromAlias: from.alias,
|
|
4695
|
+
model
|
|
4696
|
+
});
|
|
4697
|
+
}
|
|
4698
|
+
if (isNonEmptyString(finalOrderBy))
|
|
4699
|
+
parts.push(SQL_TEMPLATES.ORDER_BY, finalOrderBy);
|
|
4008
4700
|
let sql = parts.join(" ").trim();
|
|
4009
4701
|
sql = appendPagination(sql, spec);
|
|
4010
4702
|
return finalizeSql(sql, params, dialect);
|
|
@@ -4158,13 +4850,15 @@ function buildSelectSpec(input) {
|
|
|
4158
4850
|
whereResult.paramMappings,
|
|
4159
4851
|
whereResult.nextParamIndex
|
|
4160
4852
|
);
|
|
4853
|
+
const outerHasLimit = isNotNullish(take);
|
|
4161
4854
|
const includes = buildIncludeSql(
|
|
4162
4855
|
normalizedArgs,
|
|
4163
4856
|
model,
|
|
4164
4857
|
schemas,
|
|
4165
4858
|
alias,
|
|
4166
4859
|
params,
|
|
4167
|
-
dialect
|
|
4860
|
+
dialect,
|
|
4861
|
+
outerHasLimit
|
|
4168
4862
|
);
|
|
4169
4863
|
const cursorResult = buildCursorClauseIfAny({
|
|
4170
4864
|
cursor,
|
|
@@ -4344,6 +5038,14 @@ function buildInComparison(expr, op, val, params, dialect) {
|
|
|
4344
5038
|
if (val.length === 0) {
|
|
4345
5039
|
return op === Ops.IN ? "0=1" : "1=1";
|
|
4346
5040
|
}
|
|
5041
|
+
if (dialect === "sqlite" && val.length <= 30) {
|
|
5042
|
+
const placeholders = [];
|
|
5043
|
+
for (const item of val) {
|
|
5044
|
+
placeholders.push(params.add(item));
|
|
5045
|
+
}
|
|
5046
|
+
const list = placeholders.join(", ");
|
|
5047
|
+
return op === Ops.IN ? `${expr} IN (${list})` : `${expr} NOT IN (${list})`;
|
|
5048
|
+
}
|
|
4347
5049
|
const paramValue = prepareArrayParam(val, dialect);
|
|
4348
5050
|
const placeholder = params.add(paramValue);
|
|
4349
5051
|
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
@@ -4594,6 +5296,7 @@ function buildAggregateSql(args, whereResult, tableName, alias, model) {
|
|
|
4594
5296
|
throw new Error("buildAggregateSql requires at least one aggregate field");
|
|
4595
5297
|
}
|
|
4596
5298
|
const selectClause = aggFields.join(SQL_SEPARATORS.FIELD_LIST);
|
|
5299
|
+
const joinsPart = whereResult.joins && whereResult.joins.length > 0 ? whereResult.joins.join(" ") : "";
|
|
4597
5300
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4598
5301
|
const parts = [
|
|
4599
5302
|
SQL_TEMPLATES.SELECT,
|
|
@@ -4602,6 +5305,7 @@ function buildAggregateSql(args, whereResult, tableName, alias, model) {
|
|
|
4602
5305
|
tableName,
|
|
4603
5306
|
alias
|
|
4604
5307
|
];
|
|
5308
|
+
if (joinsPart) parts.push(joinsPart);
|
|
4605
5309
|
if (whereClause) parts.push(whereClause);
|
|
4606
5310
|
const sql = parts.join(" ").trim();
|
|
4607
5311
|
validateSelectQuery(sql);
|
|
@@ -4654,7 +5358,9 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4654
5358
|
byFields
|
|
4655
5359
|
);
|
|
4656
5360
|
const havingClause = buildGroupByHaving(args, alias, params, model, d);
|
|
5361
|
+
const joinsPart = whereResult.joins && whereResult.joins.length > 0 ? whereResult.joins.join(" ") : "";
|
|
4657
5362
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
5363
|
+
const orderBySql = isNotNullish(args.orderBy) ? buildOrderBy(args.orderBy, alias, d, model) : "";
|
|
4658
5364
|
const parts = [
|
|
4659
5365
|
SQL_TEMPLATES.SELECT,
|
|
4660
5366
|
selectFields,
|
|
@@ -4662,9 +5368,21 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4662
5368
|
tableName,
|
|
4663
5369
|
alias
|
|
4664
5370
|
];
|
|
5371
|
+
if (joinsPart) parts.push(joinsPart);
|
|
4665
5372
|
if (whereClause) parts.push(whereClause);
|
|
4666
5373
|
parts.push(SQL_TEMPLATES.GROUP_BY, groupFields);
|
|
4667
5374
|
if (havingClause) parts.push(havingClause);
|
|
5375
|
+
if (orderBySql) {
|
|
5376
|
+
parts.push(SQL_TEMPLATES.ORDER_BY, orderBySql);
|
|
5377
|
+
}
|
|
5378
|
+
if (isNotNullish(args.take)) {
|
|
5379
|
+
const ph = addAutoScoped(params, args.take, "groupBy.take");
|
|
5380
|
+
parts.push(SQL_TEMPLATES.LIMIT, ph);
|
|
5381
|
+
}
|
|
5382
|
+
if (isNotNullish(args.skip)) {
|
|
5383
|
+
const ph = addAutoScoped(params, args.skip, "groupBy.skip");
|
|
5384
|
+
parts.push(SQL_TEMPLATES.OFFSET, ph);
|
|
5385
|
+
}
|
|
4668
5386
|
const sql = parts.join(" ").trim();
|
|
4669
5387
|
const snapshot = params.snapshot();
|
|
4670
5388
|
const allParams = [...whereResult.params, ...snapshot.params];
|
|
@@ -4677,32 +5395,25 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4677
5395
|
paramMappings: allMappings
|
|
4678
5396
|
};
|
|
4679
5397
|
}
|
|
4680
|
-
function
|
|
4681
|
-
|
|
5398
|
+
function findPrimaryKeyFields2(model) {
|
|
5399
|
+
const pkFields = model.fields.filter((f) => f.isId && !f.isRelation);
|
|
5400
|
+
if (pkFields.length > 0) return pkFields.map((f) => f.name);
|
|
5401
|
+
const defaultId = model.fields.find((f) => f.name === "id" && !f.isRelation);
|
|
5402
|
+
if (defaultId) return ["id"];
|
|
5403
|
+
return [];
|
|
4682
5404
|
}
|
|
4683
|
-
function
|
|
4684
|
-
|
|
5405
|
+
function normalizeCountArgs(argsOrSkip) {
|
|
5406
|
+
if (isPlainObject(argsOrSkip)) return argsOrSkip;
|
|
5407
|
+
if (argsOrSkip === void 0 || argsOrSkip === null) return {};
|
|
5408
|
+
return { skip: argsOrSkip };
|
|
4685
5409
|
}
|
|
4686
|
-
function
|
|
4687
|
-
if (
|
|
4688
|
-
|
|
4689
|
-
}
|
|
4690
|
-
if (schemaParser.isDynamicParameter(skip)) {
|
|
4691
|
-
throw new Error(
|
|
4692
|
-
"count() with skip is not supported because it produces nondeterministic results. Dynamic skip cannot be validated at build time. Use findMany().length or add explicit orderBy + cursor/skip logic in a deterministic query."
|
|
4693
|
-
);
|
|
4694
|
-
}
|
|
4695
|
-
const skipValue = parseSkipValue(skip);
|
|
4696
|
-
if (isPositiveInteger(skipValue)) {
|
|
4697
|
-
throw new Error(
|
|
4698
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4699
|
-
);
|
|
5410
|
+
function assertNoNegativeTake(args) {
|
|
5411
|
+
if (typeof args.take === "number" && args.take < 0) {
|
|
5412
|
+
throw new Error("Negative take is not supported for count()");
|
|
4700
5413
|
}
|
|
4701
5414
|
}
|
|
4702
|
-
function
|
|
4703
|
-
|
|
4704
|
-
assertSafeTableRef(tableName);
|
|
4705
|
-
validateSkipParameter(skip);
|
|
5415
|
+
function buildSimpleCountSql(whereResult, tableName, alias) {
|
|
5416
|
+
const joinsPart = whereResult.joins && whereResult.joins.length > 0 ? whereResult.joins.join(" ") : "";
|
|
4706
5417
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4707
5418
|
const parts = [
|
|
4708
5419
|
SQL_TEMPLATES.SELECT,
|
|
@@ -4713,6 +5424,7 @@ function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
|
4713
5424
|
tableName,
|
|
4714
5425
|
alias
|
|
4715
5426
|
];
|
|
5427
|
+
if (joinsPart) parts.push(joinsPart);
|
|
4716
5428
|
if (whereClause) parts.push(whereClause);
|
|
4717
5429
|
const sql = parts.join(" ").trim();
|
|
4718
5430
|
validateSelectQuery(sql);
|
|
@@ -4723,6 +5435,49 @@ function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
|
4723
5435
|
paramMappings: whereResult.paramMappings
|
|
4724
5436
|
};
|
|
4725
5437
|
}
|
|
5438
|
+
function buildCountSql(whereResult, tableName, alias, argsOrSkip, dialect, model, schemas) {
|
|
5439
|
+
assertSafeAlias(alias);
|
|
5440
|
+
assertSafeTableRef(tableName);
|
|
5441
|
+
const args = normalizeCountArgs(argsOrSkip);
|
|
5442
|
+
assertNoNegativeTake(args);
|
|
5443
|
+
if (!model) {
|
|
5444
|
+
return buildSimpleCountSql(whereResult, tableName, alias);
|
|
5445
|
+
}
|
|
5446
|
+
const pkFields = findPrimaryKeyFields2(model);
|
|
5447
|
+
const distinctFields = isNonEmptyArray(args.distinct) ? args.distinct.map((x) => String(x)).filter((x) => x) : [];
|
|
5448
|
+
const selectFields = distinctFields.length > 0 ? distinctFields : pkFields;
|
|
5449
|
+
if (selectFields.length === 0) {
|
|
5450
|
+
return buildSimpleCountSql(whereResult, tableName, alias);
|
|
5451
|
+
}
|
|
5452
|
+
const select = {};
|
|
5453
|
+
for (const f of selectFields) select[f] = true;
|
|
5454
|
+
const subArgs = __spreadProps(__spreadValues({}, args), {
|
|
5455
|
+
include: void 0,
|
|
5456
|
+
select
|
|
5457
|
+
});
|
|
5458
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
5459
|
+
const subSchemas = Array.isArray(schemas) && schemas.length > 0 ? schemas : [model];
|
|
5460
|
+
const sub = buildSelectSql({
|
|
5461
|
+
method: "findMany",
|
|
5462
|
+
args: subArgs,
|
|
5463
|
+
model,
|
|
5464
|
+
schemas: subSchemas,
|
|
5465
|
+
from: { tableName, alias },
|
|
5466
|
+
whereResult,
|
|
5467
|
+
dialect: d
|
|
5468
|
+
});
|
|
5469
|
+
const countAlias = quote("__count_sub");
|
|
5470
|
+
const sql = `${SQL_TEMPLATES.SELECT} ${SQL_TEMPLATES.COUNT_ALL} ${SQL_TEMPLATES.AS} ${quote(
|
|
5471
|
+
"_count._all"
|
|
5472
|
+
)} ${SQL_TEMPLATES.FROM} (${sub.sql}) ${SQL_TEMPLATES.AS} ${countAlias}`;
|
|
5473
|
+
validateSelectQuery(sql);
|
|
5474
|
+
validateParamConsistency(sql, sub.params);
|
|
5475
|
+
return {
|
|
5476
|
+
sql,
|
|
5477
|
+
params: sub.params,
|
|
5478
|
+
paramMappings: sub.paramMappings
|
|
5479
|
+
};
|
|
5480
|
+
}
|
|
4726
5481
|
function safeAlias(input) {
|
|
4727
5482
|
const raw = String(input).toLowerCase();
|
|
4728
5483
|
const cleaned = raw.replace(/[^a-z0-9_]/g, "_");
|
|
@@ -4734,6 +5489,17 @@ function safeAlias(input) {
|
|
|
4734
5489
|
}
|
|
4735
5490
|
return base;
|
|
4736
5491
|
}
|
|
5492
|
+
function isPrismaMethod(v) {
|
|
5493
|
+
return v === "findMany" || v === "findFirst" || v === "findUnique" || v === "aggregate" || v === "groupBy" || v === "count";
|
|
5494
|
+
}
|
|
5495
|
+
function resolveMethod(directive) {
|
|
5496
|
+
var _a, _b;
|
|
5497
|
+
const m = directive == null ? void 0 : directive.method;
|
|
5498
|
+
if (isPrismaMethod(m)) return m;
|
|
5499
|
+
const pm = (_b = (_a = directive == null ? void 0 : directive.query) == null ? void 0 : _a.processed) == null ? void 0 : _b.method;
|
|
5500
|
+
if (isPrismaMethod(pm)) return pm;
|
|
5501
|
+
return "findMany";
|
|
5502
|
+
}
|
|
4737
5503
|
function buildSqlResult(args) {
|
|
4738
5504
|
const {
|
|
4739
5505
|
method,
|
|
@@ -4763,7 +5529,11 @@ function buildSqlResult(args) {
|
|
|
4763
5529
|
whereResult,
|
|
4764
5530
|
tableName,
|
|
4765
5531
|
alias,
|
|
4766
|
-
processed
|
|
5532
|
+
processed,
|
|
5533
|
+
dialect,
|
|
5534
|
+
modelDef,
|
|
5535
|
+
schemaModels
|
|
5536
|
+
);
|
|
4767
5537
|
}
|
|
4768
5538
|
return buildSelectSql({
|
|
4769
5539
|
method,
|
|
@@ -4851,7 +5621,43 @@ function buildMainWhere(args) {
|
|
|
4851
5621
|
dialect
|
|
4852
5622
|
});
|
|
4853
5623
|
}
|
|
5624
|
+
function extractIncludeSpec2(processed, modelDef) {
|
|
5625
|
+
const includeSpec = {};
|
|
5626
|
+
const relationSet = new Set(
|
|
5627
|
+
Array.isArray(modelDef == null ? void 0 : modelDef.fields) ? modelDef.fields.filter((f) => f && f.isRelation && typeof f.name === "string").map((f) => f.name) : []
|
|
5628
|
+
);
|
|
5629
|
+
if (processed.include && isPlainObject(processed.include)) {
|
|
5630
|
+
for (const [key, value] of Object.entries(processed.include)) {
|
|
5631
|
+
if (!relationSet.has(key)) continue;
|
|
5632
|
+
if (value !== false) {
|
|
5633
|
+
includeSpec[key] = value;
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
}
|
|
5637
|
+
if (processed.select && isPlainObject(processed.select)) {
|
|
5638
|
+
for (const [key, value] of Object.entries(processed.select)) {
|
|
5639
|
+
if (!relationSet.has(key)) continue;
|
|
5640
|
+
if (value === false) continue;
|
|
5641
|
+
if (value === true) {
|
|
5642
|
+
includeSpec[key] = true;
|
|
5643
|
+
continue;
|
|
5644
|
+
}
|
|
5645
|
+
if (isPlainObject(value)) {
|
|
5646
|
+
const selectVal = value;
|
|
5647
|
+
if (selectVal.include || selectVal.select) {
|
|
5648
|
+
includeSpec[key] = value;
|
|
5649
|
+
} else {
|
|
5650
|
+
includeSpec[key] = true;
|
|
5651
|
+
}
|
|
5652
|
+
} else {
|
|
5653
|
+
includeSpec[key] = true;
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
return includeSpec;
|
|
5658
|
+
}
|
|
4854
5659
|
function buildAndNormalizeSql(args) {
|
|
5660
|
+
var _a;
|
|
4855
5661
|
const {
|
|
4856
5662
|
method,
|
|
4857
5663
|
processed,
|
|
@@ -4872,14 +5678,30 @@ function buildAndNormalizeSql(args) {
|
|
|
4872
5678
|
schemaModels,
|
|
4873
5679
|
dialect
|
|
4874
5680
|
});
|
|
4875
|
-
|
|
5681
|
+
const normalized = normalizeSqlAndMappingsForDialect(
|
|
4876
5682
|
sqlResult.sql,
|
|
4877
5683
|
sqlResult.paramMappings,
|
|
4878
5684
|
dialect
|
|
4879
5685
|
);
|
|
5686
|
+
const includeSpec = (_a = sqlResult.includeSpec && isPlainObject(sqlResult.includeSpec) ? sqlResult.includeSpec : null) != null ? _a : extractIncludeSpec2(processed, modelDef);
|
|
5687
|
+
const requiresReduction = sqlResult.requiresReduction === true;
|
|
5688
|
+
return {
|
|
5689
|
+
sql: normalized.sql,
|
|
5690
|
+
paramMappings: normalized.paramMappings,
|
|
5691
|
+
requiresReduction,
|
|
5692
|
+
includeSpec
|
|
5693
|
+
};
|
|
4880
5694
|
}
|
|
4881
5695
|
function finalizeDirective(args) {
|
|
4882
|
-
const {
|
|
5696
|
+
const {
|
|
5697
|
+
directive,
|
|
5698
|
+
method,
|
|
5699
|
+
normalizedSql,
|
|
5700
|
+
normalizedMappings,
|
|
5701
|
+
dialect,
|
|
5702
|
+
requiresReduction,
|
|
5703
|
+
includeSpec
|
|
5704
|
+
} = args;
|
|
4883
5705
|
const params = normalizedMappings.map((m) => {
|
|
4884
5706
|
var _a;
|
|
4885
5707
|
return (_a = m.value) != null ? _a : void 0;
|
|
@@ -4887,12 +5709,14 @@ function finalizeDirective(args) {
|
|
|
4887
5709
|
validateParamConsistencyByDialect(normalizedSql, params, dialect);
|
|
4888
5710
|
const { staticParams, dynamicKeys, paramOrder } = buildParamsFromMappings(normalizedMappings);
|
|
4889
5711
|
return {
|
|
4890
|
-
method
|
|
5712
|
+
method,
|
|
4891
5713
|
sql: normalizedSql,
|
|
4892
5714
|
staticParams,
|
|
4893
5715
|
dynamicKeys,
|
|
4894
5716
|
paramOrder,
|
|
4895
5717
|
paramMappings: normalizedMappings,
|
|
5718
|
+
requiresReduction,
|
|
5719
|
+
includeSpec,
|
|
4896
5720
|
originalDirective: directive
|
|
4897
5721
|
};
|
|
4898
5722
|
}
|
|
@@ -4908,8 +5732,8 @@ function generateSQL(directive) {
|
|
|
4908
5732
|
modelDef,
|
|
4909
5733
|
dialect
|
|
4910
5734
|
});
|
|
4911
|
-
const method = directive
|
|
4912
|
-
const
|
|
5735
|
+
const method = resolveMethod(directive);
|
|
5736
|
+
const built = buildAndNormalizeSql({
|
|
4913
5737
|
method,
|
|
4914
5738
|
processed: query.processed,
|
|
4915
5739
|
whereResult,
|
|
@@ -4921,9 +5745,12 @@ function generateSQL(directive) {
|
|
|
4921
5745
|
});
|
|
4922
5746
|
return finalizeDirective({
|
|
4923
5747
|
directive,
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
5748
|
+
method,
|
|
5749
|
+
normalizedSql: built.sql,
|
|
5750
|
+
normalizedMappings: built.paramMappings,
|
|
5751
|
+
dialect,
|
|
5752
|
+
requiresReduction: built.requiresReduction,
|
|
5753
|
+
includeSpec: built.includeSpec
|
|
4927
5754
|
});
|
|
4928
5755
|
}
|
|
4929
5756
|
|
|
@@ -5511,7 +6338,9 @@ function buildSQLFull(model, models, method, args, dialect) {
|
|
|
5511
6338
|
whereResult,
|
|
5512
6339
|
tableName,
|
|
5513
6340
|
alias,
|
|
5514
|
-
args.skip
|
|
6341
|
+
args.skip,
|
|
6342
|
+
dialect
|
|
6343
|
+
);
|
|
5515
6344
|
break;
|
|
5516
6345
|
default:
|
|
5517
6346
|
result = buildSelectSql({
|
|
@@ -5524,14 +6353,25 @@ function buildSQLFull(model, models, method, args, dialect) {
|
|
|
5524
6353
|
dialect
|
|
5525
6354
|
});
|
|
5526
6355
|
}
|
|
5527
|
-
|
|
6356
|
+
const sqlResult = dialect === "sqlite" ? toSqliteParams(result.sql, result.params) : { sql: result.sql, params: [...result.params] };
|
|
6357
|
+
return __spreadProps(__spreadValues({}, sqlResult), {
|
|
6358
|
+
paramMappings: result.paramMappings,
|
|
6359
|
+
requiresReduction: result.requiresReduction,
|
|
6360
|
+
includeSpec: result.includeSpec
|
|
6361
|
+
});
|
|
5528
6362
|
}
|
|
5529
6363
|
function buildSQLWithCache(model, models, method, args, dialect) {
|
|
5530
6364
|
const cacheKey = canonicalizeQuery(model.name, method, args, dialect);
|
|
5531
6365
|
const cached = queryCache.get(cacheKey);
|
|
5532
6366
|
if (cached) {
|
|
5533
6367
|
queryCacheStats.hit();
|
|
5534
|
-
return {
|
|
6368
|
+
return {
|
|
6369
|
+
sql: cached.sql,
|
|
6370
|
+
params: [...cached.params],
|
|
6371
|
+
paramMappings: cached.paramMappings,
|
|
6372
|
+
requiresReduction: cached.requiresReduction,
|
|
6373
|
+
includeSpec: cached.includeSpec
|
|
6374
|
+
};
|
|
5535
6375
|
}
|
|
5536
6376
|
queryCacheStats.miss();
|
|
5537
6377
|
const fastResult = tryFastPath(model, method, args, dialect);
|
|
@@ -5543,61 +6383,20 @@ function buildSQLWithCache(model, models, method, args, dialect) {
|
|
|
5543
6383
|
return fastResult;
|
|
5544
6384
|
}
|
|
5545
6385
|
const result = buildSQLFull(model, models, method, args, dialect);
|
|
5546
|
-
queryCache.set(cacheKey, {
|
|
6386
|
+
queryCache.set(cacheKey, {
|
|
6387
|
+
sql: result.sql,
|
|
6388
|
+
params: [...result.params],
|
|
6389
|
+
paramMappings: result.paramMappings,
|
|
6390
|
+
requiresReduction: result.requiresReduction,
|
|
6391
|
+
includeSpec: result.includeSpec
|
|
6392
|
+
});
|
|
5547
6393
|
return result;
|
|
5548
6394
|
}
|
|
5549
6395
|
|
|
5550
6396
|
// src/result-transformers.ts
|
|
5551
|
-
function parseAggregateValue(value) {
|
|
5552
|
-
if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
|
|
5553
|
-
return parseFloat(value);
|
|
5554
|
-
}
|
|
5555
|
-
return value;
|
|
5556
|
-
}
|
|
5557
|
-
function transformGroupByResults(results) {
|
|
5558
|
-
return results.map((row) => {
|
|
5559
|
-
const raw = row;
|
|
5560
|
-
const parsed = {};
|
|
5561
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
5562
|
-
const parts = key.split(".");
|
|
5563
|
-
if (parts.length === 2) {
|
|
5564
|
-
const [group, field] = parts;
|
|
5565
|
-
if (!parsed[group]) parsed[group] = {};
|
|
5566
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
5567
|
-
} else {
|
|
5568
|
-
parsed[key] = value;
|
|
5569
|
-
}
|
|
5570
|
-
}
|
|
5571
|
-
return parsed;
|
|
5572
|
-
});
|
|
5573
|
-
}
|
|
5574
|
-
function transformCountResults(results) {
|
|
5575
|
-
var _a, _b;
|
|
5576
|
-
const result = results[0];
|
|
5577
|
-
const count = (_b = (_a = result == null ? void 0 : result["_count._all"]) != null ? _a : result == null ? void 0 : result.count) != null ? _b : 0;
|
|
5578
|
-
return typeof count === "string" ? parseInt(count, 10) : count;
|
|
5579
|
-
}
|
|
5580
|
-
function transformAggregateResults(results) {
|
|
5581
|
-
const raw = results[0] || {};
|
|
5582
|
-
const parsed = {};
|
|
5583
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
5584
|
-
const parts = key.split(".");
|
|
5585
|
-
if (parts.length === 2) {
|
|
5586
|
-
const [group, field] = parts;
|
|
5587
|
-
if (!parsed[group]) parsed[group] = {};
|
|
5588
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
5589
|
-
} else {
|
|
5590
|
-
parsed[key] = value;
|
|
5591
|
-
}
|
|
5592
|
-
}
|
|
5593
|
-
return parsed;
|
|
5594
|
-
}
|
|
5595
6397
|
var RESULT_TRANSFORMERS = {
|
|
5596
6398
|
findFirst: (results) => results[0] || null,
|
|
5597
|
-
findUnique: (results) => results[0] || null
|
|
5598
|
-
count: transformCountResults,
|
|
5599
|
-
aggregate: transformAggregateResults,
|
|
5600
|
-
groupBy: transformGroupByResults
|
|
6399
|
+
findUnique: (results) => results[0] || null
|
|
5601
6400
|
};
|
|
5602
6401
|
function transformQueryResults(method, results) {
|
|
5603
6402
|
const transformer = RESULT_TRANSFORMERS[method];
|
|
@@ -6520,6 +7319,321 @@ function createTransactionExecutor(deps) {
|
|
|
6520
7319
|
};
|
|
6521
7320
|
}
|
|
6522
7321
|
|
|
7322
|
+
// src/builder/select/reducer.ts
|
|
7323
|
+
var PK_FIELDS_CACHE = /* @__PURE__ */ new WeakMap();
|
|
7324
|
+
var SCALAR_FIELDS_CACHE = /* @__PURE__ */ new WeakMap();
|
|
7325
|
+
var JSON_FIELD_SET_CACHE = /* @__PURE__ */ new WeakMap();
|
|
7326
|
+
function findPrimaryKeyFieldsCached(model) {
|
|
7327
|
+
const cached = PK_FIELDS_CACHE.get(model);
|
|
7328
|
+
if (cached) return cached;
|
|
7329
|
+
const pkFields = model.fields.filter((f) => f.isId && !f.isRelation);
|
|
7330
|
+
if (pkFields.length > 0) {
|
|
7331
|
+
const out = pkFields.map((f) => f.name);
|
|
7332
|
+
PK_FIELDS_CACHE.set(model, out);
|
|
7333
|
+
return out;
|
|
7334
|
+
}
|
|
7335
|
+
const defaultId = model.fields.find((f) => f.name === "id" && !f.isRelation);
|
|
7336
|
+
if (defaultId) {
|
|
7337
|
+
const out = ["id"];
|
|
7338
|
+
PK_FIELDS_CACHE.set(model, out);
|
|
7339
|
+
return out;
|
|
7340
|
+
}
|
|
7341
|
+
throw new Error(
|
|
7342
|
+
`Model ${model.name} has no primary key field. Models must have either fields with isId=true or a field named 'id'.`
|
|
7343
|
+
);
|
|
7344
|
+
}
|
|
7345
|
+
function scalarFieldNamesCached(model) {
|
|
7346
|
+
const cached = SCALAR_FIELDS_CACHE.get(model);
|
|
7347
|
+
if (cached) return cached;
|
|
7348
|
+
const out = model.fields.filter((f) => !f.isRelation).map((f) => f.name);
|
|
7349
|
+
SCALAR_FIELDS_CACHE.set(model, out);
|
|
7350
|
+
return out;
|
|
7351
|
+
}
|
|
7352
|
+
function jsonFieldSetCached(model) {
|
|
7353
|
+
var _a;
|
|
7354
|
+
const cached = JSON_FIELD_SET_CACHE.get(model);
|
|
7355
|
+
if (cached) return cached;
|
|
7356
|
+
const s = /* @__PURE__ */ new Set();
|
|
7357
|
+
for (const f of model.fields) {
|
|
7358
|
+
if (f.isRelation) continue;
|
|
7359
|
+
const t = String((_a = f.type) != null ? _a : "").toLowerCase();
|
|
7360
|
+
if (t === "json") s.add(f.name);
|
|
7361
|
+
}
|
|
7362
|
+
JSON_FIELD_SET_CACHE.set(model, s);
|
|
7363
|
+
return s;
|
|
7364
|
+
}
|
|
7365
|
+
function maybeParseJsonScalarFast(isJson, value) {
|
|
7366
|
+
if (!isJson) return value;
|
|
7367
|
+
if (value == null) return value;
|
|
7368
|
+
if (typeof value !== "string") return value;
|
|
7369
|
+
try {
|
|
7370
|
+
return JSON.parse(value);
|
|
7371
|
+
} catch (e) {
|
|
7372
|
+
return value;
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
function extractIncludeSpecFromRelArgs(relArgs, relModel) {
|
|
7376
|
+
const relationSet = getRelationFieldSet(relModel);
|
|
7377
|
+
const out = {};
|
|
7378
|
+
if (!isPlainObject(relArgs)) return out;
|
|
7379
|
+
const obj = relArgs;
|
|
7380
|
+
if (isPlainObject(obj.include)) {
|
|
7381
|
+
for (const [k, v] of Object.entries(
|
|
7382
|
+
obj.include
|
|
7383
|
+
)) {
|
|
7384
|
+
if (!relationSet.has(k)) continue;
|
|
7385
|
+
if (v === false) continue;
|
|
7386
|
+
out[k] = v;
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
if (isPlainObject(obj.select)) {
|
|
7390
|
+
for (const [k, v] of Object.entries(
|
|
7391
|
+
obj.select
|
|
7392
|
+
)) {
|
|
7393
|
+
if (!relationSet.has(k)) continue;
|
|
7394
|
+
if (v === false) continue;
|
|
7395
|
+
if (v === true) {
|
|
7396
|
+
out[k] = true;
|
|
7397
|
+
continue;
|
|
7398
|
+
}
|
|
7399
|
+
if (isPlainObject(v)) {
|
|
7400
|
+
const vv = v;
|
|
7401
|
+
if (isPlainObject(vv.include) || isPlainObject(vv.select)) {
|
|
7402
|
+
out[k] = v;
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
return out;
|
|
7408
|
+
}
|
|
7409
|
+
function extractScalarSelection(relArgs, relModel) {
|
|
7410
|
+
const scalars = scalarFieldNamesCached(relModel);
|
|
7411
|
+
const scalarSet = new Set(scalars);
|
|
7412
|
+
if (relArgs === true || !isPlainObject(relArgs)) {
|
|
7413
|
+
return { includeAllScalars: true, selectedScalarFields: scalars };
|
|
7414
|
+
}
|
|
7415
|
+
const obj = relArgs;
|
|
7416
|
+
if (!isPlainObject(obj.select)) {
|
|
7417
|
+
return { includeAllScalars: true, selectedScalarFields: scalars };
|
|
7418
|
+
}
|
|
7419
|
+
const sel = obj.select;
|
|
7420
|
+
const selected = [];
|
|
7421
|
+
for (const [k, v] of Object.entries(sel)) {
|
|
7422
|
+
if (!scalarSet.has(k)) continue;
|
|
7423
|
+
if (v === true) selected.push(k);
|
|
7424
|
+
}
|
|
7425
|
+
return { includeAllScalars: false, selectedScalarFields: selected };
|
|
7426
|
+
}
|
|
7427
|
+
function buildRelationScalarCols(relModel, relPath, includeAllScalars, selectedScalarFields) {
|
|
7428
|
+
const jsonSet = jsonFieldSetCached(relModel);
|
|
7429
|
+
const scalarFields = includeAllScalars ? scalarFieldNamesCached(relModel) : selectedScalarFields;
|
|
7430
|
+
const out = [];
|
|
7431
|
+
for (const fieldName of scalarFields) {
|
|
7432
|
+
out.push({
|
|
7433
|
+
fieldName,
|
|
7434
|
+
colName: `${relPath}.${fieldName}`,
|
|
7435
|
+
isJson: jsonSet.has(fieldName)
|
|
7436
|
+
});
|
|
7437
|
+
}
|
|
7438
|
+
return out;
|
|
7439
|
+
}
|
|
7440
|
+
function buildReducerConfig(parentModel, includeSpec, allModels, prefix = "", depth = 0) {
|
|
7441
|
+
if (depth > 10) {
|
|
7442
|
+
throw new Error(
|
|
7443
|
+
`Reducer config exceeded maximum depth of 10 at path '${prefix}'`
|
|
7444
|
+
);
|
|
7445
|
+
}
|
|
7446
|
+
const includedRelations = [];
|
|
7447
|
+
const modelMap = new Map(allModels.map((m) => [m.name, m]));
|
|
7448
|
+
for (const [incName, incValue] of Object.entries(includeSpec)) {
|
|
7449
|
+
if (incValue === false) continue;
|
|
7450
|
+
const field = parentModel.fields.find((f) => f.name === incName);
|
|
7451
|
+
if (!field || !field.isRelation) {
|
|
7452
|
+
throw new Error(
|
|
7453
|
+
`Field '${incName}' is not a relation on model ${parentModel.name}`
|
|
7454
|
+
);
|
|
7455
|
+
}
|
|
7456
|
+
const relatedModel = modelMap.get(field.relatedModel);
|
|
7457
|
+
if (!relatedModel) {
|
|
7458
|
+
throw new Error(
|
|
7459
|
+
`Related model '${field.relatedModel}' not found for relation '${incName}'`
|
|
7460
|
+
);
|
|
7461
|
+
}
|
|
7462
|
+
const isList = typeof field.type === "string" && field.type.endsWith("[]");
|
|
7463
|
+
const primaryKeyFields = findPrimaryKeyFieldsCached(relatedModel);
|
|
7464
|
+
const scalarSel = extractScalarSelection(incValue, relatedModel);
|
|
7465
|
+
const relPath = prefix ? `${prefix}.${incName}` : incName;
|
|
7466
|
+
let nestedIncludes = null;
|
|
7467
|
+
const nestedSpec = extractIncludeSpecFromRelArgs(incValue, relatedModel);
|
|
7468
|
+
if (Object.keys(nestedSpec).length > 0) {
|
|
7469
|
+
nestedIncludes = buildReducerConfig(
|
|
7470
|
+
relatedModel,
|
|
7471
|
+
nestedSpec,
|
|
7472
|
+
allModels,
|
|
7473
|
+
relPath,
|
|
7474
|
+
depth + 1
|
|
7475
|
+
);
|
|
7476
|
+
}
|
|
7477
|
+
const keyCols = primaryKeyFields.map((f) => `${relPath}.${f}`);
|
|
7478
|
+
const scalarCols = buildRelationScalarCols(
|
|
7479
|
+
relatedModel,
|
|
7480
|
+
relPath,
|
|
7481
|
+
scalarSel.includeAllScalars,
|
|
7482
|
+
scalarSel.selectedScalarFields
|
|
7483
|
+
);
|
|
7484
|
+
includedRelations.push({
|
|
7485
|
+
name: incName,
|
|
7486
|
+
cardinality: isList ? "many" : "one",
|
|
7487
|
+
relatedModel,
|
|
7488
|
+
primaryKeyFields,
|
|
7489
|
+
includeAllScalars: scalarSel.includeAllScalars,
|
|
7490
|
+
selectedScalarFields: scalarSel.selectedScalarFields,
|
|
7491
|
+
nestedIncludes,
|
|
7492
|
+
path: relPath,
|
|
7493
|
+
keyCols,
|
|
7494
|
+
scalarCols
|
|
7495
|
+
});
|
|
7496
|
+
}
|
|
7497
|
+
return {
|
|
7498
|
+
parentModel,
|
|
7499
|
+
includedRelations,
|
|
7500
|
+
allModels
|
|
7501
|
+
};
|
|
7502
|
+
}
|
|
7503
|
+
function typedKeyPart(v) {
|
|
7504
|
+
const t = typeof v;
|
|
7505
|
+
if (t === "string") return `s:${v}`;
|
|
7506
|
+
if (t === "number") return `n:${v}`;
|
|
7507
|
+
if (t === "boolean") return `b:${v ? 1 : 0}`;
|
|
7508
|
+
return `o:${String(v)}`;
|
|
7509
|
+
}
|
|
7510
|
+
function keyFromRowByCols(row, cols) {
|
|
7511
|
+
if (cols.length === 0) return null;
|
|
7512
|
+
if (cols.length === 1) {
|
|
7513
|
+
const v = row[cols[0]];
|
|
7514
|
+
if (v == null) return null;
|
|
7515
|
+
return typedKeyPart(v);
|
|
7516
|
+
}
|
|
7517
|
+
let out = "";
|
|
7518
|
+
for (let i = 0; i < cols.length; i++) {
|
|
7519
|
+
const v = row[cols[i]];
|
|
7520
|
+
if (v == null) return null;
|
|
7521
|
+
if (i > 0) out += "";
|
|
7522
|
+
out += typedKeyPart(v);
|
|
7523
|
+
}
|
|
7524
|
+
return out;
|
|
7525
|
+
}
|
|
7526
|
+
function getIndexForParent(store, parentObj, path) {
|
|
7527
|
+
let byPath = store.get(parentObj);
|
|
7528
|
+
if (!byPath) {
|
|
7529
|
+
byPath = /* @__PURE__ */ new Map();
|
|
7530
|
+
store.set(parentObj, byPath);
|
|
7531
|
+
}
|
|
7532
|
+
let idx = byPath.get(path);
|
|
7533
|
+
if (!idx) {
|
|
7534
|
+
idx = /* @__PURE__ */ new Map();
|
|
7535
|
+
byPath.set(path, idx);
|
|
7536
|
+
}
|
|
7537
|
+
return idx;
|
|
7538
|
+
}
|
|
7539
|
+
function initNestedPlaceholders(obj, nested) {
|
|
7540
|
+
if (!nested) return;
|
|
7541
|
+
for (const r of nested.includedRelations) {
|
|
7542
|
+
obj[r.name] = r.cardinality === "many" ? [] : null;
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
function materializeRelationObject(row, rel) {
|
|
7546
|
+
const relKey = keyFromRowByCols(row, rel.keyCols);
|
|
7547
|
+
if (relKey == null) return null;
|
|
7548
|
+
const obj = {};
|
|
7549
|
+
for (const c of rel.scalarCols) {
|
|
7550
|
+
obj[c.fieldName] = maybeParseJsonScalarFast(c.isJson, row[c.colName]);
|
|
7551
|
+
}
|
|
7552
|
+
initNestedPlaceholders(obj, rel.nestedIncludes);
|
|
7553
|
+
return obj;
|
|
7554
|
+
}
|
|
7555
|
+
function processRelation(parentObj, rel, row, manyStore) {
|
|
7556
|
+
const relKey = keyFromRowByCols(row, rel.keyCols);
|
|
7557
|
+
if (relKey == null) return;
|
|
7558
|
+
if (rel.cardinality === "one") {
|
|
7559
|
+
let current = parentObj[rel.name];
|
|
7560
|
+
if (current == null) {
|
|
7561
|
+
const created2 = materializeRelationObject(row, rel);
|
|
7562
|
+
if (!created2) return;
|
|
7563
|
+
parentObj[rel.name] = created2;
|
|
7564
|
+
current = created2;
|
|
7565
|
+
}
|
|
7566
|
+
if (rel.nestedIncludes) {
|
|
7567
|
+
for (const nestedRel of rel.nestedIncludes.includedRelations) {
|
|
7568
|
+
processRelation(current, nestedRel, row, manyStore);
|
|
7569
|
+
}
|
|
7570
|
+
}
|
|
7571
|
+
return;
|
|
7572
|
+
}
|
|
7573
|
+
const arr = parentObj[rel.name];
|
|
7574
|
+
const idx = getIndexForParent(manyStore, parentObj, rel.path);
|
|
7575
|
+
const existing = idx.get(relKey);
|
|
7576
|
+
if (existing) {
|
|
7577
|
+
if (rel.nestedIncludes) {
|
|
7578
|
+
for (const nestedRel of rel.nestedIncludes.includedRelations) {
|
|
7579
|
+
processRelation(existing, nestedRel, row, manyStore);
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7582
|
+
return;
|
|
7583
|
+
}
|
|
7584
|
+
const created = materializeRelationObject(row, rel);
|
|
7585
|
+
if (!created) return;
|
|
7586
|
+
arr.push(created);
|
|
7587
|
+
idx.set(relKey, created);
|
|
7588
|
+
if (rel.nestedIncludes) {
|
|
7589
|
+
for (const nestedRel of rel.nestedIncludes.includedRelations) {
|
|
7590
|
+
processRelation(created, nestedRel, row, manyStore);
|
|
7591
|
+
}
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7594
|
+
function pickParentScalarFieldsFromRows(parentModel, rows) {
|
|
7595
|
+
const all = scalarFieldNamesCached(parentModel);
|
|
7596
|
+
if (rows.length === 0) return all;
|
|
7597
|
+
const row0 = rows[0];
|
|
7598
|
+
const picked = [];
|
|
7599
|
+
for (const f of all) {
|
|
7600
|
+
if (Object.prototype.hasOwnProperty.call(row0, f)) picked.push(f);
|
|
7601
|
+
}
|
|
7602
|
+
return picked.length > 0 ? picked : all;
|
|
7603
|
+
}
|
|
7604
|
+
function reduceFlatRows(rows, config) {
|
|
7605
|
+
if (rows.length === 0) return [];
|
|
7606
|
+
const { parentModel, includedRelations } = config;
|
|
7607
|
+
const parentPkFields = findPrimaryKeyFieldsCached(parentModel);
|
|
7608
|
+
const parentKeyCols = parentPkFields;
|
|
7609
|
+
const parentScalarFields = pickParentScalarFieldsFromRows(parentModel, rows);
|
|
7610
|
+
const parentJsonSet = jsonFieldSetCached(parentModel);
|
|
7611
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
7612
|
+
const manyStore = /* @__PURE__ */ new WeakMap();
|
|
7613
|
+
for (const row of rows) {
|
|
7614
|
+
const parentKey = keyFromRowByCols(row, parentKeyCols);
|
|
7615
|
+
if (parentKey == null) continue;
|
|
7616
|
+
let record = resultMap.get(parentKey);
|
|
7617
|
+
if (!record) {
|
|
7618
|
+
record = {};
|
|
7619
|
+
for (const fieldName of parentScalarFields) {
|
|
7620
|
+
record[fieldName] = maybeParseJsonScalarFast(
|
|
7621
|
+
parentJsonSet.has(fieldName),
|
|
7622
|
+
row[fieldName]
|
|
7623
|
+
);
|
|
7624
|
+
}
|
|
7625
|
+
for (const rel of includedRelations) {
|
|
7626
|
+
record[rel.name] = rel.cardinality === "many" ? [] : null;
|
|
7627
|
+
}
|
|
7628
|
+
resultMap.set(parentKey, record);
|
|
7629
|
+
}
|
|
7630
|
+
for (const rel of includedRelations) {
|
|
7631
|
+
processRelation(record, rel, row, manyStore);
|
|
7632
|
+
}
|
|
7633
|
+
}
|
|
7634
|
+
return Array.from(resultMap.values());
|
|
7635
|
+
}
|
|
7636
|
+
|
|
6523
7637
|
// src/index.ts
|
|
6524
7638
|
function buildSQL(model, models, method, args, dialect) {
|
|
6525
7639
|
return buildSQLWithCache(model, models, method, args, dialect);
|
|
@@ -6561,8 +7675,20 @@ function createPrismaSQL(config) {
|
|
|
6561
7675
|
const modelMap = new Map(models.map((m) => [m.name, m]));
|
|
6562
7676
|
function query(_0, _1) {
|
|
6563
7677
|
return __async(this, arguments, function* (model, method, args = {}) {
|
|
6564
|
-
const
|
|
6565
|
-
|
|
7678
|
+
const sqlResult = toSQL(model, method, args);
|
|
7679
|
+
let results = yield execute(client, sqlResult.sql, [...sqlResult.params]);
|
|
7680
|
+
if (sqlResult.requiresReduction && sqlResult.includeSpec) {
|
|
7681
|
+
const modelDef = modelMap.get(model);
|
|
7682
|
+
if (modelDef) {
|
|
7683
|
+
const config2 = buildReducerConfig(
|
|
7684
|
+
modelDef,
|
|
7685
|
+
sqlResult.includeSpec,
|
|
7686
|
+
models
|
|
7687
|
+
);
|
|
7688
|
+
results = reduceFlatRows(results, config2);
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7691
|
+
return transformQueryResults(method, results);
|
|
6566
7692
|
});
|
|
6567
7693
|
}
|
|
6568
7694
|
function batchSql(queries) {
|
|
@@ -6613,6 +7739,7 @@ function generateSQLByModel(directives) {
|
|
|
6613
7739
|
|
|
6614
7740
|
exports.buildBatchCountSql = buildBatchCountSql;
|
|
6615
7741
|
exports.buildBatchSql = buildBatchSql;
|
|
7742
|
+
exports.buildReducerConfig = buildReducerConfig;
|
|
6616
7743
|
exports.buildSQL = buildSQL;
|
|
6617
7744
|
exports.createPrismaSQL = createPrismaSQL;
|
|
6618
7745
|
exports.createToSQL = createToSQL;
|
|
@@ -6623,6 +7750,7 @@ exports.generateSQLByModel = generateSQLByModel;
|
|
|
6623
7750
|
exports.normalizeValue = normalizeValue;
|
|
6624
7751
|
exports.parseBatchCountResults = parseBatchCountResults;
|
|
6625
7752
|
exports.parseBatchResults = parseBatchResults;
|
|
7753
|
+
exports.reduceFlatRows = reduceFlatRows;
|
|
6626
7754
|
exports.transformQueryResults = transformQueryResults;
|
|
6627
7755
|
//# sourceMappingURL=index.cjs.map
|
|
6628
7756
|
//# sourceMappingURL=index.cjs.map
|