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