prisma-sql 1.58.0 → 1.60.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 +1323 -876
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1323 -876
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1612 -1599
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +28 -133
- package/dist/index.d.ts +28 -133
- package/dist/index.js +1684 -1658
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +0 -1
package/dist/generator.cjs
CHANGED
|
@@ -14,6 +14,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
14
14
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
15
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
16
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
17
|
+
var __typeError = (msg) => {
|
|
18
|
+
throw TypeError(msg);
|
|
19
|
+
};
|
|
17
20
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
18
21
|
var __spreadValues = (a, b) => {
|
|
19
22
|
for (var prop in b || (b = {}))
|
|
@@ -30,6 +33,18 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
|
30
33
|
var __commonJS = (cb, mod) => function __require() {
|
|
31
34
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
32
35
|
};
|
|
36
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
37
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
38
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
39
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
40
|
+
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
41
|
+
set _(value) {
|
|
42
|
+
__privateSet(obj, member, value);
|
|
43
|
+
},
|
|
44
|
+
get _() {
|
|
45
|
+
return __privateGet(obj, member, getter);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
33
48
|
var __async = (__this, __arguments, generator) => {
|
|
34
49
|
return new Promise((resolve3, reject) => {
|
|
35
50
|
var fulfilled = (value) => {
|
|
@@ -56,7 +71,7 @@ var require_package = __commonJS({
|
|
|
56
71
|
"package.json"(exports$1, module) {
|
|
57
72
|
module.exports = {
|
|
58
73
|
name: "prisma-sql",
|
|
59
|
-
version: "1.
|
|
74
|
+
version: "1.60.0",
|
|
60
75
|
description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
|
|
61
76
|
main: "dist/index.cjs",
|
|
62
77
|
module: "dist/index.js",
|
|
@@ -94,7 +109,7 @@ var require_package = __commonJS({
|
|
|
94
109
|
"prisma:migrate": "npx prisma migrate dev --schema=tests/prisma/schema.prisma",
|
|
95
110
|
"prisma:reset": "npx prisma db push --force-reset --skip-generate --schema=tests/prisma/schema.prisma",
|
|
96
111
|
prepublishOnly: "npm run build",
|
|
97
|
-
"sonar-cli": "sonar-scanner -Dsonar.projectKey=
|
|
112
|
+
"sonar-cli": "sonar-scanner -Dsonar.projectKey=b -Dsonar.sources=./src -Dsonar.host.url=http://localhost:9000 -Dsonar.login=sqp_0d5fbc16a275fceb6458d193a8aa8a975956edc1",
|
|
98
113
|
sonar: "npm run sonar-cli && npx tsx scripts/sonar.ts"
|
|
99
114
|
},
|
|
100
115
|
keywords: [
|
|
@@ -152,154 +167,6 @@ var require_package = __commonJS({
|
|
|
152
167
|
}
|
|
153
168
|
});
|
|
154
169
|
|
|
155
|
-
// src/builder/shared/constants.ts
|
|
156
|
-
var IS_PRODUCTION = process.env.NODE_ENV === "production";
|
|
157
|
-
var SQL_SEPARATORS = Object.freeze({
|
|
158
|
-
FIELD_LIST: ", ",
|
|
159
|
-
CONDITION_AND: " AND ",
|
|
160
|
-
CONDITION_OR: " OR ",
|
|
161
|
-
ORDER_BY: ", "
|
|
162
|
-
});
|
|
163
|
-
var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
164
|
-
"select",
|
|
165
|
-
"from",
|
|
166
|
-
"where",
|
|
167
|
-
"having",
|
|
168
|
-
"order",
|
|
169
|
-
"group",
|
|
170
|
-
"limit",
|
|
171
|
-
"offset",
|
|
172
|
-
"join",
|
|
173
|
-
"inner",
|
|
174
|
-
"left",
|
|
175
|
-
"right",
|
|
176
|
-
"outer",
|
|
177
|
-
"cross",
|
|
178
|
-
"full",
|
|
179
|
-
"and",
|
|
180
|
-
"or",
|
|
181
|
-
"not",
|
|
182
|
-
"by",
|
|
183
|
-
"as",
|
|
184
|
-
"on",
|
|
185
|
-
"union",
|
|
186
|
-
"intersect",
|
|
187
|
-
"except",
|
|
188
|
-
"case",
|
|
189
|
-
"when",
|
|
190
|
-
"then",
|
|
191
|
-
"else",
|
|
192
|
-
"end"
|
|
193
|
-
]);
|
|
194
|
-
var SQL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
195
|
-
...ALIAS_FORBIDDEN_KEYWORDS,
|
|
196
|
-
"user",
|
|
197
|
-
"users",
|
|
198
|
-
"table",
|
|
199
|
-
"column",
|
|
200
|
-
"index",
|
|
201
|
-
"values",
|
|
202
|
-
"in",
|
|
203
|
-
"like",
|
|
204
|
-
"between",
|
|
205
|
-
"is",
|
|
206
|
-
"exists",
|
|
207
|
-
"null",
|
|
208
|
-
"true",
|
|
209
|
-
"false",
|
|
210
|
-
"all",
|
|
211
|
-
"any",
|
|
212
|
-
"some",
|
|
213
|
-
"update",
|
|
214
|
-
"insert",
|
|
215
|
-
"delete",
|
|
216
|
-
"create",
|
|
217
|
-
"drop",
|
|
218
|
-
"alter",
|
|
219
|
-
"truncate",
|
|
220
|
-
"grant",
|
|
221
|
-
"revoke",
|
|
222
|
-
"exec",
|
|
223
|
-
"execute"
|
|
224
|
-
]);
|
|
225
|
-
var SQL_RESERVED_WORDS = SQL_KEYWORDS;
|
|
226
|
-
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
227
|
-
var SPECIAL_FIELDS = Object.freeze({
|
|
228
|
-
ID: "id"
|
|
229
|
-
});
|
|
230
|
-
var SQL_TEMPLATES = Object.freeze({
|
|
231
|
-
PUBLIC_SCHEMA: "public",
|
|
232
|
-
WHERE: "WHERE",
|
|
233
|
-
SELECT: "SELECT",
|
|
234
|
-
FROM: "FROM",
|
|
235
|
-
ORDER_BY: "ORDER BY",
|
|
236
|
-
GROUP_BY: "GROUP BY",
|
|
237
|
-
HAVING: "HAVING",
|
|
238
|
-
LIMIT: "LIMIT",
|
|
239
|
-
OFFSET: "OFFSET",
|
|
240
|
-
COUNT_ALL: "COUNT(*)",
|
|
241
|
-
AS: "AS",
|
|
242
|
-
DISTINCT_ON: "DISTINCT ON",
|
|
243
|
-
IS_NULL: "IS NULL",
|
|
244
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
245
|
-
LIKE: "LIKE",
|
|
246
|
-
AND: "AND",
|
|
247
|
-
OR: "OR",
|
|
248
|
-
NOT: "NOT"
|
|
249
|
-
});
|
|
250
|
-
var SCHEMA_PREFIXES = Object.freeze({
|
|
251
|
-
INTERNAL: "@",
|
|
252
|
-
COMMENT: "//"
|
|
253
|
-
});
|
|
254
|
-
var Ops = Object.freeze({
|
|
255
|
-
EQUALS: "equals",
|
|
256
|
-
NOT: "not",
|
|
257
|
-
GT: "gt",
|
|
258
|
-
GTE: "gte",
|
|
259
|
-
LT: "lt",
|
|
260
|
-
LTE: "lte",
|
|
261
|
-
IN: "in",
|
|
262
|
-
NOT_IN: "notIn",
|
|
263
|
-
CONTAINS: "contains",
|
|
264
|
-
STARTS_WITH: "startsWith",
|
|
265
|
-
ENDS_WITH: "endsWith",
|
|
266
|
-
HAS: "has",
|
|
267
|
-
HAS_SOME: "hasSome",
|
|
268
|
-
HAS_EVERY: "hasEvery",
|
|
269
|
-
IS_EMPTY: "isEmpty",
|
|
270
|
-
PATH: "path",
|
|
271
|
-
STRING_CONTAINS: "string_contains",
|
|
272
|
-
STRING_STARTS_WITH: "string_starts_with",
|
|
273
|
-
STRING_ENDS_WITH: "string_ends_with"
|
|
274
|
-
});
|
|
275
|
-
var LogicalOps = Object.freeze({
|
|
276
|
-
AND: "AND",
|
|
277
|
-
OR: "OR",
|
|
278
|
-
NOT: "NOT"
|
|
279
|
-
});
|
|
280
|
-
var RelationFilters = Object.freeze({
|
|
281
|
-
SOME: "some",
|
|
282
|
-
EVERY: "every",
|
|
283
|
-
NONE: "none"
|
|
284
|
-
});
|
|
285
|
-
var Modes = Object.freeze({
|
|
286
|
-
INSENSITIVE: "insensitive",
|
|
287
|
-
DEFAULT: "default"
|
|
288
|
-
});
|
|
289
|
-
var Wildcards = Object.freeze({
|
|
290
|
-
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
291
|
-
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
292
|
-
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
293
|
-
});
|
|
294
|
-
var REGEX_CACHE = {
|
|
295
|
-
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
296
|
-
};
|
|
297
|
-
var LIMITS = Object.freeze({
|
|
298
|
-
MAX_QUERY_DEPTH: 50,
|
|
299
|
-
MAX_ARRAY_SIZE: 1e4,
|
|
300
|
-
MAX_STRING_LENGTH: 1e4
|
|
301
|
-
});
|
|
302
|
-
|
|
303
170
|
// src/utils/normalize-value.ts
|
|
304
171
|
var MAX_DEPTH = 20;
|
|
305
172
|
function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
@@ -307,45 +174,54 @@ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0)
|
|
|
307
174
|
throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
|
|
308
175
|
}
|
|
309
176
|
if (value instanceof Date) {
|
|
310
|
-
|
|
311
|
-
if (!Number.isFinite(t)) {
|
|
312
|
-
throw new Error("Invalid Date value in SQL params");
|
|
313
|
-
}
|
|
314
|
-
return value.toISOString();
|
|
177
|
+
return normalizeDateValue(value);
|
|
315
178
|
}
|
|
316
179
|
if (typeof value === "bigint") {
|
|
317
180
|
return value.toString();
|
|
318
181
|
}
|
|
319
182
|
if (Array.isArray(value)) {
|
|
320
|
-
|
|
321
|
-
if (seen.has(arrRef)) {
|
|
322
|
-
throw new Error("Circular reference in SQL params");
|
|
323
|
-
}
|
|
324
|
-
seen.add(arrRef);
|
|
325
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
326
|
-
seen.delete(arrRef);
|
|
327
|
-
return out;
|
|
183
|
+
return normalizeArrayValue(value, seen, depth);
|
|
328
184
|
}
|
|
329
185
|
if (value && typeof value === "object") {
|
|
330
|
-
|
|
331
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
332
|
-
const proto = Object.getPrototypeOf(value);
|
|
333
|
-
const isPlain = proto === Object.prototype || proto === null;
|
|
334
|
-
if (!isPlain) return value;
|
|
335
|
-
const obj = value;
|
|
336
|
-
if (seen.has(obj)) {
|
|
337
|
-
throw new Error("Circular reference in SQL params");
|
|
338
|
-
}
|
|
339
|
-
seen.add(obj);
|
|
340
|
-
const out = {};
|
|
341
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
342
|
-
out[k] = normalizeValue(v, seen, depth + 1);
|
|
343
|
-
}
|
|
344
|
-
seen.delete(obj);
|
|
345
|
-
return out;
|
|
186
|
+
return normalizeObjectValue(value, seen, depth);
|
|
346
187
|
}
|
|
347
188
|
return value;
|
|
348
189
|
}
|
|
190
|
+
function normalizeDateValue(date) {
|
|
191
|
+
const t = date.getTime();
|
|
192
|
+
if (!Number.isFinite(t)) {
|
|
193
|
+
throw new Error("Invalid Date value in SQL params");
|
|
194
|
+
}
|
|
195
|
+
return date.toISOString();
|
|
196
|
+
}
|
|
197
|
+
function normalizeArrayValue(value, seen, depth) {
|
|
198
|
+
const arrRef = value;
|
|
199
|
+
if (seen.has(arrRef)) {
|
|
200
|
+
throw new Error("Circular reference in SQL params");
|
|
201
|
+
}
|
|
202
|
+
seen.add(arrRef);
|
|
203
|
+
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
204
|
+
seen.delete(arrRef);
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
function normalizeObjectValue(value, seen, depth) {
|
|
208
|
+
if (value instanceof Uint8Array) return value;
|
|
209
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
210
|
+
const proto = Object.getPrototypeOf(value);
|
|
211
|
+
const isPlain = proto === Object.prototype || proto === null;
|
|
212
|
+
if (!isPlain) return value;
|
|
213
|
+
const obj = value;
|
|
214
|
+
if (seen.has(obj)) {
|
|
215
|
+
throw new Error("Circular reference in SQL params");
|
|
216
|
+
}
|
|
217
|
+
seen.add(obj);
|
|
218
|
+
const out = {};
|
|
219
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
220
|
+
out[k] = normalizeValue(v, seen, depth + 1);
|
|
221
|
+
}
|
|
222
|
+
seen.delete(obj);
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
349
225
|
|
|
350
226
|
// src/sql-builder-dialect.ts
|
|
351
227
|
var globalDialect = "postgres";
|
|
@@ -525,9 +401,158 @@ function prepareArrayParam(value, dialect) {
|
|
|
525
401
|
if (dialect === "postgres") {
|
|
526
402
|
return value.map((v) => normalizeValue(v));
|
|
527
403
|
}
|
|
528
|
-
return JSON.stringify(value);
|
|
404
|
+
return JSON.stringify(value.map((v) => normalizeValue(v)));
|
|
529
405
|
}
|
|
530
406
|
|
|
407
|
+
// src/builder/shared/constants.ts
|
|
408
|
+
var IS_PRODUCTION = process.env.NODE_ENV === "production";
|
|
409
|
+
var SQL_SEPARATORS = Object.freeze({
|
|
410
|
+
FIELD_LIST: ", ",
|
|
411
|
+
CONDITION_AND: " AND ",
|
|
412
|
+
CONDITION_OR: " OR ",
|
|
413
|
+
ORDER_BY: ", "
|
|
414
|
+
});
|
|
415
|
+
var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
416
|
+
"select",
|
|
417
|
+
"from",
|
|
418
|
+
"where",
|
|
419
|
+
"having",
|
|
420
|
+
"order",
|
|
421
|
+
"group",
|
|
422
|
+
"limit",
|
|
423
|
+
"offset",
|
|
424
|
+
"join",
|
|
425
|
+
"inner",
|
|
426
|
+
"left",
|
|
427
|
+
"right",
|
|
428
|
+
"outer",
|
|
429
|
+
"cross",
|
|
430
|
+
"full",
|
|
431
|
+
"and",
|
|
432
|
+
"or",
|
|
433
|
+
"not",
|
|
434
|
+
"by",
|
|
435
|
+
"as",
|
|
436
|
+
"on",
|
|
437
|
+
"union",
|
|
438
|
+
"intersect",
|
|
439
|
+
"except",
|
|
440
|
+
"case",
|
|
441
|
+
"when",
|
|
442
|
+
"then",
|
|
443
|
+
"else",
|
|
444
|
+
"end"
|
|
445
|
+
]);
|
|
446
|
+
var SQL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
447
|
+
...ALIAS_FORBIDDEN_KEYWORDS,
|
|
448
|
+
"user",
|
|
449
|
+
"users",
|
|
450
|
+
"table",
|
|
451
|
+
"column",
|
|
452
|
+
"index",
|
|
453
|
+
"values",
|
|
454
|
+
"in",
|
|
455
|
+
"like",
|
|
456
|
+
"between",
|
|
457
|
+
"is",
|
|
458
|
+
"exists",
|
|
459
|
+
"null",
|
|
460
|
+
"true",
|
|
461
|
+
"false",
|
|
462
|
+
"all",
|
|
463
|
+
"any",
|
|
464
|
+
"some",
|
|
465
|
+
"update",
|
|
466
|
+
"insert",
|
|
467
|
+
"delete",
|
|
468
|
+
"create",
|
|
469
|
+
"drop",
|
|
470
|
+
"alter",
|
|
471
|
+
"truncate",
|
|
472
|
+
"grant",
|
|
473
|
+
"revoke",
|
|
474
|
+
"exec",
|
|
475
|
+
"execute"
|
|
476
|
+
]);
|
|
477
|
+
var SQL_RESERVED_WORDS = SQL_KEYWORDS;
|
|
478
|
+
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
479
|
+
var SPECIAL_FIELDS = Object.freeze({
|
|
480
|
+
ID: "id"
|
|
481
|
+
});
|
|
482
|
+
var SQL_TEMPLATES = Object.freeze({
|
|
483
|
+
PUBLIC_SCHEMA: "public",
|
|
484
|
+
WHERE: "WHERE",
|
|
485
|
+
SELECT: "SELECT",
|
|
486
|
+
FROM: "FROM",
|
|
487
|
+
ORDER_BY: "ORDER BY",
|
|
488
|
+
GROUP_BY: "GROUP BY",
|
|
489
|
+
HAVING: "HAVING",
|
|
490
|
+
LIMIT: "LIMIT",
|
|
491
|
+
OFFSET: "OFFSET",
|
|
492
|
+
COUNT_ALL: "COUNT(*)",
|
|
493
|
+
AS: "AS",
|
|
494
|
+
DISTINCT_ON: "DISTINCT ON",
|
|
495
|
+
IS_NULL: "IS NULL",
|
|
496
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
497
|
+
LIKE: "LIKE",
|
|
498
|
+
AND: "AND",
|
|
499
|
+
OR: "OR",
|
|
500
|
+
NOT: "NOT"
|
|
501
|
+
});
|
|
502
|
+
var SCHEMA_PREFIXES = Object.freeze({
|
|
503
|
+
INTERNAL: "@",
|
|
504
|
+
COMMENT: "//"
|
|
505
|
+
});
|
|
506
|
+
var Ops = Object.freeze({
|
|
507
|
+
EQUALS: "equals",
|
|
508
|
+
NOT: "not",
|
|
509
|
+
GT: "gt",
|
|
510
|
+
GTE: "gte",
|
|
511
|
+
LT: "lt",
|
|
512
|
+
LTE: "lte",
|
|
513
|
+
IN: "in",
|
|
514
|
+
NOT_IN: "notIn",
|
|
515
|
+
CONTAINS: "contains",
|
|
516
|
+
STARTS_WITH: "startsWith",
|
|
517
|
+
ENDS_WITH: "endsWith",
|
|
518
|
+
HAS: "has",
|
|
519
|
+
HAS_SOME: "hasSome",
|
|
520
|
+
HAS_EVERY: "hasEvery",
|
|
521
|
+
IS_EMPTY: "isEmpty",
|
|
522
|
+
PATH: "path",
|
|
523
|
+
STRING_CONTAINS: "string_contains",
|
|
524
|
+
STRING_STARTS_WITH: "string_starts_with",
|
|
525
|
+
STRING_ENDS_WITH: "string_ends_with"
|
|
526
|
+
});
|
|
527
|
+
var LogicalOps = Object.freeze({
|
|
528
|
+
AND: "AND",
|
|
529
|
+
OR: "OR",
|
|
530
|
+
NOT: "NOT"
|
|
531
|
+
});
|
|
532
|
+
var RelationFilters = Object.freeze({
|
|
533
|
+
SOME: "some",
|
|
534
|
+
EVERY: "every",
|
|
535
|
+
NONE: "none"
|
|
536
|
+
});
|
|
537
|
+
var Modes = Object.freeze({
|
|
538
|
+
INSENSITIVE: "insensitive",
|
|
539
|
+
DEFAULT: "default"
|
|
540
|
+
});
|
|
541
|
+
var Wildcards = Object.freeze({
|
|
542
|
+
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
543
|
+
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
544
|
+
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
545
|
+
});
|
|
546
|
+
var REGEX_CACHE = {
|
|
547
|
+
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
548
|
+
};
|
|
549
|
+
var LIMITS = Object.freeze({
|
|
550
|
+
MAX_QUERY_DEPTH: 50,
|
|
551
|
+
MAX_ARRAY_SIZE: 1e4,
|
|
552
|
+
MAX_STRING_LENGTH: 1e4,
|
|
553
|
+
MAX_HAVING_DEPTH: 50
|
|
554
|
+
});
|
|
555
|
+
|
|
531
556
|
// src/builder/shared/validators/type-guards.ts
|
|
532
557
|
function isNotNullish(value) {
|
|
533
558
|
return value !== null && value !== void 0;
|
|
@@ -594,73 +619,6 @@ function createError(message, ctx, code = "VALIDATION_ERROR") {
|
|
|
594
619
|
return new SqlBuilderError(parts.join("\n"), code, ctx);
|
|
595
620
|
}
|
|
596
621
|
|
|
597
|
-
// src/builder/shared/model-field-cache.ts
|
|
598
|
-
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
599
|
-
function quote(id) {
|
|
600
|
-
const needsQuoting2 = !/^[a-z_][a-z0-9_]*$/.test(id) || /^(select|from|where|having|order|group|limit|offset|join|inner|left|right|outer|cross|full|and|or|not|by|as|on|union|intersect|except|case|when|then|else|end|user|users|table|column|index|values|in|like|between|is|exists|null|true|false|all|any|some|update|insert|delete|create|drop|alter|truncate|grant|revoke|exec|execute)$/i.test(
|
|
601
|
-
id
|
|
602
|
-
);
|
|
603
|
-
if (needsQuoting2) {
|
|
604
|
-
return `"${id.replace(/"/g, '""')}"`;
|
|
605
|
-
}
|
|
606
|
-
return id;
|
|
607
|
-
}
|
|
608
|
-
function ensureFullCache(model) {
|
|
609
|
-
let cache = MODEL_CACHE.get(model);
|
|
610
|
-
if (!cache) {
|
|
611
|
-
const fieldInfo = /* @__PURE__ */ new Map();
|
|
612
|
-
const scalarFields = /* @__PURE__ */ new Set();
|
|
613
|
-
const relationFields = /* @__PURE__ */ new Set();
|
|
614
|
-
const columnMap = /* @__PURE__ */ new Map();
|
|
615
|
-
const fieldByName = /* @__PURE__ */ new Map();
|
|
616
|
-
const quotedColumns = /* @__PURE__ */ new Map();
|
|
617
|
-
for (const f of model.fields) {
|
|
618
|
-
const info = {
|
|
619
|
-
name: f.name,
|
|
620
|
-
dbName: f.dbName || f.name,
|
|
621
|
-
type: f.type,
|
|
622
|
-
isRelation: !!f.isRelation,
|
|
623
|
-
isRequired: !!f.isRequired
|
|
624
|
-
};
|
|
625
|
-
fieldInfo.set(f.name, info);
|
|
626
|
-
fieldByName.set(f.name, f);
|
|
627
|
-
if (info.isRelation) {
|
|
628
|
-
relationFields.add(f.name);
|
|
629
|
-
} else {
|
|
630
|
-
scalarFields.add(f.name);
|
|
631
|
-
const dbName = info.dbName;
|
|
632
|
-
columnMap.set(f.name, dbName);
|
|
633
|
-
quotedColumns.set(f.name, quote(dbName));
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
cache = {
|
|
637
|
-
fieldInfo,
|
|
638
|
-
scalarFields,
|
|
639
|
-
relationFields,
|
|
640
|
-
columnMap,
|
|
641
|
-
fieldByName,
|
|
642
|
-
quotedColumns
|
|
643
|
-
};
|
|
644
|
-
MODEL_CACHE.set(model, cache);
|
|
645
|
-
}
|
|
646
|
-
return cache;
|
|
647
|
-
}
|
|
648
|
-
function getFieldInfo(model, fieldName) {
|
|
649
|
-
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
650
|
-
}
|
|
651
|
-
function getScalarFieldSet(model) {
|
|
652
|
-
return ensureFullCache(model).scalarFields;
|
|
653
|
-
}
|
|
654
|
-
function getRelationFieldSet(model) {
|
|
655
|
-
return ensureFullCache(model).relationFields;
|
|
656
|
-
}
|
|
657
|
-
function getColumnMap(model) {
|
|
658
|
-
return ensureFullCache(model).columnMap;
|
|
659
|
-
}
|
|
660
|
-
function getQuotedColumn(model, fieldName) {
|
|
661
|
-
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
622
|
// src/builder/shared/validators/sql-validators.ts
|
|
665
623
|
function isValidWhereClause(clause) {
|
|
666
624
|
return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
|
|
@@ -804,9 +762,90 @@ function needsQuoting(identifier) {
|
|
|
804
762
|
return false;
|
|
805
763
|
}
|
|
806
764
|
|
|
765
|
+
// src/builder/shared/model-field-cache.ts
|
|
766
|
+
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
767
|
+
function quoteIdent(id) {
|
|
768
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
769
|
+
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
770
|
+
}
|
|
771
|
+
for (let i = 0; i < id.length; i++) {
|
|
772
|
+
const code = id.charCodeAt(i);
|
|
773
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
774
|
+
throw new Error(
|
|
775
|
+
`quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (needsQuoting(id)) {
|
|
780
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
781
|
+
}
|
|
782
|
+
return id;
|
|
783
|
+
}
|
|
784
|
+
function ensureFullCache(model) {
|
|
785
|
+
let cache = MODEL_CACHE.get(model);
|
|
786
|
+
if (!cache) {
|
|
787
|
+
const fieldInfo = /* @__PURE__ */ new Map();
|
|
788
|
+
const scalarFields = /* @__PURE__ */ new Set();
|
|
789
|
+
const relationFields = /* @__PURE__ */ new Set();
|
|
790
|
+
const columnMap = /* @__PURE__ */ new Map();
|
|
791
|
+
const fieldByName = /* @__PURE__ */ new Map();
|
|
792
|
+
const quotedColumns = /* @__PURE__ */ new Map();
|
|
793
|
+
for (const f of model.fields) {
|
|
794
|
+
const info = {
|
|
795
|
+
name: f.name,
|
|
796
|
+
dbName: f.dbName || f.name,
|
|
797
|
+
type: f.type,
|
|
798
|
+
isRelation: !!f.isRelation,
|
|
799
|
+
isRequired: !!f.isRequired
|
|
800
|
+
};
|
|
801
|
+
fieldInfo.set(f.name, info);
|
|
802
|
+
fieldByName.set(f.name, f);
|
|
803
|
+
if (info.isRelation) {
|
|
804
|
+
relationFields.add(f.name);
|
|
805
|
+
} else {
|
|
806
|
+
scalarFields.add(f.name);
|
|
807
|
+
const dbName = info.dbName;
|
|
808
|
+
columnMap.set(f.name, dbName);
|
|
809
|
+
quotedColumns.set(f.name, quoteIdent(dbName));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
cache = {
|
|
813
|
+
fieldInfo,
|
|
814
|
+
scalarFields,
|
|
815
|
+
relationFields,
|
|
816
|
+
columnMap,
|
|
817
|
+
fieldByName,
|
|
818
|
+
quotedColumns
|
|
819
|
+
};
|
|
820
|
+
MODEL_CACHE.set(model, cache);
|
|
821
|
+
}
|
|
822
|
+
return cache;
|
|
823
|
+
}
|
|
824
|
+
function getFieldInfo(model, fieldName) {
|
|
825
|
+
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
826
|
+
}
|
|
827
|
+
function getScalarFieldSet(model) {
|
|
828
|
+
return ensureFullCache(model).scalarFields;
|
|
829
|
+
}
|
|
830
|
+
function getRelationFieldSet(model) {
|
|
831
|
+
return ensureFullCache(model).relationFields;
|
|
832
|
+
}
|
|
833
|
+
function getColumnMap(model) {
|
|
834
|
+
return ensureFullCache(model).columnMap;
|
|
835
|
+
}
|
|
836
|
+
function getQuotedColumn(model, fieldName) {
|
|
837
|
+
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
838
|
+
}
|
|
839
|
+
|
|
807
840
|
// src/builder/shared/sql-utils.ts
|
|
808
841
|
function containsControlChars(s) {
|
|
809
|
-
|
|
842
|
+
for (let i = 0; i < s.length; i++) {
|
|
843
|
+
const code = s.charCodeAt(i);
|
|
844
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return false;
|
|
810
849
|
}
|
|
811
850
|
function assertNoControlChars(label, s) {
|
|
812
851
|
if (containsControlChars(s)) {
|
|
@@ -881,20 +920,9 @@ function parseUnquotedPart(input, start) {
|
|
|
881
920
|
}
|
|
882
921
|
return i;
|
|
883
922
|
}
|
|
884
|
-
function
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (trimmed.length === 0) {
|
|
888
|
-
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
889
|
-
}
|
|
890
|
-
if (raw !== trimmed) {
|
|
891
|
-
throw new Error(
|
|
892
|
-
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
893
|
-
);
|
|
894
|
-
}
|
|
895
|
-
assertNoControlChars("tableName/tableRef", trimmed);
|
|
896
|
-
for (let i2 = 0; i2 < trimmed.length; i2++) {
|
|
897
|
-
const c = trimmed.charCodeAt(i2);
|
|
923
|
+
function validateQualifiedNameFormat(trimmed) {
|
|
924
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
925
|
+
const c = trimmed.charCodeAt(i);
|
|
898
926
|
if (c === 9 || c === 11 || c === 12 || c === 32) {
|
|
899
927
|
throw new Error(
|
|
900
928
|
`tableName/tableRef must not contain whitespace: ${JSON.stringify(trimmed)}`
|
|
@@ -911,6 +939,8 @@ function assertSafeQualifiedName(input) {
|
|
|
911
939
|
);
|
|
912
940
|
}
|
|
913
941
|
}
|
|
942
|
+
}
|
|
943
|
+
function parseQualifiedNameParts(trimmed) {
|
|
914
944
|
let i = 0;
|
|
915
945
|
const n = trimmed.length;
|
|
916
946
|
let parts = 0;
|
|
@@ -946,7 +976,22 @@ function assertSafeQualifiedName(input) {
|
|
|
946
976
|
}
|
|
947
977
|
}
|
|
948
978
|
}
|
|
949
|
-
function
|
|
979
|
+
function assertSafeQualifiedName(input) {
|
|
980
|
+
const raw = String(input);
|
|
981
|
+
const trimmed = raw.trim();
|
|
982
|
+
if (trimmed.length === 0) {
|
|
983
|
+
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
984
|
+
}
|
|
985
|
+
if (raw !== trimmed) {
|
|
986
|
+
throw new Error(
|
|
987
|
+
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
assertNoControlChars("tableName/tableRef", trimmed);
|
|
991
|
+
validateQualifiedNameFormat(trimmed);
|
|
992
|
+
parseQualifiedNameParts(trimmed);
|
|
993
|
+
}
|
|
994
|
+
function quote(id) {
|
|
950
995
|
if (isEmptyString(id)) {
|
|
951
996
|
throw new Error("quote: identifier is required and cannot be empty");
|
|
952
997
|
}
|
|
@@ -966,13 +1011,13 @@ function resolveColumnName(model, fieldName) {
|
|
|
966
1011
|
return columnMap.get(fieldName) || fieldName;
|
|
967
1012
|
}
|
|
968
1013
|
function quoteColumn(model, fieldName) {
|
|
969
|
-
if (!model) return
|
|
1014
|
+
if (!model) return quote(fieldName);
|
|
970
1015
|
const cached = getQuotedColumn(model, fieldName);
|
|
971
|
-
return cached ||
|
|
1016
|
+
return cached || quote(fieldName);
|
|
972
1017
|
}
|
|
973
1018
|
function col(alias, field, model) {
|
|
974
1019
|
const columnName = model ? getColumnMap(model).get(field) || field : field;
|
|
975
|
-
const quoted = model ? getQuotedColumn(model, field) ||
|
|
1020
|
+
const quoted = model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName);
|
|
976
1021
|
return alias + "." + quoted;
|
|
977
1022
|
}
|
|
978
1023
|
function colWithAlias(alias, field, model) {
|
|
@@ -983,9 +1028,9 @@ function colWithAlias(alias, field, model) {
|
|
|
983
1028
|
throw new Error("colWithAlias: field is required and cannot be empty");
|
|
984
1029
|
}
|
|
985
1030
|
const columnName = resolveColumnName(model, field);
|
|
986
|
-
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) ||
|
|
1031
|
+
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName));
|
|
987
1032
|
if (columnName !== field) {
|
|
988
|
-
return columnRef + " AS " +
|
|
1033
|
+
return columnRef + " AS " + quote(field);
|
|
989
1034
|
}
|
|
990
1035
|
return columnRef;
|
|
991
1036
|
}
|
|
@@ -1008,7 +1053,7 @@ function buildTableReference(schemaName, tableName, dialect) {
|
|
|
1008
1053
|
}
|
|
1009
1054
|
const d = dialect != null ? dialect : "postgres";
|
|
1010
1055
|
if (d === "sqlite") {
|
|
1011
|
-
return
|
|
1056
|
+
return quote(tableName);
|
|
1012
1057
|
}
|
|
1013
1058
|
if (isEmptyString(schemaName)) {
|
|
1014
1059
|
throw new Error(
|
|
@@ -1035,7 +1080,7 @@ function assertSafeAlias(alias) {
|
|
|
1035
1080
|
if (a !== alias) {
|
|
1036
1081
|
throw new Error("Invalid alias: leading/trailing whitespace");
|
|
1037
1082
|
}
|
|
1038
|
-
if (
|
|
1083
|
+
if (containsControlChars(a)) {
|
|
1039
1084
|
throw new Error(
|
|
1040
1085
|
"Invalid alias: contains unsafe characters (control characters)"
|
|
1041
1086
|
);
|
|
@@ -1056,7 +1101,7 @@ function assertSafeAlias(alias) {
|
|
|
1056
1101
|
"Invalid alias: must be a simple identifier without whitespace"
|
|
1057
1102
|
);
|
|
1058
1103
|
}
|
|
1059
|
-
if (!/^[A-Za-z_]
|
|
1104
|
+
if (!/^[A-Za-z_]\w*$/.test(a)) {
|
|
1060
1105
|
throw new Error(
|
|
1061
1106
|
`Invalid alias: must be a simple identifier (alphanumeric with underscores): "${alias}"`
|
|
1062
1107
|
);
|
|
@@ -1104,8 +1149,7 @@ function isValidRelationField(field) {
|
|
|
1104
1149
|
return false;
|
|
1105
1150
|
const fk = normalizeKeyList(field.foreignKey);
|
|
1106
1151
|
if (fk.length === 0) return false;
|
|
1107
|
-
const
|
|
1108
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1152
|
+
const refs = normalizeKeyList(field.references);
|
|
1109
1153
|
if (refs.length === 0) {
|
|
1110
1154
|
return fk.length === 1;
|
|
1111
1155
|
}
|
|
@@ -1113,8 +1157,7 @@ function isValidRelationField(field) {
|
|
|
1113
1157
|
return true;
|
|
1114
1158
|
}
|
|
1115
1159
|
function getReferenceFieldNames(field, foreignKeyCount) {
|
|
1116
|
-
const
|
|
1117
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1160
|
+
const refs = normalizeKeyList(field.references);
|
|
1118
1161
|
if (refs.length === 0) {
|
|
1119
1162
|
if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
|
|
1120
1163
|
return [];
|
|
@@ -1275,29 +1318,35 @@ function normalizeOrderByInput(orderBy, parseValue) {
|
|
|
1275
1318
|
}
|
|
1276
1319
|
|
|
1277
1320
|
// src/builder/shared/order-by-determinism.ts
|
|
1278
|
-
function
|
|
1279
|
-
if (!model) return
|
|
1280
|
-
|
|
1321
|
+
function findTiebreakerField(model) {
|
|
1322
|
+
if (!model) return null;
|
|
1323
|
+
const scalarSet = getScalarFieldSet(model);
|
|
1324
|
+
for (const f of model.fields) {
|
|
1325
|
+
if (f.isId && !f.isRelation && scalarSet.has(f.name)) return f.name;
|
|
1326
|
+
}
|
|
1327
|
+
if (scalarSet.has("id")) return "id";
|
|
1328
|
+
return null;
|
|
1281
1329
|
}
|
|
1282
|
-
function
|
|
1330
|
+
function hasTiebreaker(orderBy, parse, field) {
|
|
1283
1331
|
if (!isNotNullish(orderBy)) return false;
|
|
1284
1332
|
const normalized = normalizeOrderByInput(orderBy, parse);
|
|
1285
1333
|
return normalized.some(
|
|
1286
|
-
(obj) => Object.prototype.hasOwnProperty.call(obj,
|
|
1334
|
+
(obj) => Object.prototype.hasOwnProperty.call(obj, field)
|
|
1287
1335
|
);
|
|
1288
1336
|
}
|
|
1289
|
-
function
|
|
1290
|
-
if (Array.isArray(orderBy)) return [...orderBy, {
|
|
1291
|
-
return [orderBy, {
|
|
1337
|
+
function addTiebreaker(orderBy, field) {
|
|
1338
|
+
if (Array.isArray(orderBy)) return [...orderBy, { [field]: "asc" }];
|
|
1339
|
+
return [orderBy, { [field]: "asc" }];
|
|
1292
1340
|
}
|
|
1293
1341
|
function ensureDeterministicOrderByInput(args) {
|
|
1294
1342
|
const { orderBy, model, parseValue } = args;
|
|
1295
|
-
|
|
1343
|
+
const tiebreaker = findTiebreakerField(model);
|
|
1344
|
+
if (!tiebreaker) return orderBy;
|
|
1296
1345
|
if (!isNotNullish(orderBy)) {
|
|
1297
|
-
return {
|
|
1346
|
+
return { [tiebreaker]: "asc" };
|
|
1298
1347
|
}
|
|
1299
|
-
if (
|
|
1300
|
-
return
|
|
1348
|
+
if (hasTiebreaker(orderBy, parseValue, tiebreaker)) return orderBy;
|
|
1349
|
+
return addTiebreaker(orderBy, tiebreaker);
|
|
1301
1350
|
}
|
|
1302
1351
|
|
|
1303
1352
|
// src/builder/shared/validators/field-assertions.ts
|
|
@@ -1444,11 +1493,9 @@ function defaultNullsFor(dialect, direction) {
|
|
|
1444
1493
|
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1445
1494
|
if (cursorEntries.length === 0) return orderEntries;
|
|
1446
1495
|
const existing = /* @__PURE__ */ new Set();
|
|
1447
|
-
for (
|
|
1448
|
-
existing.add(orderEntries[i].field);
|
|
1496
|
+
for (const entry of orderEntries) existing.add(entry.field);
|
|
1449
1497
|
let out = null;
|
|
1450
|
-
for (
|
|
1451
|
-
const field = cursorEntries[i][0];
|
|
1498
|
+
for (const [field] of cursorEntries) {
|
|
1452
1499
|
if (!existing.has(field)) {
|
|
1453
1500
|
if (!out) out = orderEntries.slice();
|
|
1454
1501
|
out.push({ field, direction: "asc" });
|
|
@@ -1673,6 +1720,7 @@ function buildOrderEntries(orderBy) {
|
|
|
1673
1720
|
}
|
|
1674
1721
|
return entries;
|
|
1675
1722
|
}
|
|
1723
|
+
var MAX_NOT_DEPTH = 50;
|
|
1676
1724
|
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1677
1725
|
const entries = Object.entries(val).filter(
|
|
1678
1726
|
([k, v]) => k !== "mode" && v !== void 0
|
|
@@ -1687,45 +1735,71 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
|
1687
1735
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1688
1736
|
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1689
1737
|
}
|
|
1690
|
-
function
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1738
|
+
function validateOperatorRequirements(op, mode, dialect) {
|
|
1739
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1740
|
+
Ops.CONTAINS,
|
|
1741
|
+
Ops.STARTS_WITH,
|
|
1742
|
+
Ops.ENDS_WITH
|
|
1743
|
+
]);
|
|
1744
|
+
if (STRING_LIKE_OPS.has(op) && !isNotNullish(dialect)) {
|
|
1745
|
+
throw createError(`Like operators require a SQL dialect`, { operator: op });
|
|
1694
1746
|
}
|
|
1695
|
-
if (op === Ops.
|
|
1696
|
-
|
|
1747
|
+
if ((op === Ops.IN || op === Ops.NOT_IN) && !isNotNullish(dialect)) {
|
|
1748
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1697
1749
|
}
|
|
1698
|
-
if (op === Ops.
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1703
|
-
const placeholder = params.addAuto(val);
|
|
1704
|
-
return caseInsensitiveEquals(expr, placeholder);
|
|
1750
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1751
|
+
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1752
|
+
operator: op
|
|
1753
|
+
});
|
|
1705
1754
|
}
|
|
1755
|
+
}
|
|
1756
|
+
function routeOperatorHandler(expr, op, val, params, mode, dialect) {
|
|
1706
1757
|
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1707
1758
|
Ops.CONTAINS,
|
|
1708
1759
|
Ops.STARTS_WITH,
|
|
1709
1760
|
Ops.ENDS_WITH
|
|
1710
1761
|
]);
|
|
1711
1762
|
if (STRING_LIKE_OPS.has(op)) {
|
|
1712
|
-
if (!isNotNullish(dialect)) {
|
|
1713
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1714
|
-
operator: op
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
1763
|
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1718
1764
|
}
|
|
1719
1765
|
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1720
|
-
if (!isNotNullish(dialect)) {
|
|
1721
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1722
|
-
}
|
|
1723
1766
|
return handleInOperator(expr, op, val, params, dialect);
|
|
1724
1767
|
}
|
|
1725
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE &&
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1768
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1769
|
+
const placeholder = params.addAuto(val);
|
|
1770
|
+
return caseInsensitiveEquals(expr, placeholder);
|
|
1771
|
+
}
|
|
1772
|
+
return null;
|
|
1773
|
+
}
|
|
1774
|
+
function buildScalarOperator(expr, op, val, params, options = {}) {
|
|
1775
|
+
const { mode, fieldType, dialect, depth = 0 } = options;
|
|
1776
|
+
if (val === void 0) return "";
|
|
1777
|
+
if (depth > MAX_NOT_DEPTH) {
|
|
1778
|
+
throw new Error(
|
|
1779
|
+
`NOT operator nesting too deep (max ${MAX_NOT_DEPTH} levels). This usually indicates a circular reference or adversarial input.`
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
if (val === null) {
|
|
1783
|
+
return handleNullValue(expr, op);
|
|
1784
|
+
}
|
|
1785
|
+
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1786
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect, depth);
|
|
1787
|
+
}
|
|
1788
|
+
if (op === Ops.NOT) {
|
|
1789
|
+
const placeholder = params.addAuto(val);
|
|
1790
|
+
return `${expr} <> ${placeholder}`;
|
|
1791
|
+
}
|
|
1792
|
+
validateOperatorRequirements(op, mode, dialect);
|
|
1793
|
+
const specialHandler = routeOperatorHandler(
|
|
1794
|
+
expr,
|
|
1795
|
+
op,
|
|
1796
|
+
val,
|
|
1797
|
+
params,
|
|
1798
|
+
mode,
|
|
1799
|
+
dialect
|
|
1800
|
+
);
|
|
1801
|
+
if (specialHandler !== null) {
|
|
1802
|
+
return specialHandler;
|
|
1729
1803
|
}
|
|
1730
1804
|
return handleComparisonOperator(expr, op, val, params);
|
|
1731
1805
|
}
|
|
@@ -1739,7 +1813,7 @@ function normalizeMode(v) {
|
|
|
1739
1813
|
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1740
1814
|
return void 0;
|
|
1741
1815
|
}
|
|
1742
|
-
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1816
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, depth = 0) {
|
|
1743
1817
|
const innerMode = normalizeMode(val.mode);
|
|
1744
1818
|
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1745
1819
|
const entries = Object.entries(val).filter(
|
|
@@ -1749,43 +1823,35 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
|
1749
1823
|
if (!isNotNullish(dialect)) {
|
|
1750
1824
|
const clauses = [];
|
|
1751
1825
|
for (const [subOp, subVal] of entries) {
|
|
1752
|
-
const sub = buildScalarOperator(
|
|
1753
|
-
|
|
1754
|
-
subOp,
|
|
1755
|
-
subVal,
|
|
1756
|
-
params,
|
|
1757
|
-
effectiveMode,
|
|
1826
|
+
const sub = buildScalarOperator(expr, subOp, subVal, params, {
|
|
1827
|
+
mode: effectiveMode,
|
|
1758
1828
|
fieldType,
|
|
1759
|
-
void 0
|
|
1760
|
-
|
|
1829
|
+
dialect: void 0,
|
|
1830
|
+
depth: depth + 1
|
|
1831
|
+
});
|
|
1761
1832
|
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1762
1833
|
}
|
|
1763
1834
|
if (clauses.length === 0) return "";
|
|
1764
1835
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1765
|
-
|
|
1836
|
+
const separator2 = ` ${SQL_TEMPLATES.AND} `;
|
|
1837
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator2)})`;
|
|
1766
1838
|
}
|
|
1839
|
+
const separator = ` ${SQL_TEMPLATES.AND} `;
|
|
1767
1840
|
return buildNotComposite(
|
|
1768
1841
|
expr,
|
|
1769
1842
|
val,
|
|
1770
1843
|
params,
|
|
1771
1844
|
dialect,
|
|
1772
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p,
|
|
1773
|
-
|
|
1845
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, {
|
|
1846
|
+
mode: effectiveMode,
|
|
1847
|
+
fieldType,
|
|
1848
|
+
dialect: d,
|
|
1849
|
+
depth: depth + 1
|
|
1850
|
+
}),
|
|
1851
|
+
separator
|
|
1774
1852
|
);
|
|
1775
1853
|
}
|
|
1776
|
-
function buildDynamicLikePattern(op, placeholder
|
|
1777
|
-
if (dialect === "postgres") {
|
|
1778
|
-
switch (op) {
|
|
1779
|
-
case Ops.CONTAINS:
|
|
1780
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1781
|
-
case Ops.STARTS_WITH:
|
|
1782
|
-
return `(${placeholder} || '%')`;
|
|
1783
|
-
case Ops.ENDS_WITH:
|
|
1784
|
-
return `('%' || ${placeholder})`;
|
|
1785
|
-
default:
|
|
1786
|
-
return placeholder;
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1854
|
+
function buildDynamicLikePattern(op, placeholder) {
|
|
1789
1855
|
switch (op) {
|
|
1790
1856
|
case Ops.CONTAINS:
|
|
1791
1857
|
return `('%' || ${placeholder} || '%')`;
|
|
@@ -1801,7 +1867,7 @@ function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
|
1801
1867
|
if (val === void 0) return "";
|
|
1802
1868
|
if (schemaParser.isDynamicParameter(val)) {
|
|
1803
1869
|
const placeholder2 = params.addAuto(val);
|
|
1804
|
-
const patternExpr = buildDynamicLikePattern(op, placeholder2
|
|
1870
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2);
|
|
1805
1871
|
if (mode === Modes.INSENSITIVE) {
|
|
1806
1872
|
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1807
1873
|
}
|
|
@@ -1979,24 +2045,42 @@ function handleArrayIsEmpty(expr, val, dialect) {
|
|
|
1979
2045
|
// src/builder/where/operators-json.ts
|
|
1980
2046
|
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1981
2047
|
var MAX_PATH_SEGMENT_LENGTH = 255;
|
|
2048
|
+
var MAX_PATH_SEGMENTS = 100;
|
|
2049
|
+
function sanitizeForError(s) {
|
|
2050
|
+
let result = "";
|
|
2051
|
+
for (let i = 0; i < s.length; i++) {
|
|
2052
|
+
const code = s.charCodeAt(i);
|
|
2053
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
2054
|
+
result += `\\x${code.toString(16).padStart(2, "0")}`;
|
|
2055
|
+
} else {
|
|
2056
|
+
result += s[i];
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
return result;
|
|
2060
|
+
}
|
|
1982
2061
|
function validateJsonPathSegments(segments) {
|
|
1983
|
-
|
|
2062
|
+
if (segments.length > MAX_PATH_SEGMENTS) {
|
|
2063
|
+
throw createError(`JSON path too long: max ${MAX_PATH_SEGMENTS} segments`, {
|
|
2064
|
+
operator: Ops.PATH
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
for (let i = 0; i < segments.length; i++) {
|
|
2068
|
+
const segment = segments[i];
|
|
1984
2069
|
if (typeof segment !== "string") {
|
|
1985
|
-
throw createError(
|
|
1986
|
-
operator: Ops.PATH
|
|
1987
|
-
value: segment
|
|
2070
|
+
throw createError(`JSON path segment at index ${i} must be string`, {
|
|
2071
|
+
operator: Ops.PATH
|
|
1988
2072
|
});
|
|
1989
2073
|
}
|
|
1990
2074
|
if (segment.length > MAX_PATH_SEGMENT_LENGTH) {
|
|
1991
2075
|
throw createError(
|
|
1992
|
-
`JSON path segment too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
1993
|
-
{ operator: Ops.PATH
|
|
2076
|
+
`JSON path segment at index ${i} too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
2077
|
+
{ operator: Ops.PATH }
|
|
1994
2078
|
);
|
|
1995
2079
|
}
|
|
1996
2080
|
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1997
2081
|
throw createError(
|
|
1998
|
-
`Invalid JSON path segment: '${segment}'
|
|
1999
|
-
{ operator: Ops.PATH
|
|
2082
|
+
`Invalid JSON path segment at index ${i}: '${sanitizeForError(segment)}'`,
|
|
2083
|
+
{ operator: Ops.PATH }
|
|
2000
2084
|
);
|
|
2001
2085
|
}
|
|
2002
2086
|
}
|
|
@@ -2087,7 +2171,7 @@ function freezeJoins(items) {
|
|
|
2087
2171
|
function isListRelation(fieldType) {
|
|
2088
2172
|
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
2089
2173
|
}
|
|
2090
|
-
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
2174
|
+
function buildToOneNullCheck(field, parentModel, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
2091
2175
|
const isLocal = field.isForeignKeyLocal === true;
|
|
2092
2176
|
const fkFields = normalizeKeyList(field.foreignKey);
|
|
2093
2177
|
if (isLocal) {
|
|
@@ -2097,8 +2181,7 @@ function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, want
|
|
|
2097
2181
|
});
|
|
2098
2182
|
}
|
|
2099
2183
|
const parts = fkFields.map((fk) => {
|
|
2100
|
-
const
|
|
2101
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
2184
|
+
const expr = `${parentAlias}.${quoteColumn(parentModel, fk)}`;
|
|
2102
2185
|
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
2103
2186
|
});
|
|
2104
2187
|
if (parts.length === 1) return parts[0];
|
|
@@ -2115,6 +2198,35 @@ function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
|
|
|
2115
2198
|
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2116
2199
|
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
2117
2200
|
}
|
|
2201
|
+
function tryOptimizeNoneFilter(noneValue, ctx, relModel, relTable, relAlias, join3, sub) {
|
|
2202
|
+
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
2203
|
+
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
2204
|
+
if (!canOptimize) return null;
|
|
2205
|
+
const checkField = relModel.fields.find(
|
|
2206
|
+
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
2207
|
+
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
2208
|
+
if (!checkField) return null;
|
|
2209
|
+
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
2210
|
+
const whereClause = `${relAlias}.${quoteColumn(relModel, checkField.name)} IS NULL`;
|
|
2211
|
+
return Object.freeze({
|
|
2212
|
+
clause: whereClause,
|
|
2213
|
+
joins: freezeJoins([leftJoinSql])
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
function processRelationFilter(key, wrap, args) {
|
|
2217
|
+
const { value, fieldName, ctx, relAlias, relModel, whereBuilder } = args;
|
|
2218
|
+
const raw = value[key];
|
|
2219
|
+
if (raw === void 0 || raw === null) return null;
|
|
2220
|
+
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2221
|
+
alias: relAlias,
|
|
2222
|
+
model: relModel,
|
|
2223
|
+
path: [...ctx.path, fieldName, key],
|
|
2224
|
+
isSubquery: true,
|
|
2225
|
+
depth: ctx.depth + 1
|
|
2226
|
+
}));
|
|
2227
|
+
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2228
|
+
return wrap(sub.clause, j);
|
|
2229
|
+
}
|
|
2118
2230
|
function buildListRelationFilters(args) {
|
|
2119
2231
|
const {
|
|
2120
2232
|
fieldName,
|
|
@@ -2135,21 +2247,16 @@ function buildListRelationFilters(args) {
|
|
|
2135
2247
|
isSubquery: true,
|
|
2136
2248
|
depth: ctx.depth + 1
|
|
2137
2249
|
}));
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
clause: whereClause,
|
|
2149
|
-
joins: freezeJoins([leftJoinSql])
|
|
2150
|
-
});
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2250
|
+
const optimized = tryOptimizeNoneFilter(
|
|
2251
|
+
noneValue,
|
|
2252
|
+
ctx,
|
|
2253
|
+
relModel,
|
|
2254
|
+
relTable,
|
|
2255
|
+
relAlias,
|
|
2256
|
+
join3,
|
|
2257
|
+
sub
|
|
2258
|
+
);
|
|
2259
|
+
if (optimized) return optimized;
|
|
2153
2260
|
}
|
|
2154
2261
|
const filters = [
|
|
2155
2262
|
{
|
|
@@ -2170,17 +2277,8 @@ function buildListRelationFilters(args) {
|
|
|
2170
2277
|
];
|
|
2171
2278
|
const clauses = [];
|
|
2172
2279
|
for (const { key, wrap } of filters) {
|
|
2173
|
-
const
|
|
2174
|
-
if (
|
|
2175
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2176
|
-
alias: relAlias,
|
|
2177
|
-
model: relModel,
|
|
2178
|
-
path: [...ctx.path, fieldName, key],
|
|
2179
|
-
isSubquery: true,
|
|
2180
|
-
depth: ctx.depth + 1
|
|
2181
|
-
}));
|
|
2182
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2183
|
-
clauses.push(wrap(sub.clause, j));
|
|
2280
|
+
const clause = processRelationFilter(key, wrap, args);
|
|
2281
|
+
if (clause) clauses.push(clause);
|
|
2184
2282
|
}
|
|
2185
2283
|
if (clauses.length === 0) {
|
|
2186
2284
|
throw createError(
|
|
@@ -2236,6 +2334,7 @@ function buildToOneRelationFilters(args) {
|
|
|
2236
2334
|
const wantNull = filterKey === "is";
|
|
2237
2335
|
const clause2 = buildToOneNullCheck(
|
|
2238
2336
|
field,
|
|
2337
|
+
ctx.model,
|
|
2239
2338
|
ctx.alias,
|
|
2240
2339
|
relTable,
|
|
2241
2340
|
relAlias,
|
|
@@ -2465,7 +2564,8 @@ function buildWhereInternal(where, ctx, builder) {
|
|
|
2465
2564
|
}
|
|
2466
2565
|
const allJoins = [];
|
|
2467
2566
|
const clauses = [];
|
|
2468
|
-
for (const
|
|
2567
|
+
for (const key in where) {
|
|
2568
|
+
const value = where[key];
|
|
2469
2569
|
if (value === void 0) continue;
|
|
2470
2570
|
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2471
2571
|
appendResult(result, clauses, allJoins);
|
|
@@ -2580,15 +2680,11 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2580
2680
|
if (fieldType && isJsonType(fieldType) && JSON_OPS.has(op)) {
|
|
2581
2681
|
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2582
2682
|
}
|
|
2583
|
-
return buildScalarOperator(
|
|
2584
|
-
expr,
|
|
2585
|
-
op,
|
|
2586
|
-
val,
|
|
2587
|
-
ctx.params,
|
|
2683
|
+
return buildScalarOperator(expr, op, val, ctx.params, {
|
|
2588
2684
|
mode,
|
|
2589
2685
|
fieldType,
|
|
2590
|
-
ctx.dialect
|
|
2591
|
-
);
|
|
2686
|
+
dialect: ctx.dialect
|
|
2687
|
+
});
|
|
2592
2688
|
}
|
|
2593
2689
|
|
|
2594
2690
|
// src/builder/shared/alias-generator.ts
|
|
@@ -2702,49 +2798,50 @@ function validateState(params, mappings, index) {
|
|
|
2702
2798
|
validateMappings(mappings);
|
|
2703
2799
|
assertNextIndexMatches(mappings.length, index);
|
|
2704
2800
|
}
|
|
2705
|
-
function
|
|
2706
|
-
let index = startIndex;
|
|
2707
|
-
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2708
|
-
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2801
|
+
function buildDynamicNameIndex(mappings) {
|
|
2709
2802
|
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2710
|
-
for (
|
|
2711
|
-
const m = mappings[i];
|
|
2803
|
+
for (const m of mappings) {
|
|
2712
2804
|
if (typeof m.dynamicName === "string") {
|
|
2713
2805
|
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2714
2806
|
}
|
|
2715
2807
|
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2724
|
-
);
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
function format(position) {
|
|
2728
|
-
return `$${position}`;
|
|
2808
|
+
return dynamicNameToIndex;
|
|
2809
|
+
}
|
|
2810
|
+
function assertCanAddParam(currentIndex) {
|
|
2811
|
+
if (currentIndex > MAX_PARAM_INDEX) {
|
|
2812
|
+
throw new Error(
|
|
2813
|
+
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${currentIndex}`
|
|
2814
|
+
);
|
|
2729
2815
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2816
|
+
}
|
|
2817
|
+
function formatPosition(position) {
|
|
2818
|
+
return `$${position}`;
|
|
2819
|
+
}
|
|
2820
|
+
function validateDynamicName(dynamicName) {
|
|
2821
|
+
const dn = dynamicName.trim();
|
|
2822
|
+
if (dn.length === 0) {
|
|
2823
|
+
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2736
2824
|
}
|
|
2825
|
+
return dn;
|
|
2826
|
+
}
|
|
2827
|
+
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2828
|
+
let index = startIndex;
|
|
2829
|
+
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2830
|
+
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2831
|
+
const dynamicNameToIndex = buildDynamicNameIndex(mappings);
|
|
2832
|
+
let dirty = true;
|
|
2833
|
+
let cachedSnapshot = null;
|
|
2737
2834
|
function addDynamic(dynamicName) {
|
|
2738
|
-
const dn =
|
|
2835
|
+
const dn = validateDynamicName(dynamicName);
|
|
2739
2836
|
const existing = dynamicNameToIndex.get(dn);
|
|
2740
|
-
if (existing !== void 0) return
|
|
2837
|
+
if (existing !== void 0) return formatPosition(existing);
|
|
2741
2838
|
const position = index;
|
|
2742
2839
|
dynamicNameToIndex.set(dn, position);
|
|
2743
2840
|
params.push(void 0);
|
|
2744
2841
|
mappings.push({ index: position, dynamicName: dn });
|
|
2745
2842
|
index++;
|
|
2746
2843
|
dirty = true;
|
|
2747
|
-
return
|
|
2844
|
+
return formatPosition(position);
|
|
2748
2845
|
}
|
|
2749
2846
|
function addStatic(value) {
|
|
2750
2847
|
const position = index;
|
|
@@ -2753,10 +2850,10 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2753
2850
|
mappings.push({ index: position, value: normalizedValue });
|
|
2754
2851
|
index++;
|
|
2755
2852
|
dirty = true;
|
|
2756
|
-
return
|
|
2853
|
+
return formatPosition(position);
|
|
2757
2854
|
}
|
|
2758
2855
|
function add(value, dynamicName) {
|
|
2759
|
-
|
|
2856
|
+
assertCanAddParam(index);
|
|
2760
2857
|
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2761
2858
|
}
|
|
2762
2859
|
function addAuto(value) {
|
|
@@ -2768,17 +2865,13 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2768
2865
|
}
|
|
2769
2866
|
function snapshot() {
|
|
2770
2867
|
if (!dirty && cachedSnapshot) return cachedSnapshot;
|
|
2771
|
-
if (!frozenParams) frozenParams = Object.freeze(params.slice());
|
|
2772
|
-
if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
|
|
2773
2868
|
const snap = {
|
|
2774
2869
|
index,
|
|
2775
|
-
params:
|
|
2776
|
-
mappings:
|
|
2870
|
+
params: params.slice(),
|
|
2871
|
+
mappings: mappings.slice()
|
|
2777
2872
|
};
|
|
2778
2873
|
cachedSnapshot = snap;
|
|
2779
2874
|
dirty = false;
|
|
2780
|
-
frozenParams = null;
|
|
2781
|
-
frozenMappings = null;
|
|
2782
2875
|
return snap;
|
|
2783
2876
|
}
|
|
2784
2877
|
return {
|
|
@@ -2981,6 +3074,17 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
|
|
|
2981
3074
|
var MAX_INCLUDE_DEPTH = 10;
|
|
2982
3075
|
var MAX_TOTAL_SUBQUERIES = 100;
|
|
2983
3076
|
var MAX_TOTAL_INCLUDES = 50;
|
|
3077
|
+
function buildIncludeScope(includePath) {
|
|
3078
|
+
if (includePath.length === 0) return "include";
|
|
3079
|
+
let scope = "include";
|
|
3080
|
+
for (let i = 0; i < includePath.length; i++) {
|
|
3081
|
+
scope += `.${includePath[i]}`;
|
|
3082
|
+
if (i < includePath.length - 1) {
|
|
3083
|
+
scope += ".include";
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
return scope;
|
|
3087
|
+
}
|
|
2984
3088
|
function getRelationTableReference(relModel, dialect) {
|
|
2985
3089
|
return buildTableReference(
|
|
2986
3090
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
@@ -3124,19 +3228,11 @@ function finalizeOrderByForInclude(args) {
|
|
|
3124
3228
|
}
|
|
3125
3229
|
function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
|
|
3126
3230
|
let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
|
|
3127
|
-
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
ctx.
|
|
3131
|
-
|
|
3132
|
-
relAlias,
|
|
3133
|
-
ctx.aliasGen,
|
|
3134
|
-
ctx.params,
|
|
3135
|
-
ctx.dialect,
|
|
3136
|
-
ctx.visitPath || [],
|
|
3137
|
-
(ctx.depth || 0) + 1,
|
|
3138
|
-
ctx.stats
|
|
3139
|
-
) : [];
|
|
3231
|
+
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(relArgs, __spreadProps(__spreadValues({}, ctx), {
|
|
3232
|
+
model: relModel,
|
|
3233
|
+
parentAlias: relAlias,
|
|
3234
|
+
depth: (ctx.depth || 0) + 1
|
|
3235
|
+
})) : [];
|
|
3140
3236
|
if (isNonEmptyArray(nestedIncludes)) {
|
|
3141
3237
|
const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3142
3238
|
const nestedSelects = nestedIncludes.map(
|
|
@@ -3191,6 +3287,7 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3191
3287
|
whereClause: args.whereClause
|
|
3192
3288
|
});
|
|
3193
3289
|
if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3290
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3194
3291
|
if (isNotNullish(args.takeVal)) {
|
|
3195
3292
|
return appendLimitOffset(
|
|
3196
3293
|
sql,
|
|
@@ -3198,15 +3295,10 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3198
3295
|
args.ctx.params,
|
|
3199
3296
|
args.takeVal,
|
|
3200
3297
|
args.skipVal,
|
|
3201
|
-
|
|
3298
|
+
scopeBase
|
|
3202
3299
|
);
|
|
3203
3300
|
}
|
|
3204
|
-
return limitOneSql(
|
|
3205
|
-
sql,
|
|
3206
|
-
args.ctx.params,
|
|
3207
|
-
args.skipVal,
|
|
3208
|
-
`include.${args.relName}`
|
|
3209
|
-
);
|
|
3301
|
+
return limitOneSql(sql, args.ctx.params, args.skipVal, scopeBase);
|
|
3210
3302
|
}
|
|
3211
3303
|
function buildListIncludeSpec(args) {
|
|
3212
3304
|
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
@@ -3234,13 +3326,14 @@ function buildListIncludeSpec(args) {
|
|
|
3234
3326
|
whereClause: args.whereClause
|
|
3235
3327
|
});
|
|
3236
3328
|
if (args.orderBySql) base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3329
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3237
3330
|
base = appendLimitOffset(
|
|
3238
3331
|
base,
|
|
3239
3332
|
args.ctx.dialect,
|
|
3240
3333
|
args.ctx.params,
|
|
3241
3334
|
args.takeVal,
|
|
3242
3335
|
args.skipVal,
|
|
3243
|
-
|
|
3336
|
+
scopeBase
|
|
3244
3337
|
);
|
|
3245
3338
|
const selectExpr = jsonAgg("row", args.ctx.dialect);
|
|
3246
3339
|
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
@@ -3289,7 +3382,6 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3289
3382
|
);
|
|
3290
3383
|
if (!isList) {
|
|
3291
3384
|
const sql = buildOneToOneIncludeSql({
|
|
3292
|
-
relName,
|
|
3293
3385
|
relTable,
|
|
3294
3386
|
relAlias,
|
|
3295
3387
|
joins: whereParts.joins,
|
|
@@ -3317,8 +3409,14 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3317
3409
|
ctx
|
|
3318
3410
|
});
|
|
3319
3411
|
}
|
|
3320
|
-
function buildIncludeSqlInternal(args,
|
|
3321
|
-
|
|
3412
|
+
function buildIncludeSqlInternal(args, ctx) {
|
|
3413
|
+
const stats = ctx.stats || {
|
|
3414
|
+
totalIncludes: 0,
|
|
3415
|
+
totalSubqueries: 0,
|
|
3416
|
+
maxDepth: 0
|
|
3417
|
+
};
|
|
3418
|
+
const depth = ctx.depth || 0;
|
|
3419
|
+
const visitPath = ctx.visitPath || [];
|
|
3322
3420
|
if (depth > MAX_INCLUDE_DEPTH) {
|
|
3323
3421
|
throw new Error(
|
|
3324
3422
|
`Maximum include depth of ${MAX_INCLUDE_DEPTH} exceeded. Path: ${visitPath.join(" -> ")}. Deep includes cause exponential SQL complexity and performance issues.`
|
|
@@ -3326,7 +3424,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3326
3424
|
}
|
|
3327
3425
|
stats.maxDepth = Math.max(stats.maxDepth, depth);
|
|
3328
3426
|
const includes = [];
|
|
3329
|
-
const entries = relationEntriesFromArgs(args, model);
|
|
3427
|
+
const entries = relationEntriesFromArgs(args, ctx.model);
|
|
3330
3428
|
for (const [relName, relArgs] of entries) {
|
|
3331
3429
|
if (relArgs === false) continue;
|
|
3332
3430
|
stats.totalIncludes++;
|
|
@@ -3341,8 +3439,12 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3341
3439
|
`Query complexity limit exceeded: ${stats.totalSubqueries} subqueries generated. Maximum allowed: ${MAX_TOTAL_SUBQUERIES}. This indicates exponential include nesting. Stats: depth=${stats.maxDepth}, includes=${stats.totalIncludes}. Path: ${visitPath.join(" -> ")}. Simplify your include structure or split into multiple queries.`
|
|
3342
3440
|
);
|
|
3343
3441
|
}
|
|
3344
|
-
const resolved = resolveRelationOrThrow(
|
|
3345
|
-
|
|
3442
|
+
const resolved = resolveRelationOrThrow(
|
|
3443
|
+
ctx.model,
|
|
3444
|
+
ctx.schemaByName,
|
|
3445
|
+
relName
|
|
3446
|
+
);
|
|
3447
|
+
const relationPath = `${ctx.model.name}.${relName}`;
|
|
3346
3448
|
const currentPath = [...visitPath, relationPath];
|
|
3347
3449
|
if (visitPath.includes(relationPath)) {
|
|
3348
3450
|
throw new Error(
|
|
@@ -3357,19 +3459,14 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3357
3459
|
`Include too deeply nested: model '${resolved.relModel.name}' appears ${modelOccurrences} times in path: ${currentPath.join(" -> ")}`
|
|
3358
3460
|
);
|
|
3359
3461
|
}
|
|
3462
|
+
const nextIncludePath = [...ctx.includePath, relName];
|
|
3360
3463
|
includes.push(
|
|
3361
|
-
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, {
|
|
3362
|
-
|
|
3363
|
-
schemas,
|
|
3364
|
-
schemaByName,
|
|
3365
|
-
parentAlias,
|
|
3366
|
-
aliasGen,
|
|
3367
|
-
dialect,
|
|
3368
|
-
params,
|
|
3464
|
+
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
|
|
3465
|
+
includePath: nextIncludePath,
|
|
3369
3466
|
visitPath: currentPath,
|
|
3370
3467
|
depth: depth + 1,
|
|
3371
3468
|
stats
|
|
3372
|
-
})
|
|
3469
|
+
}))
|
|
3373
3470
|
);
|
|
3374
3471
|
}
|
|
3375
3472
|
return includes;
|
|
@@ -3383,8 +3480,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3383
3480
|
};
|
|
3384
3481
|
const schemaByName = /* @__PURE__ */ new Map();
|
|
3385
3482
|
for (const m of schemas) schemaByName.set(m.name, m);
|
|
3386
|
-
return buildIncludeSqlInternal(
|
|
3387
|
-
args,
|
|
3483
|
+
return buildIncludeSqlInternal(args, {
|
|
3388
3484
|
model,
|
|
3389
3485
|
schemas,
|
|
3390
3486
|
schemaByName,
|
|
@@ -3392,10 +3488,11 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3392
3488
|
aliasGen,
|
|
3393
3489
|
params,
|
|
3394
3490
|
dialect,
|
|
3395
|
-
[],
|
|
3396
|
-
|
|
3491
|
+
includePath: [],
|
|
3492
|
+
visitPath: [],
|
|
3493
|
+
depth: 0,
|
|
3397
3494
|
stats
|
|
3398
|
-
);
|
|
3495
|
+
});
|
|
3399
3496
|
}
|
|
3400
3497
|
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
3401
3498
|
const relationSet = getRelationFieldSet(model);
|
|
@@ -3440,8 +3537,7 @@ function resolveCountKeyPairs(field) {
|
|
|
3440
3537
|
if (fkFields.length === 0) {
|
|
3441
3538
|
throw new Error("Relation count requires foreignKey");
|
|
3442
3539
|
}
|
|
3443
|
-
const
|
|
3444
|
-
const refs = normalizeKeyList(refsRaw);
|
|
3540
|
+
const refs = normalizeKeyList(field.references);
|
|
3445
3541
|
const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
|
|
3446
3542
|
if (refFields.length !== fkFields.length) {
|
|
3447
3543
|
throw new Error(
|
|
@@ -3576,139 +3672,146 @@ function finalizeSql(sql, params, dialect) {
|
|
|
3576
3672
|
paramMappings: snapshot.mappings
|
|
3577
3673
|
};
|
|
3578
3674
|
}
|
|
3579
|
-
function
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
i2 += 2;
|
|
3601
|
-
continue;
|
|
3602
|
-
}
|
|
3603
|
-
if (!saw)
|
|
3604
|
-
throw new Error(
|
|
3605
|
-
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3606
|
-
);
|
|
3607
|
-
return { text: out, next: i2 + 1, quoted: true };
|
|
3608
|
-
}
|
|
3609
|
-
out += s[i2];
|
|
3610
|
-
saw = true;
|
|
3611
|
-
i2++;
|
|
3612
|
-
}
|
|
3613
|
-
throw new Error(
|
|
3614
|
-
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3615
|
-
);
|
|
3616
|
-
}
|
|
3617
|
-
let i = start;
|
|
3618
|
-
while (i < n) {
|
|
3619
|
-
const c = s.charCodeAt(i);
|
|
3620
|
-
if (c === 32 || c === 9) break;
|
|
3621
|
-
if (c === 46) break;
|
|
3622
|
-
i++;
|
|
3623
|
-
}
|
|
3624
|
-
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3625
|
-
};
|
|
3626
|
-
const skipSpaces = (s, i) => {
|
|
3627
|
-
while (i < s.length) {
|
|
3628
|
-
const c = s.charCodeAt(i);
|
|
3629
|
-
if (c !== 32 && c !== 9) break;
|
|
3675
|
+
function isIdent(s) {
|
|
3676
|
+
return /^[A-Za-z_]\w*$/.test(s);
|
|
3677
|
+
}
|
|
3678
|
+
function skipSpaces(s, i) {
|
|
3679
|
+
while (i < s.length) {
|
|
3680
|
+
const c = s.charCodeAt(i);
|
|
3681
|
+
if (c !== 32 && c !== 9) break;
|
|
3682
|
+
i++;
|
|
3683
|
+
}
|
|
3684
|
+
return i;
|
|
3685
|
+
}
|
|
3686
|
+
function parseQuotedIdentifier(s, start) {
|
|
3687
|
+
const n = s.length;
|
|
3688
|
+
let i = start + 1;
|
|
3689
|
+
let out = "";
|
|
3690
|
+
let saw = false;
|
|
3691
|
+
while (i < n) {
|
|
3692
|
+
const c = s.charCodeAt(i);
|
|
3693
|
+
if (c !== 34) {
|
|
3694
|
+
out += s[i];
|
|
3695
|
+
saw = true;
|
|
3630
3696
|
i++;
|
|
3697
|
+
continue;
|
|
3631
3698
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
i = skipSpaces(p, i);
|
|
3639
|
-
const a = readIdentOrQuoted(p, i);
|
|
3640
|
-
const actualAlias = a.text.toLowerCase();
|
|
3641
|
-
if (!isIdent(a.text)) {
|
|
3642
|
-
throw new Error(
|
|
3643
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3644
|
-
);
|
|
3645
|
-
}
|
|
3646
|
-
if (actualAlias !== fromLower) {
|
|
3647
|
-
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3699
|
+
const next = i + 1;
|
|
3700
|
+
if (next < n && s.charCodeAt(next) === 34) {
|
|
3701
|
+
out += '"';
|
|
3702
|
+
saw = true;
|
|
3703
|
+
i += 2;
|
|
3704
|
+
continue;
|
|
3648
3705
|
}
|
|
3649
|
-
|
|
3650
|
-
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3706
|
+
if (!saw) {
|
|
3651
3707
|
throw new Error(
|
|
3652
|
-
`sqlite distinct emulation
|
|
3708
|
+
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3653
3709
|
);
|
|
3654
3710
|
}
|
|
3711
|
+
return { text: out, next: i + 1, quoted: true };
|
|
3712
|
+
}
|
|
3713
|
+
throw new Error(
|
|
3714
|
+
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
function parseUnquotedIdentifier(s, start) {
|
|
3718
|
+
const n = s.length;
|
|
3719
|
+
let i = start;
|
|
3720
|
+
while (i < n) {
|
|
3721
|
+
const c = s.charCodeAt(i);
|
|
3722
|
+
if (c === 32 || c === 9 || c === 46) break;
|
|
3655
3723
|
i++;
|
|
3656
|
-
i = skipSpaces(p, i);
|
|
3657
|
-
const colPart = readIdentOrQuoted(p, i);
|
|
3658
|
-
const columnName = colPart.text.trim();
|
|
3659
|
-
if (columnName.length === 0) {
|
|
3660
|
-
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3661
|
-
}
|
|
3662
|
-
i = colPart.next;
|
|
3663
|
-
i = skipSpaces(p, i);
|
|
3664
|
-
let outAlias = "";
|
|
3665
|
-
if (i < p.length) {
|
|
3666
|
-
const rest = p.slice(i).trim();
|
|
3667
|
-
if (rest.length > 0) {
|
|
3668
|
-
const m = rest.match(/^AS\s+/i);
|
|
3669
|
-
if (!m) {
|
|
3670
|
-
throw new Error(
|
|
3671
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3672
|
-
);
|
|
3673
|
-
}
|
|
3674
|
-
let j = i;
|
|
3675
|
-
j = skipSpaces(p, j);
|
|
3676
|
-
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3677
|
-
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3678
|
-
}
|
|
3679
|
-
j += 2;
|
|
3680
|
-
j = skipSpaces(p, j);
|
|
3681
|
-
const out = readIdentOrQuoted(p, j);
|
|
3682
|
-
outAlias = out.text.trim();
|
|
3683
|
-
if (outAlias.length === 0) {
|
|
3684
|
-
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3685
|
-
}
|
|
3686
|
-
j = skipSpaces(p, out.next);
|
|
3687
|
-
if (j !== p.length) {
|
|
3688
|
-
throw new Error(
|
|
3689
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3690
|
-
);
|
|
3691
|
-
}
|
|
3692
|
-
}
|
|
3693
|
-
}
|
|
3694
|
-
const name = outAlias.length > 0 ? outAlias : columnName;
|
|
3695
|
-
names.push(name);
|
|
3696
3724
|
}
|
|
3697
|
-
return
|
|
3725
|
+
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3726
|
+
}
|
|
3727
|
+
function readIdentOrQuoted(s, start) {
|
|
3728
|
+
const n = s.length;
|
|
3729
|
+
if (start >= n) return { text: "", next: start, quoted: false };
|
|
3730
|
+
if (s.charCodeAt(start) === 34) {
|
|
3731
|
+
return parseQuotedIdentifier(s, start);
|
|
3732
|
+
}
|
|
3733
|
+
return parseUnquotedIdentifier(s, start);
|
|
3734
|
+
}
|
|
3735
|
+
function parseOutputAlias(p, i) {
|
|
3736
|
+
if (i >= p.length) return "";
|
|
3737
|
+
const rest = p.slice(i).trim();
|
|
3738
|
+
if (rest.length === 0) return "";
|
|
3739
|
+
const m = rest.match(/^AS\s+/i);
|
|
3740
|
+
if (!m) {
|
|
3741
|
+
throw new Error(
|
|
3742
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3743
|
+
);
|
|
3744
|
+
}
|
|
3745
|
+
let j = i;
|
|
3746
|
+
j = skipSpaces(p, j);
|
|
3747
|
+
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3748
|
+
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3749
|
+
}
|
|
3750
|
+
j += 2;
|
|
3751
|
+
j = skipSpaces(p, j);
|
|
3752
|
+
const out = readIdentOrQuoted(p, j);
|
|
3753
|
+
const outAlias = out.text.trim();
|
|
3754
|
+
if (outAlias.length === 0) {
|
|
3755
|
+
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3756
|
+
}
|
|
3757
|
+
j = skipSpaces(p, out.next);
|
|
3758
|
+
if (j !== p.length) {
|
|
3759
|
+
throw new Error(
|
|
3760
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3761
|
+
);
|
|
3762
|
+
}
|
|
3763
|
+
return outAlias;
|
|
3764
|
+
}
|
|
3765
|
+
function parseSelectField(p, fromAlias) {
|
|
3766
|
+
const fromLower = fromAlias.toLowerCase();
|
|
3767
|
+
let i = 0;
|
|
3768
|
+
i = skipSpaces(p, i);
|
|
3769
|
+
const a = readIdentOrQuoted(p, i);
|
|
3770
|
+
const actualAlias = a.text.toLowerCase();
|
|
3771
|
+
if (!isIdent(a.text)) {
|
|
3772
|
+
throw new Error(
|
|
3773
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3776
|
+
if (actualAlias !== fromLower) {
|
|
3777
|
+
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3778
|
+
}
|
|
3779
|
+
i = a.next;
|
|
3780
|
+
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3781
|
+
throw new Error(
|
|
3782
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3783
|
+
);
|
|
3784
|
+
}
|
|
3785
|
+
i++;
|
|
3786
|
+
i = skipSpaces(p, i);
|
|
3787
|
+
const colPart = readIdentOrQuoted(p, i);
|
|
3788
|
+
const columnName = colPart.text.trim();
|
|
3789
|
+
if (columnName.length === 0) {
|
|
3790
|
+
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3791
|
+
}
|
|
3792
|
+
i = colPart.next;
|
|
3793
|
+
i = skipSpaces(p, i);
|
|
3794
|
+
const outAlias = parseOutputAlias(p, i);
|
|
3795
|
+
return outAlias.length > 0 ? outAlias : columnName;
|
|
3698
3796
|
}
|
|
3699
|
-
function
|
|
3700
|
-
const
|
|
3701
|
-
if (
|
|
3702
|
-
const
|
|
3703
|
-
const
|
|
3704
|
-
|
|
3797
|
+
function parseSimpleScalarSelect(select, fromAlias) {
|
|
3798
|
+
const raw = select.trim();
|
|
3799
|
+
if (raw.length === 0) return [];
|
|
3800
|
+
const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
|
|
3801
|
+
const names = [];
|
|
3802
|
+
for (const p of parts) {
|
|
3803
|
+
const trimmed = p.trim();
|
|
3804
|
+
if (trimmed.length === 0) continue;
|
|
3805
|
+
names.push(parseSelectField(trimmed, fromAlias));
|
|
3806
|
+
}
|
|
3807
|
+
return names;
|
|
3705
3808
|
}
|
|
3706
3809
|
function buildDistinctColumns(distinct, fromAlias, model) {
|
|
3707
3810
|
return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3708
3811
|
}
|
|
3709
3812
|
function buildOutputColumns(scalarNames, includeNames, hasCount) {
|
|
3710
3813
|
const outputCols = hasCount ? [...scalarNames, ...includeNames, "_count"] : [...scalarNames, ...includeNames];
|
|
3711
|
-
const formatted = outputCols.map((n) =>
|
|
3814
|
+
const formatted = outputCols.map((n) => quote(n)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3712
3815
|
if (!isNonEmptyString(formatted)) {
|
|
3713
3816
|
throw new Error("distinct emulation requires at least one output column");
|
|
3714
3817
|
}
|
|
@@ -3725,6 +3828,52 @@ function buildWindowOrder(args) {
|
|
|
3725
3828
|
const idTiebreaker = idField ? ", " + col(fromAlias, "id", model) + " ASC" : "";
|
|
3726
3829
|
return baseOrder + idTiebreaker;
|
|
3727
3830
|
}
|
|
3831
|
+
function extractDistinctOrderEntries(spec) {
|
|
3832
|
+
if (isNotNullish(spec.args.orderBy)) {
|
|
3833
|
+
const normalized = normalizeOrderByInput(
|
|
3834
|
+
spec.args.orderBy,
|
|
3835
|
+
parseOrderByValue
|
|
3836
|
+
);
|
|
3837
|
+
const entries = [];
|
|
3838
|
+
for (const item of normalized) {
|
|
3839
|
+
for (const field in item) {
|
|
3840
|
+
if (!Object.prototype.hasOwnProperty.call(item, field)) continue;
|
|
3841
|
+
const value = item[field];
|
|
3842
|
+
if (typeof value === "string") {
|
|
3843
|
+
entries.push({ field, direction: value });
|
|
3844
|
+
} else {
|
|
3845
|
+
const obj = value;
|
|
3846
|
+
entries.push({ field, direction: obj.sort, nulls: obj.nulls });
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
if (entries.length > 0) return entries;
|
|
3851
|
+
}
|
|
3852
|
+
if (isNotNullish(spec.distinct) && isNonEmptyArray(spec.distinct)) {
|
|
3853
|
+
return [...spec.distinct].map((f) => ({
|
|
3854
|
+
field: f,
|
|
3855
|
+
direction: "asc"
|
|
3856
|
+
}));
|
|
3857
|
+
}
|
|
3858
|
+
return [];
|
|
3859
|
+
}
|
|
3860
|
+
function buildFieldNameOrderBy(entries, alias) {
|
|
3861
|
+
if (entries.length === 0) return "";
|
|
3862
|
+
const out = [];
|
|
3863
|
+
for (const e of entries) {
|
|
3864
|
+
const dir = e.direction.toUpperCase();
|
|
3865
|
+
const c = `${alias}.${quote(e.field)}`;
|
|
3866
|
+
if (isNotNullish(e.nulls)) {
|
|
3867
|
+
const isNullExpr = `(${c} IS NULL)`;
|
|
3868
|
+
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
3869
|
+
out.push(isNullExpr + " " + nullRankDir);
|
|
3870
|
+
out.push(c + " " + dir);
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
out.push(c + " " + dir);
|
|
3874
|
+
}
|
|
3875
|
+
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
3876
|
+
}
|
|
3728
3877
|
function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
3729
3878
|
var _a, _b;
|
|
3730
3879
|
const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
|
|
@@ -3751,7 +3900,8 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3751
3900
|
fromAlias: from.alias,
|
|
3752
3901
|
model
|
|
3753
3902
|
});
|
|
3754
|
-
const
|
|
3903
|
+
const outerEntries = extractDistinctOrderEntries(spec);
|
|
3904
|
+
const outerOrder = buildFieldNameOrderBy(outerEntries, '"__tp_distinct"');
|
|
3755
3905
|
const joins = buildJoinsSql(whereJoins, countJoins);
|
|
3756
3906
|
const conditions = [];
|
|
3757
3907
|
if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
|
|
@@ -3786,17 +3936,33 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3786
3936
|
}
|
|
3787
3937
|
return outerParts.join(" ");
|
|
3788
3938
|
}
|
|
3939
|
+
function resolveCountSelect(countSelectRaw, model) {
|
|
3940
|
+
if (countSelectRaw === true) {
|
|
3941
|
+
const relationSet = getRelationFieldSet(model);
|
|
3942
|
+
if (relationSet.size === 0) return null;
|
|
3943
|
+
const allRelations = {};
|
|
3944
|
+
for (const name of relationSet) {
|
|
3945
|
+
allRelations[name] = true;
|
|
3946
|
+
}
|
|
3947
|
+
return allRelations;
|
|
3948
|
+
}
|
|
3949
|
+
if (isPlainObject(countSelectRaw) && "select" in countSelectRaw) {
|
|
3950
|
+
return countSelectRaw.select;
|
|
3951
|
+
}
|
|
3952
|
+
return null;
|
|
3953
|
+
}
|
|
3789
3954
|
function buildIncludeColumns(spec) {
|
|
3790
3955
|
var _a, _b;
|
|
3791
3956
|
const { select, includes, dialect, model, schemas, from, params } = spec;
|
|
3792
3957
|
const baseSelect = (select != null ? select : "").trim();
|
|
3793
3958
|
let countCols = "";
|
|
3794
3959
|
let countJoins = [];
|
|
3795
|
-
const
|
|
3796
|
-
if (
|
|
3797
|
-
|
|
3960
|
+
const countSelectRaw = (_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count;
|
|
3961
|
+
if (countSelectRaw) {
|
|
3962
|
+
const resolvedCountSelect = resolveCountSelect(countSelectRaw, model);
|
|
3963
|
+
if (resolvedCountSelect && Object.keys(resolvedCountSelect).length > 0) {
|
|
3798
3964
|
const countBuild = buildRelationCountSql(
|
|
3799
|
-
|
|
3965
|
+
resolvedCountSelect,
|
|
3800
3966
|
model,
|
|
3801
3967
|
schemas,
|
|
3802
3968
|
from.alias,
|
|
@@ -3804,7 +3970,7 @@ function buildIncludeColumns(spec) {
|
|
|
3804
3970
|
dialect
|
|
3805
3971
|
);
|
|
3806
3972
|
if (countBuild.jsonPairs) {
|
|
3807
|
-
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " +
|
|
3973
|
+
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " + quote("_count");
|
|
3808
3974
|
}
|
|
3809
3975
|
countJoins = countBuild.joins;
|
|
3810
3976
|
}
|
|
@@ -3817,7 +3983,7 @@ function buildIncludeColumns(spec) {
|
|
|
3817
3983
|
const emptyJson = dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3818
3984
|
const includeCols = hasIncludes ? includes.map((inc) => {
|
|
3819
3985
|
const expr = inc.isOneToOne ? "(" + inc.sql + ")" : "COALESCE((" + inc.sql + "), " + emptyJson + ")";
|
|
3820
|
-
return expr + " " + SQL_TEMPLATES.AS + " " +
|
|
3986
|
+
return expr + " " + SQL_TEMPLATES.AS + " " + quote(inc.name);
|
|
3821
3987
|
}).join(SQL_SEPARATORS.FIELD_LIST) : "";
|
|
3822
3988
|
const allCols = joinNonEmpty(
|
|
3823
3989
|
[includeCols, countCols],
|
|
@@ -4182,6 +4348,7 @@ function buildComparisons(expr, filter, params, dialect, builder, excludeKeys =
|
|
|
4182
4348
|
}
|
|
4183
4349
|
|
|
4184
4350
|
// src/builder/aggregates.ts
|
|
4351
|
+
var MAX_NOT_DEPTH2 = 50;
|
|
4185
4352
|
var AGGREGATES = [
|
|
4186
4353
|
["_sum", "SUM"],
|
|
4187
4354
|
["_avg", "AVG"],
|
|
@@ -4296,8 +4463,13 @@ function buildBinaryComparison(expr, op, val, params) {
|
|
|
4296
4463
|
const placeholder = addHavingParam(params, op, val);
|
|
4297
4464
|
return expr + " " + sqlOp + " " + placeholder;
|
|
4298
4465
|
}
|
|
4299
|
-
function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
4466
|
+
function buildSimpleComparison(expr, op, val, params, dialect, depth = 0) {
|
|
4300
4467
|
assertHavingOp(op);
|
|
4468
|
+
if (depth > MAX_NOT_DEPTH2) {
|
|
4469
|
+
throw new Error(
|
|
4470
|
+
`NOT operator nesting too deep in HAVING (max ${MAX_NOT_DEPTH2} levels).`
|
|
4471
|
+
);
|
|
4472
|
+
}
|
|
4301
4473
|
if (val === null) return buildNullComparison(expr, op);
|
|
4302
4474
|
if (op === Ops.NOT && isPlainObject(val)) {
|
|
4303
4475
|
return buildNotComposite(
|
|
@@ -4305,7 +4477,7 @@ function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
|
4305
4477
|
val,
|
|
4306
4478
|
params,
|
|
4307
4479
|
dialect,
|
|
4308
|
-
buildSimpleComparison,
|
|
4480
|
+
(e, subOp, subVal, p, d) => buildSimpleComparison(e, subOp, subVal, p, d, depth + 1),
|
|
4309
4481
|
SQL_SEPARATORS.CONDITION_AND
|
|
4310
4482
|
);
|
|
4311
4483
|
}
|
|
@@ -4322,23 +4494,36 @@ function combineLogical(key, subClauses) {
|
|
|
4322
4494
|
if (key === LogicalOps.NOT) return negateClauses(subClauses);
|
|
4323
4495
|
return subClauses.join(" " + key + " ");
|
|
4324
4496
|
}
|
|
4325
|
-
function buildHavingNode(node, alias, params, dialect, model) {
|
|
4497
|
+
function buildHavingNode(node, alias, params, dialect, model, depth = 0) {
|
|
4498
|
+
if (depth > LIMITS.MAX_HAVING_DEPTH) {
|
|
4499
|
+
throw new Error(
|
|
4500
|
+
`HAVING clause nesting too deep (max ${LIMITS.MAX_HAVING_DEPTH} levels). This usually indicates a circular reference.`
|
|
4501
|
+
);
|
|
4502
|
+
}
|
|
4326
4503
|
const clauses = [];
|
|
4327
4504
|
for (const key in node) {
|
|
4328
4505
|
if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
|
|
4329
4506
|
const value = node[key];
|
|
4330
|
-
const built = buildHavingEntry(
|
|
4507
|
+
const built = buildHavingEntry(
|
|
4508
|
+
key,
|
|
4509
|
+
value,
|
|
4510
|
+
alias,
|
|
4511
|
+
params,
|
|
4512
|
+
dialect,
|
|
4513
|
+
model,
|
|
4514
|
+
depth
|
|
4515
|
+
);
|
|
4331
4516
|
for (const c of built) {
|
|
4332
4517
|
if (c && c.length > 0) clauses.push(c);
|
|
4333
4518
|
}
|
|
4334
4519
|
}
|
|
4335
4520
|
return clauses.join(SQL_SEPARATORS.CONDITION_AND);
|
|
4336
4521
|
}
|
|
4337
|
-
function buildLogicalClause2(key, value, alias, params, dialect, model) {
|
|
4522
|
+
function buildLogicalClause2(key, value, alias, params, dialect, model, depth = 0) {
|
|
4338
4523
|
const items = normalizeLogicalValue2(key, value);
|
|
4339
4524
|
const subClauses = [];
|
|
4340
4525
|
for (const it of items) {
|
|
4341
|
-
const c = buildHavingNode(it, alias, params, dialect, model);
|
|
4526
|
+
const c = buildHavingNode(it, alias, params, dialect, model, depth + 1);
|
|
4342
4527
|
if (c && c.length > 0) subClauses.push("(" + c + ")");
|
|
4343
4528
|
}
|
|
4344
4529
|
if (subClauses.length === 0) return "";
|
|
@@ -4397,7 +4582,7 @@ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect
|
|
|
4397
4582
|
}
|
|
4398
4583
|
return out;
|
|
4399
4584
|
}
|
|
4400
|
-
function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
4585
|
+
function buildHavingEntry(key, value, alias, params, dialect, model, depth = 0) {
|
|
4401
4586
|
if (isLogicalKey(key)) {
|
|
4402
4587
|
const logical = buildLogicalClause2(
|
|
4403
4588
|
key,
|
|
@@ -4405,7 +4590,8 @@ function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
|
4405
4590
|
alias,
|
|
4406
4591
|
params,
|
|
4407
4592
|
dialect,
|
|
4408
|
-
model
|
|
4593
|
+
model,
|
|
4594
|
+
depth
|
|
4409
4595
|
);
|
|
4410
4596
|
return logical ? [logical] : [];
|
|
4411
4597
|
}
|
|
@@ -4432,7 +4618,7 @@ function buildHavingClause(having, alias, params, model, dialect) {
|
|
|
4432
4618
|
if (!isNotNullish(having)) return "";
|
|
4433
4619
|
const d = dialect != null ? dialect : getGlobalDialect();
|
|
4434
4620
|
if (!isPlainObject(having)) throw new Error("having must be an object");
|
|
4435
|
-
return buildHavingNode(having, alias, params, d, model);
|
|
4621
|
+
return buildHavingNode(having, alias, params, d, model, 0);
|
|
4436
4622
|
}
|
|
4437
4623
|
function normalizeCountArg(v) {
|
|
4438
4624
|
if (!isNotNullish(v)) return void 0;
|
|
@@ -4442,13 +4628,13 @@ function normalizeCountArg(v) {
|
|
|
4442
4628
|
}
|
|
4443
4629
|
function pushCountAllField(fields) {
|
|
4444
4630
|
fields.push(
|
|
4445
|
-
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " +
|
|
4631
|
+
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " + quote("_count._all")
|
|
4446
4632
|
);
|
|
4447
4633
|
}
|
|
4448
4634
|
function pushCountField(fields, alias, fieldName, model) {
|
|
4449
4635
|
const outAlias = "_count." + fieldName;
|
|
4450
4636
|
fields.push(
|
|
4451
|
-
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4637
|
+
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4452
4638
|
);
|
|
4453
4639
|
}
|
|
4454
4640
|
function addCountFields(fields, countArg, alias, model) {
|
|
@@ -4485,7 +4671,7 @@ function assertAggregatableScalarField(model, agg, fieldName) {
|
|
|
4485
4671
|
function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model) {
|
|
4486
4672
|
const outAlias = agg + "." + fieldName;
|
|
4487
4673
|
fields.push(
|
|
4488
|
-
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4674
|
+
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4489
4675
|
);
|
|
4490
4676
|
}
|
|
4491
4677
|
function addAggregateFields(fields, args, alias, model) {
|
|
@@ -4569,7 +4755,7 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4569
4755
|
assertSafeAlias(alias);
|
|
4570
4756
|
assertSafeTableRef(tableName);
|
|
4571
4757
|
const byFields = assertGroupByBy(args, model);
|
|
4572
|
-
const d = getGlobalDialect();
|
|
4758
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
4573
4759
|
const params = createParamStore(whereResult.nextParamIndex);
|
|
4574
4760
|
const { groupFields, selectFields } = buildGroupBySelectParts(
|
|
4575
4761
|
args,
|
|
@@ -4601,38 +4787,38 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4601
4787
|
paramMappings: allMappings
|
|
4602
4788
|
};
|
|
4603
4789
|
}
|
|
4790
|
+
function isPositiveInteger(value) {
|
|
4791
|
+
return Number.isFinite(value) && Number.isInteger(value) && value > 0;
|
|
4792
|
+
}
|
|
4793
|
+
function parseSkipValue(skip) {
|
|
4794
|
+
return typeof skip === "string" ? Number(skip.trim()) : skip;
|
|
4795
|
+
}
|
|
4796
|
+
function validateSkipParameter(skip) {
|
|
4797
|
+
if (skip === void 0 || skip === null) {
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4800
|
+
if (schemaParser.isDynamicParameter(skip)) {
|
|
4801
|
+
throw new Error(
|
|
4802
|
+
"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."
|
|
4803
|
+
);
|
|
4804
|
+
}
|
|
4805
|
+
const skipValue = parseSkipValue(skip);
|
|
4806
|
+
if (isPositiveInteger(skipValue)) {
|
|
4807
|
+
throw new Error(
|
|
4808
|
+
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4809
|
+
);
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4604
4812
|
function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
4605
4813
|
assertSafeAlias(alias);
|
|
4606
4814
|
assertSafeTableRef(tableName);
|
|
4607
|
-
|
|
4608
|
-
if (schemaParser.isDynamicParameter(skip)) {
|
|
4609
|
-
throw new Error(
|
|
4610
|
-
"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."
|
|
4611
|
-
);
|
|
4612
|
-
}
|
|
4613
|
-
if (typeof skip === "string") {
|
|
4614
|
-
const s = skip.trim();
|
|
4615
|
-
if (s.length > 0) {
|
|
4616
|
-
const n = Number(s);
|
|
4617
|
-
if (Number.isFinite(n) && Number.isInteger(n) && n > 0) {
|
|
4618
|
-
throw new Error(
|
|
4619
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4620
|
-
);
|
|
4621
|
-
}
|
|
4622
|
-
}
|
|
4623
|
-
}
|
|
4624
|
-
if (typeof skip === "number" && Number.isFinite(skip) && Number.isInteger(skip) && skip > 0) {
|
|
4625
|
-
throw new Error(
|
|
4626
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4627
|
-
);
|
|
4628
|
-
}
|
|
4629
|
-
}
|
|
4815
|
+
validateSkipParameter(skip);
|
|
4630
4816
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4631
4817
|
const parts = [
|
|
4632
4818
|
SQL_TEMPLATES.SELECT,
|
|
4633
4819
|
SQL_TEMPLATES.COUNT_ALL,
|
|
4634
4820
|
SQL_TEMPLATES.AS,
|
|
4635
|
-
|
|
4821
|
+
quote("_count._all"),
|
|
4636
4822
|
SQL_TEMPLATES.FROM,
|
|
4637
4823
|
tableName,
|
|
4638
4824
|
alias
|
|
@@ -4673,15 +4859,21 @@ function buildSqlResult(args) {
|
|
|
4673
4859
|
return buildAggregateSql(processed, whereResult, tableName, alias, modelDef);
|
|
4674
4860
|
}
|
|
4675
4861
|
if (method === "groupBy") {
|
|
4676
|
-
return buildGroupBySql(
|
|
4862
|
+
return buildGroupBySql(
|
|
4863
|
+
processed,
|
|
4864
|
+
whereResult,
|
|
4865
|
+
tableName,
|
|
4866
|
+
alias,
|
|
4867
|
+
modelDef,
|
|
4868
|
+
dialect
|
|
4869
|
+
);
|
|
4677
4870
|
}
|
|
4678
4871
|
if (method === "count") {
|
|
4679
4872
|
return buildCountSql(
|
|
4680
4873
|
whereResult,
|
|
4681
4874
|
tableName,
|
|
4682
4875
|
alias,
|
|
4683
|
-
processed.skip
|
|
4684
|
-
);
|
|
4876
|
+
processed.skip);
|
|
4685
4877
|
}
|
|
4686
4878
|
return buildSelectSql({
|
|
4687
4879
|
method,
|
|
@@ -4721,22 +4913,23 @@ function normalizeSqlAndMappingsForDialect(sql, paramMappings, dialect) {
|
|
|
4721
4913
|
}
|
|
4722
4914
|
function buildParamsFromMappings(mappings) {
|
|
4723
4915
|
const sorted = [...mappings].sort((a, b) => a.index - b.index);
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4916
|
+
const staticParams = [];
|
|
4917
|
+
const dynamicKeys = [];
|
|
4918
|
+
let paramOrder = "";
|
|
4919
|
+
for (const m of sorted) {
|
|
4920
|
+
if (m.dynamicName !== void 0) {
|
|
4921
|
+
dynamicKeys.push(m.dynamicName);
|
|
4922
|
+
paramOrder += "d";
|
|
4923
|
+
} else if (m.value !== void 0) {
|
|
4924
|
+
staticParams.push(m.value);
|
|
4925
|
+
paramOrder += "s";
|
|
4926
|
+
} else {
|
|
4734
4927
|
throw new Error(
|
|
4735
4928
|
`CRITICAL: ParamMap ${m.index} has neither dynamicName nor value`
|
|
4736
4929
|
);
|
|
4737
|
-
}
|
|
4738
|
-
|
|
4739
|
-
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
return { staticParams, dynamicKeys, paramOrder };
|
|
4740
4933
|
}
|
|
4741
4934
|
function resolveModelContext(directive) {
|
|
4742
4935
|
const { model, datamodel } = directive.context;
|
|
@@ -4802,12 +4995,13 @@ function finalizeDirective(args) {
|
|
|
4802
4995
|
return (_a = m.value) != null ? _a : void 0;
|
|
4803
4996
|
});
|
|
4804
4997
|
validateParamConsistencyByDialect(normalizedSql, params, dialect);
|
|
4805
|
-
const { staticParams, dynamicKeys } = buildParamsFromMappings(normalizedMappings);
|
|
4998
|
+
const { staticParams, dynamicKeys, paramOrder } = buildParamsFromMappings(normalizedMappings);
|
|
4806
4999
|
return {
|
|
4807
5000
|
method: directive.method,
|
|
4808
5001
|
sql: normalizedSql,
|
|
4809
5002
|
staticParams,
|
|
4810
5003
|
dynamicKeys,
|
|
5004
|
+
paramOrder,
|
|
4811
5005
|
paramMappings: normalizedMappings,
|
|
4812
5006
|
originalDirective: directive
|
|
4813
5007
|
};
|
|
@@ -4842,6 +5036,257 @@ function generateSQL(directive) {
|
|
|
4842
5036
|
dialect
|
|
4843
5037
|
});
|
|
4844
5038
|
}
|
|
5039
|
+
|
|
5040
|
+
// src/utils/s3-fifo.ts
|
|
5041
|
+
function withDispose(it) {
|
|
5042
|
+
const anyIt = it;
|
|
5043
|
+
if (anyIt[Symbol.dispose] === void 0) {
|
|
5044
|
+
anyIt[Symbol.dispose] = () => {
|
|
5045
|
+
};
|
|
5046
|
+
}
|
|
5047
|
+
return it;
|
|
5048
|
+
}
|
|
5049
|
+
var BoundedCache = class {
|
|
5050
|
+
constructor(maxSize) {
|
|
5051
|
+
this.map = /* @__PURE__ */ new Map();
|
|
5052
|
+
this.ghost = /* @__PURE__ */ new Set();
|
|
5053
|
+
this.smallHead = null;
|
|
5054
|
+
this.smallTail = null;
|
|
5055
|
+
this.smallSize = 0;
|
|
5056
|
+
this.mainHead = null;
|
|
5057
|
+
this.mainTail = null;
|
|
5058
|
+
this.mainSize = 0;
|
|
5059
|
+
this.maxSize = maxSize;
|
|
5060
|
+
this.smallLimit = Math.max(1, Math.floor(maxSize * 0.1));
|
|
5061
|
+
this.mainLimit = maxSize - this.smallLimit;
|
|
5062
|
+
this.ghostLimit = this.mainLimit;
|
|
5063
|
+
}
|
|
5064
|
+
get size() {
|
|
5065
|
+
return this.map.size;
|
|
5066
|
+
}
|
|
5067
|
+
get(key) {
|
|
5068
|
+
const node = this.map.get(key);
|
|
5069
|
+
if (!node) return void 0;
|
|
5070
|
+
node.freq = Math.min(node.freq + 1, 3);
|
|
5071
|
+
return node.value;
|
|
5072
|
+
}
|
|
5073
|
+
set(key, value) {
|
|
5074
|
+
const existing = this.map.get(key);
|
|
5075
|
+
if (existing) {
|
|
5076
|
+
existing.value = value;
|
|
5077
|
+
return this;
|
|
5078
|
+
}
|
|
5079
|
+
if (this.ghost.has(key)) {
|
|
5080
|
+
this.ghost.delete(key);
|
|
5081
|
+
const node2 = this.createNode(key, value, "main");
|
|
5082
|
+
this.map.set(key, node2);
|
|
5083
|
+
this.pushMain(node2);
|
|
5084
|
+
if (this.mainSize > this.mainLimit) this.evictMain();
|
|
5085
|
+
return this;
|
|
5086
|
+
}
|
|
5087
|
+
const node = this.createNode(key, value, "small");
|
|
5088
|
+
this.map.set(key, node);
|
|
5089
|
+
this.pushSmall(node);
|
|
5090
|
+
if (this.size > this.maxSize) {
|
|
5091
|
+
if (this.smallSize > this.smallLimit) this.evictSmall();
|
|
5092
|
+
else this.evictMain();
|
|
5093
|
+
}
|
|
5094
|
+
return this;
|
|
5095
|
+
}
|
|
5096
|
+
has(key) {
|
|
5097
|
+
return this.map.has(key);
|
|
5098
|
+
}
|
|
5099
|
+
delete(key) {
|
|
5100
|
+
const node = this.map.get(key);
|
|
5101
|
+
if (!node) return false;
|
|
5102
|
+
this.map.delete(key);
|
|
5103
|
+
this.removeNode(node);
|
|
5104
|
+
return true;
|
|
5105
|
+
}
|
|
5106
|
+
clear() {
|
|
5107
|
+
this.map.clear();
|
|
5108
|
+
this.ghost.clear();
|
|
5109
|
+
this.smallHead = this.smallTail = null;
|
|
5110
|
+
this.mainHead = this.mainTail = null;
|
|
5111
|
+
this.smallSize = this.mainSize = 0;
|
|
5112
|
+
}
|
|
5113
|
+
keys() {
|
|
5114
|
+
return withDispose(
|
|
5115
|
+
(function* (self) {
|
|
5116
|
+
for (const key of self.map.keys()) yield key;
|
|
5117
|
+
})(this)
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
values() {
|
|
5121
|
+
return withDispose(
|
|
5122
|
+
(function* (self) {
|
|
5123
|
+
for (const node of self.map.values()) yield node.value;
|
|
5124
|
+
})(this)
|
|
5125
|
+
);
|
|
5126
|
+
}
|
|
5127
|
+
entries() {
|
|
5128
|
+
return withDispose(
|
|
5129
|
+
(function* (self) {
|
|
5130
|
+
for (const [key, node] of self.map.entries())
|
|
5131
|
+
yield [key, node.value];
|
|
5132
|
+
})(this)
|
|
5133
|
+
);
|
|
5134
|
+
}
|
|
5135
|
+
forEach(callbackfn, thisArg) {
|
|
5136
|
+
for (const [key, node] of this.map.entries()) {
|
|
5137
|
+
callbackfn.call(thisArg, node.value, key, this);
|
|
5138
|
+
}
|
|
5139
|
+
}
|
|
5140
|
+
[Symbol.iterator]() {
|
|
5141
|
+
return this.entries();
|
|
5142
|
+
}
|
|
5143
|
+
get [Symbol.toStringTag]() {
|
|
5144
|
+
return "BoundedCache";
|
|
5145
|
+
}
|
|
5146
|
+
createNode(key, value, queue) {
|
|
5147
|
+
return { key, value, freq: 0, queue, prev: null, next: null };
|
|
5148
|
+
}
|
|
5149
|
+
pushSmall(node) {
|
|
5150
|
+
node.next = this.smallHead;
|
|
5151
|
+
node.prev = null;
|
|
5152
|
+
if (this.smallHead) this.smallHead.prev = node;
|
|
5153
|
+
else this.smallTail = node;
|
|
5154
|
+
this.smallHead = node;
|
|
5155
|
+
this.smallSize++;
|
|
5156
|
+
}
|
|
5157
|
+
pushMain(node) {
|
|
5158
|
+
node.next = this.mainHead;
|
|
5159
|
+
node.prev = null;
|
|
5160
|
+
if (this.mainHead) this.mainHead.prev = node;
|
|
5161
|
+
else this.mainTail = node;
|
|
5162
|
+
this.mainHead = node;
|
|
5163
|
+
this.mainSize++;
|
|
5164
|
+
}
|
|
5165
|
+
popSmall() {
|
|
5166
|
+
if (!this.smallTail) return null;
|
|
5167
|
+
const node = this.smallTail;
|
|
5168
|
+
this.smallTail = node.prev;
|
|
5169
|
+
if (this.smallTail) this.smallTail.next = null;
|
|
5170
|
+
else this.smallHead = null;
|
|
5171
|
+
node.prev = null;
|
|
5172
|
+
node.next = null;
|
|
5173
|
+
this.smallSize--;
|
|
5174
|
+
return node;
|
|
5175
|
+
}
|
|
5176
|
+
popMain() {
|
|
5177
|
+
if (!this.mainTail) return null;
|
|
5178
|
+
const node = this.mainTail;
|
|
5179
|
+
this.mainTail = node.prev;
|
|
5180
|
+
if (this.mainTail) this.mainTail.next = null;
|
|
5181
|
+
else this.mainHead = null;
|
|
5182
|
+
node.prev = null;
|
|
5183
|
+
node.next = null;
|
|
5184
|
+
this.mainSize--;
|
|
5185
|
+
return node;
|
|
5186
|
+
}
|
|
5187
|
+
removeNode(node) {
|
|
5188
|
+
this.unlinkNode(node);
|
|
5189
|
+
if (node.queue === "small") {
|
|
5190
|
+
if (node === this.smallHead) this.smallHead = node.next;
|
|
5191
|
+
if (node === this.smallTail) this.smallTail = node.prev;
|
|
5192
|
+
this.smallSize--;
|
|
5193
|
+
} else {
|
|
5194
|
+
if (node === this.mainHead) this.mainHead = node.next;
|
|
5195
|
+
if (node === this.mainTail) this.mainTail = node.prev;
|
|
5196
|
+
this.mainSize--;
|
|
5197
|
+
}
|
|
5198
|
+
node.prev = null;
|
|
5199
|
+
node.next = null;
|
|
5200
|
+
}
|
|
5201
|
+
unlinkNode(node) {
|
|
5202
|
+
if (node.prev) node.prev.next = node.next;
|
|
5203
|
+
if (node.next) node.next.prev = node.prev;
|
|
5204
|
+
}
|
|
5205
|
+
shouldPromoteFromSmall(node) {
|
|
5206
|
+
return node.freq > 1;
|
|
5207
|
+
}
|
|
5208
|
+
shouldRetryInMain(node) {
|
|
5209
|
+
return node.freq >= 1;
|
|
5210
|
+
}
|
|
5211
|
+
promoteToMain(node) {
|
|
5212
|
+
node.queue = "main";
|
|
5213
|
+
this.pushMain(node);
|
|
5214
|
+
}
|
|
5215
|
+
addToGhost(key) {
|
|
5216
|
+
this.ghost.add(key);
|
|
5217
|
+
if (this.ghost.size <= this.ghostLimit) return;
|
|
5218
|
+
const firstGhost = this.ghost.values().next().value;
|
|
5219
|
+
if (firstGhost !== void 0) this.ghost.delete(firstGhost);
|
|
5220
|
+
}
|
|
5221
|
+
evictFromCache(node) {
|
|
5222
|
+
this.map.delete(node.key);
|
|
5223
|
+
}
|
|
5224
|
+
evictSmall() {
|
|
5225
|
+
while (this.smallSize > 0) {
|
|
5226
|
+
const node = this.popSmall();
|
|
5227
|
+
if (!node) return;
|
|
5228
|
+
if (this.shouldPromoteFromSmall(node)) {
|
|
5229
|
+
this.promoteToMain(node);
|
|
5230
|
+
if (this.mainSize > this.mainLimit) {
|
|
5231
|
+
this.evictMain();
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
continue;
|
|
5235
|
+
}
|
|
5236
|
+
this.evictFromCache(node);
|
|
5237
|
+
this.addToGhost(node.key);
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
evictMain() {
|
|
5242
|
+
while (this.mainSize > 0) {
|
|
5243
|
+
const node = this.popMain();
|
|
5244
|
+
if (!node) return;
|
|
5245
|
+
if (this.shouldRetryInMain(node)) {
|
|
5246
|
+
node.freq--;
|
|
5247
|
+
this.pushMain(node);
|
|
5248
|
+
continue;
|
|
5249
|
+
}
|
|
5250
|
+
this.evictFromCache(node);
|
|
5251
|
+
return;
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
};
|
|
5255
|
+
function createBoundedCache(maxSize) {
|
|
5256
|
+
return new BoundedCache(maxSize);
|
|
5257
|
+
}
|
|
5258
|
+
|
|
5259
|
+
// src/query-cache.ts
|
|
5260
|
+
var _hits, _misses;
|
|
5261
|
+
var QueryCacheStats = class {
|
|
5262
|
+
constructor() {
|
|
5263
|
+
__privateAdd(this, _hits, 0);
|
|
5264
|
+
__privateAdd(this, _misses, 0);
|
|
5265
|
+
}
|
|
5266
|
+
hit() {
|
|
5267
|
+
__privateWrapper(this, _hits)._++;
|
|
5268
|
+
}
|
|
5269
|
+
miss() {
|
|
5270
|
+
__privateWrapper(this, _misses)._++;
|
|
5271
|
+
}
|
|
5272
|
+
reset() {
|
|
5273
|
+
__privateSet(this, _hits, 0);
|
|
5274
|
+
__privateSet(this, _misses, 0);
|
|
5275
|
+
}
|
|
5276
|
+
get snapshot() {
|
|
5277
|
+
return Object.freeze({
|
|
5278
|
+
hits: __privateGet(this, _hits),
|
|
5279
|
+
misses: __privateGet(this, _misses),
|
|
5280
|
+
size: queryCache.size
|
|
5281
|
+
});
|
|
5282
|
+
}
|
|
5283
|
+
};
|
|
5284
|
+
_hits = new WeakMap();
|
|
5285
|
+
_misses = new WeakMap();
|
|
5286
|
+
var queryCache = createBoundedCache(1e3);
|
|
5287
|
+
new QueryCacheStats();
|
|
5288
|
+
|
|
5289
|
+
// src/index.ts
|
|
4845
5290
|
function generateSQL2(directive) {
|
|
4846
5291
|
return generateSQL(directive);
|
|
4847
5292
|
}
|
|
@@ -4870,6 +5315,57 @@ function extractEnumMappings(datamodel) {
|
|
|
4870
5315
|
}
|
|
4871
5316
|
return { mappings, fieldTypes };
|
|
4872
5317
|
}
|
|
5318
|
+
function processModelDirectives(modelName, result, config) {
|
|
5319
|
+
const modelQueries = /* @__PURE__ */ new Map();
|
|
5320
|
+
let skipped = 0;
|
|
5321
|
+
for (const directive of result.directives) {
|
|
5322
|
+
try {
|
|
5323
|
+
const method = directive.method;
|
|
5324
|
+
const sqlDirective = generateSQL2(directive);
|
|
5325
|
+
if (!modelQueries.has(method)) {
|
|
5326
|
+
modelQueries.set(method, /* @__PURE__ */ new Map());
|
|
5327
|
+
}
|
|
5328
|
+
const methodQueriesMap = modelQueries.get(method);
|
|
5329
|
+
const queryKey = createQueryKey(directive.query.processed);
|
|
5330
|
+
methodQueriesMap.set(queryKey, {
|
|
5331
|
+
sql: sqlDirective.sql,
|
|
5332
|
+
params: sqlDirective.staticParams,
|
|
5333
|
+
dynamicKeys: sqlDirective.dynamicKeys,
|
|
5334
|
+
paramMappings: sqlDirective.paramMappings
|
|
5335
|
+
});
|
|
5336
|
+
} catch (error) {
|
|
5337
|
+
if (!config.skipInvalid) throw error;
|
|
5338
|
+
skipped++;
|
|
5339
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
5340
|
+
console.warn(` \u26A0 Skipped ${modelName}.${directive.method}: ${errMsg}`);
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
return { modelQueries, skipped };
|
|
5344
|
+
}
|
|
5345
|
+
function processAllModelDirectives(directiveResults, config) {
|
|
5346
|
+
const queries = /* @__PURE__ */ new Map();
|
|
5347
|
+
let skippedCount = 0;
|
|
5348
|
+
for (const [modelName, result] of directiveResults) {
|
|
5349
|
+
if (result.directives.length === 0) continue;
|
|
5350
|
+
const { modelQueries, skipped } = processModelDirectives(
|
|
5351
|
+
modelName,
|
|
5352
|
+
result,
|
|
5353
|
+
config
|
|
5354
|
+
);
|
|
5355
|
+
queries.set(modelName, modelQueries);
|
|
5356
|
+
skippedCount += skipped;
|
|
5357
|
+
}
|
|
5358
|
+
return { queries, skippedCount };
|
|
5359
|
+
}
|
|
5360
|
+
function countTotalQueries(queries) {
|
|
5361
|
+
return Array.from(queries.values()).reduce(
|
|
5362
|
+
(sum, methodMap) => sum + Array.from(methodMap.values()).reduce(
|
|
5363
|
+
(s, queryMap) => s + queryMap.size,
|
|
5364
|
+
0
|
|
5365
|
+
),
|
|
5366
|
+
0
|
|
5367
|
+
);
|
|
5368
|
+
}
|
|
4873
5369
|
function generateClient(options) {
|
|
4874
5370
|
return __async(this, null, function* () {
|
|
4875
5371
|
const { datamodel, outputDir, config } = options;
|
|
@@ -4882,48 +5378,22 @@ function generateClient(options) {
|
|
|
4882
5378
|
skipInvalid: config.skipInvalid
|
|
4883
5379
|
}
|
|
4884
5380
|
);
|
|
4885
|
-
const queries
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
queries.set(modelName, /* @__PURE__ */ new Map());
|
|
4890
|
-
}
|
|
4891
|
-
const modelQueries = queries.get(modelName);
|
|
4892
|
-
for (const directive of result.directives) {
|
|
4893
|
-
try {
|
|
4894
|
-
const method = directive.method;
|
|
4895
|
-
const sqlDirective = generateSQL2(directive);
|
|
4896
|
-
if (!modelQueries.has(method)) {
|
|
4897
|
-
modelQueries.set(method, /* @__PURE__ */ new Map());
|
|
4898
|
-
}
|
|
4899
|
-
const methodQueriesMap = modelQueries.get(method);
|
|
4900
|
-
const queryKey = createQueryKey(directive.query.processed);
|
|
4901
|
-
methodQueriesMap.set(queryKey, {
|
|
4902
|
-
sql: sqlDirective.sql,
|
|
4903
|
-
params: sqlDirective.staticParams,
|
|
4904
|
-
dynamicKeys: sqlDirective.dynamicKeys,
|
|
4905
|
-
paramMappings: sqlDirective.paramMappings
|
|
4906
|
-
});
|
|
4907
|
-
} catch (error) {
|
|
4908
|
-
if (!config.skipInvalid) throw error;
|
|
4909
|
-
}
|
|
4910
|
-
}
|
|
4911
|
-
}
|
|
5381
|
+
const { queries, skippedCount } = processAllModelDirectives(
|
|
5382
|
+
directiveResults,
|
|
5383
|
+
config
|
|
5384
|
+
);
|
|
4912
5385
|
const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
|
|
4913
5386
|
yield promises.mkdir(absoluteOutputDir, { recursive: true });
|
|
4914
5387
|
const code = generateCode(models, queries, config.dialect, datamodel);
|
|
4915
5388
|
const outputPath = path.join(absoluteOutputDir, "index.ts");
|
|
4916
5389
|
yield promises.writeFile(outputPath, code);
|
|
4917
|
-
const totalQueries =
|
|
4918
|
-
(sum, methodMap) => sum + Array.from(methodMap.values()).reduce(
|
|
4919
|
-
(s, queryMap) => s + queryMap.size,
|
|
4920
|
-
0
|
|
4921
|
-
),
|
|
4922
|
-
0
|
|
4923
|
-
);
|
|
5390
|
+
const totalQueries = countTotalQueries(queries);
|
|
4924
5391
|
console.log(
|
|
4925
5392
|
`\u2713 Generated ${queries.size} models, ${totalQueries} prebaked queries`
|
|
4926
5393
|
);
|
|
5394
|
+
if (skippedCount > 0) {
|
|
5395
|
+
console.log(`\u26A0 Skipped ${skippedCount} directive(s) due to errors`);
|
|
5396
|
+
}
|
|
4927
5397
|
console.log(`\u2713 Output: ${outputPath}`);
|
|
4928
5398
|
});
|
|
4929
5399
|
}
|
|
@@ -4939,15 +5409,13 @@ function createQueryKey(processedQuery) {
|
|
|
4939
5409
|
return value;
|
|
4940
5410
|
});
|
|
4941
5411
|
}
|
|
4942
|
-
function
|
|
4943
|
-
const cleanModels = models.map((model) => __spreadProps(__spreadValues({}, model), {
|
|
4944
|
-
fields: model.fields.filter((f) => f !== void 0 && f !== null)
|
|
4945
|
-
}));
|
|
4946
|
-
const { mappings, fieldTypes } = extractEnumMappings(datamodel);
|
|
5412
|
+
function generateImports() {
|
|
4947
5413
|
return `// Generated by @prisma-sql/generator - DO NOT EDIT
|
|
4948
5414
|
import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type BatchCountQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
|
|
4949
|
-
|
|
4950
|
-
|
|
5415
|
+
import { normalizeValue } from 'prisma-sql/utils/normalize-value'`;
|
|
5416
|
+
}
|
|
5417
|
+
function generateCoreTypes() {
|
|
5418
|
+
return `class DeferredQuery {
|
|
4951
5419
|
constructor(
|
|
4952
5420
|
public readonly model: string,
|
|
4953
5421
|
public readonly method: PrismaMethod,
|
|
@@ -4979,22 +5447,21 @@ const ACCELERATED_METHODS = new Set<PrismaMethod>([
|
|
|
4979
5447
|
'count',
|
|
4980
5448
|
'aggregate',
|
|
4981
5449
|
'groupBy',
|
|
4982
|
-
])
|
|
4983
|
-
|
|
4984
|
-
function
|
|
5450
|
+
])`;
|
|
5451
|
+
}
|
|
5452
|
+
function generateHelpers() {
|
|
5453
|
+
return `function createBatchProxy(): BatchProxy {
|
|
4985
5454
|
return new Proxy(
|
|
4986
5455
|
{},
|
|
4987
5456
|
{
|
|
4988
5457
|
get(_target, modelName: string): any {
|
|
4989
5458
|
if (typeof modelName === 'symbol') return undefined
|
|
4990
|
-
|
|
4991
5459
|
const model = MODEL_MAP.get(modelName)
|
|
4992
5460
|
if (!model) {
|
|
4993
5461
|
throw new Error(
|
|
4994
5462
|
\`Model '\${modelName}' not found. Available: \${[...MODEL_MAP.keys()].join(', ')}\`,
|
|
4995
5463
|
)
|
|
4996
5464
|
}
|
|
4997
|
-
|
|
4998
5465
|
return new Proxy(
|
|
4999
5466
|
{},
|
|
5000
5467
|
{
|
|
@@ -5004,7 +5471,6 @@ function createBatchProxy(): BatchProxy {
|
|
|
5004
5471
|
\`Method '\${method}' not supported in batch. Supported: \${[...ACCELERATED_METHODS].join(', ')}\`,
|
|
5005
5472
|
)
|
|
5006
5473
|
}
|
|
5007
|
-
|
|
5008
5474
|
return (args?: any): DeferredQuery => {
|
|
5009
5475
|
return new DeferredQuery(
|
|
5010
5476
|
modelName,
|
|
@@ -5020,52 +5486,6 @@ function createBatchProxy(): BatchProxy {
|
|
|
5020
5486
|
) as BatchProxy
|
|
5021
5487
|
}
|
|
5022
5488
|
|
|
5023
|
-
function normalizeValue(value: unknown, seen = new WeakSet<object>(), depth = 0): unknown {
|
|
5024
|
-
const MAX_DEPTH = 20
|
|
5025
|
-
if (depth > MAX_DEPTH) {
|
|
5026
|
-
throw new Error(\`Max normalization depth exceeded (\${MAX_DEPTH} levels)\`)
|
|
5027
|
-
}
|
|
5028
|
-
if (value instanceof Date) {
|
|
5029
|
-
const t = value.getTime()
|
|
5030
|
-
if (!Number.isFinite(t)) {
|
|
5031
|
-
throw new Error('Invalid Date value in SQL params')
|
|
5032
|
-
}
|
|
5033
|
-
return value.toISOString()
|
|
5034
|
-
}
|
|
5035
|
-
if (typeof value === 'bigint') {
|
|
5036
|
-
return value.toString()
|
|
5037
|
-
}
|
|
5038
|
-
if (Array.isArray(value)) {
|
|
5039
|
-
const arrRef = value as unknown as object
|
|
5040
|
-
if (seen.has(arrRef)) {
|
|
5041
|
-
throw new Error('Circular reference in SQL params')
|
|
5042
|
-
}
|
|
5043
|
-
seen.add(arrRef)
|
|
5044
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1))
|
|
5045
|
-
seen.delete(arrRef)
|
|
5046
|
-
return out
|
|
5047
|
-
}
|
|
5048
|
-
if (value && typeof value === 'object') {
|
|
5049
|
-
if (value instanceof Uint8Array) return value
|
|
5050
|
-
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) return value
|
|
5051
|
-
const proto = Object.getPrototypeOf(value)
|
|
5052
|
-
const isPlain = proto === Object.prototype || proto === null
|
|
5053
|
-
if (!isPlain) return value
|
|
5054
|
-
const obj = value as Record<string, unknown>
|
|
5055
|
-
if (seen.has(obj)) {
|
|
5056
|
-
throw new Error('Circular reference in SQL params')
|
|
5057
|
-
}
|
|
5058
|
-
seen.add(obj)
|
|
5059
|
-
const out: Record<string, unknown> = {}
|
|
5060
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
5061
|
-
out[k] = normalizeValue(v, seen, depth + 1)
|
|
5062
|
-
}
|
|
5063
|
-
seen.delete(obj)
|
|
5064
|
-
return out
|
|
5065
|
-
}
|
|
5066
|
-
return value
|
|
5067
|
-
}
|
|
5068
|
-
|
|
5069
5489
|
function normalizeParams(params: unknown[]): unknown[] {
|
|
5070
5490
|
return params.map(p => normalizeValue(p))
|
|
5071
5491
|
}
|
|
@@ -5081,7 +5501,68 @@ function getByPath(obj: any, path: string): unknown {
|
|
|
5081
5501
|
return result
|
|
5082
5502
|
}
|
|
5083
5503
|
|
|
5084
|
-
|
|
5504
|
+
function resolveParamsFromMappings(args: any, paramMappings: any[]): unknown[] {
|
|
5505
|
+
const params: unknown[] = []
|
|
5506
|
+
for (let i = 0; i < paramMappings.length; i++) {
|
|
5507
|
+
const m = paramMappings[i]
|
|
5508
|
+
if (m.value !== undefined) {
|
|
5509
|
+
params.push(m.value)
|
|
5510
|
+
continue
|
|
5511
|
+
}
|
|
5512
|
+
if (m.dynamicName === undefined) {
|
|
5513
|
+
throw new Error(\`CRITICAL: ParamMap \${m.index} has neither dynamicName nor value\`)
|
|
5514
|
+
}
|
|
5515
|
+
const colonIdx = m.dynamicName.indexOf(':')
|
|
5516
|
+
const scopePath = colonIdx !== -1 ? m.dynamicName.slice(0, colonIdx) : null
|
|
5517
|
+
const name = colonIdx !== -1 ? m.dynamicName.slice(colonIdx + 1) : m.dynamicName
|
|
5518
|
+
let value: unknown
|
|
5519
|
+
if (!scopePath || scopePath.startsWith('root.')) {
|
|
5520
|
+
value = name.includes('.')
|
|
5521
|
+
? getByPath(args, name)
|
|
5522
|
+
: args?.[name]
|
|
5523
|
+
} else {
|
|
5524
|
+
value = getByPath(args, scopePath)
|
|
5525
|
+
}
|
|
5526
|
+
if (value === undefined) {
|
|
5527
|
+
throw new Error(\`Missing required parameter: \${m.dynamicName}\`)
|
|
5528
|
+
}
|
|
5529
|
+
params.push(normalizeValue(value))
|
|
5530
|
+
}
|
|
5531
|
+
return params
|
|
5532
|
+
}
|
|
5533
|
+
|
|
5534
|
+
function shouldSqliteUseGet(method: string): boolean {
|
|
5535
|
+
return (
|
|
5536
|
+
method === 'count' ||
|
|
5537
|
+
method === 'findFirst' ||
|
|
5538
|
+
method === 'findUnique' ||
|
|
5539
|
+
method === 'aggregate'
|
|
5540
|
+
)
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
async function executeQuery(client: any, method: string, sql: string, params: unknown[]): Promise<unknown[]> {
|
|
5544
|
+
const normalizedParams = normalizeParams(params)
|
|
5545
|
+
if (DIALECT === 'postgres') {
|
|
5546
|
+
return await client.unsafe(sql, normalizedParams)
|
|
5547
|
+
}
|
|
5548
|
+
const stmt = client.prepare(sql)
|
|
5549
|
+
if (shouldSqliteUseGet(method)) {
|
|
5550
|
+
const row = stmt.get(...normalizedParams)
|
|
5551
|
+
if (row === undefined) return []
|
|
5552
|
+
return [row]
|
|
5553
|
+
}
|
|
5554
|
+
return stmt.all(...normalizedParams)
|
|
5555
|
+
}
|
|
5556
|
+
|
|
5557
|
+
async function executeRaw(client: any, sql: string, params?: unknown[]): Promise<unknown[]> {
|
|
5558
|
+
if (DIALECT === 'postgres') {
|
|
5559
|
+
return await client.unsafe(sql, (params || []) as any[])
|
|
5560
|
+
}
|
|
5561
|
+
throw new Error('Raw execution for sqlite not supported in transactions')
|
|
5562
|
+
}`;
|
|
5563
|
+
}
|
|
5564
|
+
function generateDataConstants(cleanModels, mappings, fieldTypes, queries, dialect) {
|
|
5565
|
+
return `export const MODELS: Model[] = ${JSON.stringify(cleanModels, null, 2)}
|
|
5085
5566
|
|
|
5086
5567
|
const ENUM_MAPPINGS: Record<string, Record<string, string>> = ${JSON.stringify(mappings, null, 2)}
|
|
5087
5568
|
|
|
@@ -5096,12 +5577,19 @@ const QUERIES: Record<string, Record<string, Record<string, {
|
|
|
5096
5577
|
|
|
5097
5578
|
const DIALECT = ${JSON.stringify(dialect)}
|
|
5098
5579
|
|
|
5099
|
-
const MODEL_MAP = new Map(MODELS.map(m => [m.name, m]))
|
|
5100
|
-
|
|
5101
|
-
function
|
|
5580
|
+
const MODEL_MAP = new Map(MODELS.map(m => [m.name, m]))`;
|
|
5581
|
+
}
|
|
5582
|
+
function generateTransformLogic() {
|
|
5583
|
+
return `function isDynamicKeyForQueryKey(path: string[], key: string): boolean {
|
|
5102
5584
|
if (key !== 'skip' && key !== 'take' && key !== 'cursor') return false
|
|
5585
|
+
const parent = path.length > 0 ? path[path.length - 1] : null
|
|
5586
|
+
if (!parent) return true
|
|
5587
|
+
if (parent === 'include' || parent === 'select') return false
|
|
5103
5588
|
if (path.includes('where')) return false
|
|
5104
5589
|
if (path.includes('data')) return false
|
|
5590
|
+
if (path.includes('orderBy')) return false
|
|
5591
|
+
if (path.includes('having')) return false
|
|
5592
|
+
if (path.includes('by')) return false
|
|
5105
5593
|
return true
|
|
5106
5594
|
}
|
|
5107
5595
|
|
|
@@ -5128,6 +5616,14 @@ function transformEnumInValue(value: unknown, enumType: string | undefined): unk
|
|
|
5128
5616
|
return mapping[value]
|
|
5129
5617
|
}
|
|
5130
5618
|
|
|
5619
|
+
if (typeof value === 'object' && !(value instanceof Date)) {
|
|
5620
|
+
const result: Record<string, unknown> = {}
|
|
5621
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
5622
|
+
result[k] = transformEnumInValue(v, enumType)
|
|
5623
|
+
}
|
|
5624
|
+
return result
|
|
5625
|
+
}
|
|
5626
|
+
|
|
5131
5627
|
return value
|
|
5132
5628
|
}
|
|
5133
5629
|
|
|
@@ -5155,19 +5651,16 @@ function transformEnumValuesByModel(modelName: string, obj: any, path: string[]
|
|
|
5155
5651
|
if (typeof obj === 'object') {
|
|
5156
5652
|
const transformed: any = {}
|
|
5157
5653
|
const modelFields = ENUM_FIELDS[modelName] || {}
|
|
5158
|
-
|
|
5159
5654
|
for (const [key, value] of Object.entries(obj)) {
|
|
5160
5655
|
const nextPath = [...path, key]
|
|
5161
5656
|
|
|
5162
5657
|
const relModel = getRelatedModelName(modelName, key)
|
|
5163
|
-
|
|
5164
5658
|
if (relModel && value && typeof value === 'object') {
|
|
5165
5659
|
transformed[key] = transformEnumValuesByModel(relModel, value, nextPath)
|
|
5166
5660
|
continue
|
|
5167
5661
|
}
|
|
5168
5662
|
|
|
5169
5663
|
const enumType = modelFields[key]
|
|
5170
|
-
|
|
5171
5664
|
if (enumType) {
|
|
5172
5665
|
if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
|
5173
5666
|
const transformedOperators: any = {}
|
|
@@ -5186,25 +5679,27 @@ function transformEnumValuesByModel(modelName: string, obj: any, path: string[]
|
|
|
5186
5679
|
transformed[key] = value
|
|
5187
5680
|
}
|
|
5188
5681
|
}
|
|
5189
|
-
|
|
5190
5682
|
return transformed
|
|
5191
5683
|
}
|
|
5192
5684
|
|
|
5193
5685
|
return obj
|
|
5194
5686
|
}
|
|
5195
5687
|
|
|
5688
|
+
function bigIntSafeReplacer(_key: string, value: unknown): unknown {
|
|
5689
|
+
if (typeof value === 'bigint') return '__bigint__' + value.toString()
|
|
5690
|
+
return value
|
|
5691
|
+
}
|
|
5692
|
+
|
|
5196
5693
|
function normalizeQuery(args: any): string {
|
|
5197
5694
|
if (!args) return '{}'
|
|
5198
|
-
|
|
5199
|
-
const normalized = JSON.parse(
|
|
5695
|
+
const jsonStr = JSON.stringify(args, bigIntSafeReplacer)
|
|
5696
|
+
const normalized = JSON.parse(jsonStr)
|
|
5200
5697
|
|
|
5201
5698
|
function replaceDynamicParams(obj: any, path: string[] = []): any {
|
|
5202
5699
|
if (!obj || typeof obj !== 'object') return obj
|
|
5203
|
-
|
|
5204
5700
|
if (Array.isArray(obj)) {
|
|
5205
5701
|
return obj.map((v) => replaceDynamicParams(v, path))
|
|
5206
5702
|
}
|
|
5207
|
-
|
|
5208
5703
|
const result: any = {}
|
|
5209
5704
|
for (const [key, value] of Object.entries(obj)) {
|
|
5210
5705
|
if (isDynamicKeyForQueryKey(path, key)) {
|
|
@@ -5218,22 +5713,8 @@ function normalizeQuery(args: any): string {
|
|
|
5218
5713
|
|
|
5219
5714
|
const withMarkers = replaceDynamicParams(normalized)
|
|
5220
5715
|
|
|
5221
|
-
|
|
5222
|
-
if (
|
|
5223
|
-
|
|
5224
|
-
const result: any = {}
|
|
5225
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
5226
|
-
if (value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
5227
|
-
continue
|
|
5228
|
-
}
|
|
5229
|
-
result[key] = removeEmptyObjects(value)
|
|
5230
|
-
}
|
|
5231
|
-
return result
|
|
5232
|
-
}
|
|
5233
|
-
|
|
5234
|
-
const cleaned = removeEmptyObjects(withMarkers)
|
|
5235
|
-
|
|
5236
|
-
return JSON.stringify(cleaned, (key, value) => {
|
|
5716
|
+
return JSON.stringify(withMarkers, (key, value) => {
|
|
5717
|
+
if (typeof value === 'bigint') return '__bigint__' + value.toString()
|
|
5237
5718
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
5238
5719
|
const sorted: Record<string, unknown> = {}
|
|
5239
5720
|
for (const k of Object.keys(value).sort()) {
|
|
@@ -5243,52 +5724,10 @@ function normalizeQuery(args: any): string {
|
|
|
5243
5724
|
}
|
|
5244
5725
|
return value
|
|
5245
5726
|
})
|
|
5727
|
+
}`;
|
|
5246
5728
|
}
|
|
5247
|
-
|
|
5248
|
-
function
|
|
5249
|
-
const params: unknown[] = []
|
|
5250
|
-
|
|
5251
|
-
for (const key of dynamicKeys) {
|
|
5252
|
-
const parts = key.split(':')
|
|
5253
|
-
const lookupKey = parts.length === 2 ? parts[1] : key
|
|
5254
|
-
|
|
5255
|
-
const value =
|
|
5256
|
-
lookupKey.includes('.') ? getByPath(args, lookupKey) : args?.[lookupKey]
|
|
5257
|
-
|
|
5258
|
-
if (value === undefined) {
|
|
5259
|
-
throw new Error(\`Missing required parameter: \${key}\`)
|
|
5260
|
-
}
|
|
5261
|
-
|
|
5262
|
-
params.push(normalizeValue(value))
|
|
5263
|
-
}
|
|
5264
|
-
|
|
5265
|
-
return params
|
|
5266
|
-
}
|
|
5267
|
-
|
|
5268
|
-
async function executeQuery(client: any, sql: string, params: unknown[]): Promise<unknown[]> {
|
|
5269
|
-
const normalizedParams = normalizeParams(params)
|
|
5270
|
-
|
|
5271
|
-
if (DIALECT === 'postgres') {
|
|
5272
|
-
return await client.unsafe(sql, normalizedParams)
|
|
5273
|
-
}
|
|
5274
|
-
|
|
5275
|
-
const stmt = client.prepare(sql)
|
|
5276
|
-
|
|
5277
|
-
if (sql.toUpperCase().includes('COUNT(*) AS')) {
|
|
5278
|
-
return [stmt.get(...normalizedParams)]
|
|
5279
|
-
}
|
|
5280
|
-
|
|
5281
|
-
return stmt.all(...normalizedParams)
|
|
5282
|
-
}
|
|
5283
|
-
|
|
5284
|
-
async function executeRaw(client: any, sql: string, params?: unknown[]): Promise<unknown[]> {
|
|
5285
|
-
if (DIALECT === 'postgres') {
|
|
5286
|
-
return await client.unsafe(sql, (params || []) as any[])
|
|
5287
|
-
}
|
|
5288
|
-
throw new Error('Raw execution for sqlite not supported in transactions')
|
|
5289
|
-
}
|
|
5290
|
-
|
|
5291
|
-
export function speedExtension(config: {
|
|
5729
|
+
function generateExtension() {
|
|
5730
|
+
return `export function speedExtension(config: {
|
|
5292
5731
|
postgres?: any
|
|
5293
5732
|
sqlite?: any
|
|
5294
5733
|
debug?: boolean
|
|
@@ -5302,14 +5741,11 @@ export function speedExtension(config: {
|
|
|
5302
5741
|
}) => void
|
|
5303
5742
|
}) {
|
|
5304
5743
|
const { postgres, sqlite, debug, onQuery } = config
|
|
5305
|
-
|
|
5306
5744
|
if (!postgres && !sqlite) {
|
|
5307
5745
|
throw new Error('speedExtension requires postgres or sqlite client')
|
|
5308
5746
|
}
|
|
5309
|
-
|
|
5310
5747
|
const client = postgres || sqlite
|
|
5311
5748
|
const actualDialect = postgres ? 'postgres' : 'sqlite'
|
|
5312
|
-
|
|
5313
5749
|
if (actualDialect !== DIALECT) {
|
|
5314
5750
|
throw new Error(\`Generated code is for \${DIALECT}, but you provided \${actualDialect}\`)
|
|
5315
5751
|
}
|
|
@@ -5327,53 +5763,55 @@ export function speedExtension(config: {
|
|
|
5327
5763
|
const modelName = this?.name || this?.$name
|
|
5328
5764
|
const startTime = Date.now()
|
|
5329
5765
|
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
return this.$parent[modelName][method](args)
|
|
5766
|
+
try {
|
|
5767
|
+
const transformedArgs = transformEnumValuesByModel(modelName, args || {})
|
|
5768
|
+
const queryKey = normalizeQuery(transformedArgs)
|
|
5769
|
+
const prebakedQuery = QUERIES[modelName]?.[method]?.[queryKey]
|
|
5770
|
+
let sql: string
|
|
5771
|
+
let params: unknown[]
|
|
5772
|
+
let prebaked = false
|
|
5773
|
+
|
|
5774
|
+
if (prebakedQuery) {
|
|
5775
|
+
sql = prebakedQuery.sql
|
|
5776
|
+
params = resolveParamsFromMappings(transformedArgs, prebakedQuery.paramMappings)
|
|
5777
|
+
prebaked = true
|
|
5778
|
+
} else {
|
|
5779
|
+
const model = MODELS.find((m) => m.name === modelName)
|
|
5780
|
+
if (!model) {
|
|
5781
|
+
return this.$parent[modelName][method](args)
|
|
5782
|
+
}
|
|
5783
|
+
const result = buildSQL(model, MODELS, method, transformedArgs, DIALECT)
|
|
5784
|
+
sql = result.sql
|
|
5785
|
+
params = result.params as unknown[]
|
|
5351
5786
|
}
|
|
5352
5787
|
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5788
|
+
if (debug) {
|
|
5789
|
+
console.log(\`[\${DIALECT}] \${modelName}.\${method} \${prebaked ? '\u26A1 PREBAKED' : '\u{1F528} RUNTIME'}\`)
|
|
5790
|
+
console.log('SQL:', sql)
|
|
5791
|
+
console.log('Params:', params)
|
|
5792
|
+
}
|
|
5357
5793
|
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5794
|
+
const results = await executeQuery(client, method, sql, params)
|
|
5795
|
+
const duration = Date.now() - startTime
|
|
5796
|
+
|
|
5797
|
+
onQuery?.({
|
|
5798
|
+
model: modelName,
|
|
5799
|
+
method,
|
|
5800
|
+
sql,
|
|
5801
|
+
params,
|
|
5802
|
+
duration,
|
|
5803
|
+
prebaked,
|
|
5804
|
+
})
|
|
5805
|
+
|
|
5806
|
+
return transformQueryResults(method, results)
|
|
5807
|
+
} catch (error) {
|
|
5808
|
+
const msg = error instanceof Error ? error.message : String(error)
|
|
5809
|
+
console.warn(\`[prisma-sql] \${modelName}.\${method} acceleration failed, falling back to Prisma: \${msg}\`)
|
|
5810
|
+
if (debug && error instanceof Error && error.stack) {
|
|
5811
|
+
console.warn(error.stack)
|
|
5812
|
+
}
|
|
5813
|
+
return this.$parent[modelName][method](args)
|
|
5362
5814
|
}
|
|
5363
|
-
|
|
5364
|
-
const results = await executeQuery(client, sql, params)
|
|
5365
|
-
const duration = Date.now() - startTime
|
|
5366
|
-
|
|
5367
|
-
onQuery?.({
|
|
5368
|
-
model: modelName,
|
|
5369
|
-
method,
|
|
5370
|
-
sql,
|
|
5371
|
-
params,
|
|
5372
|
-
duration,
|
|
5373
|
-
prebaked,
|
|
5374
|
-
})
|
|
5375
|
-
|
|
5376
|
-
return transformQueryResults(method, results)
|
|
5377
5815
|
}
|
|
5378
5816
|
|
|
5379
5817
|
async function batch<T extends Record<string, DeferredQuery>>(
|
|
@@ -5381,20 +5819,17 @@ export function speedExtension(config: {
|
|
|
5381
5819
|
): Promise<{ [K in keyof T]: any }> {
|
|
5382
5820
|
const batchProxy = createBatchProxy()
|
|
5383
5821
|
const queries = await callback(batchProxy)
|
|
5384
|
-
|
|
5385
5822
|
const batchQueries: Record<string, BatchQuery> = {}
|
|
5386
|
-
|
|
5387
5823
|
for (const [key, deferred] of Object.entries(queries)) {
|
|
5388
5824
|
if (!(deferred instanceof DeferredQuery)) {
|
|
5389
5825
|
throw new Error(
|
|
5390
5826
|
\`Batch query '\${key}' must be a deferred query. Did you await it?\`,
|
|
5391
5827
|
)
|
|
5392
5828
|
}
|
|
5393
|
-
|
|
5394
5829
|
batchQueries[key] = {
|
|
5395
5830
|
model: deferred.model,
|
|
5396
5831
|
method: deferred.method,
|
|
5397
|
-
args: deferred.args || {},
|
|
5832
|
+
args: transformEnumValuesByModel(deferred.model, deferred.args || {}),
|
|
5398
5833
|
}
|
|
5399
5834
|
}
|
|
5400
5835
|
|
|
@@ -5415,9 +5850,9 @@ export function speedExtension(config: {
|
|
|
5415
5850
|
const normalizedParams = normalizeParams(params)
|
|
5416
5851
|
const rows = await client.unsafe(sql, normalizedParams as any[])
|
|
5417
5852
|
const row = rows[0] as Record<string, unknown>
|
|
5418
|
-
const results = parseBatchResults(row, keys, batchQueries, aliases)
|
|
5419
|
-
const duration = Date.now() - startTime
|
|
5853
|
+
const results = parseBatchResults(row, keys, batchQueries, aliases, MODEL_MAP)
|
|
5420
5854
|
|
|
5855
|
+
const duration = Date.now() - startTime
|
|
5421
5856
|
onQuery?.({
|
|
5422
5857
|
model: '_batch',
|
|
5423
5858
|
method: 'batch',
|
|
@@ -5432,7 +5867,6 @@ export function speedExtension(config: {
|
|
|
5432
5867
|
|
|
5433
5868
|
async function batchCount(queries: BatchCountQuery[]): Promise<number[]> {
|
|
5434
5869
|
if (queries.length === 0) return []
|
|
5435
|
-
|
|
5436
5870
|
const startTime = Date.now()
|
|
5437
5871
|
const { sql, params } = buildBatchCountSql(
|
|
5438
5872
|
queries,
|
|
@@ -5451,8 +5885,8 @@ export function speedExtension(config: {
|
|
|
5451
5885
|
const rows = await client.unsafe(sql, normalizedParams as any[])
|
|
5452
5886
|
const row = rows[0] as Record<string, unknown>
|
|
5453
5887
|
const results = parseBatchCountResults(row, queries.length)
|
|
5454
|
-
const duration = Date.now() - startTime
|
|
5455
5888
|
|
|
5889
|
+
const duration = Date.now() - startTime
|
|
5456
5890
|
onQuery?.({
|
|
5457
5891
|
model: '_batch',
|
|
5458
5892
|
method: 'count',
|
|
@@ -5470,14 +5904,15 @@ export function speedExtension(config: {
|
|
|
5470
5904
|
options?: TransactionOptions,
|
|
5471
5905
|
): Promise<unknown[]> {
|
|
5472
5906
|
const startTime = Date.now()
|
|
5473
|
-
|
|
5474
5907
|
if (debug) {
|
|
5475
5908
|
console.log(\`[\${DIALECT}] $transaction (\${queries.length} queries)\`)
|
|
5476
5909
|
}
|
|
5477
|
-
|
|
5478
|
-
|
|
5910
|
+
const transformedQueries = queries.map(q => ({
|
|
5911
|
+
...q,
|
|
5912
|
+
args: transformEnumValuesByModel(q.model, q.args || {}),
|
|
5913
|
+
}))
|
|
5914
|
+
const results = await txExecutor.execute(transformedQueries, options)
|
|
5479
5915
|
const duration = Date.now() - startTime
|
|
5480
|
-
|
|
5481
5916
|
onQuery?.({
|
|
5482
5917
|
model: '_transaction',
|
|
5483
5918
|
method: 'transaction',
|
|
@@ -5486,13 +5921,11 @@ export function speedExtension(config: {
|
|
|
5486
5921
|
duration,
|
|
5487
5922
|
prebaked: false,
|
|
5488
5923
|
})
|
|
5489
|
-
|
|
5490
5924
|
return results
|
|
5491
5925
|
}
|
|
5492
5926
|
|
|
5493
5927
|
return prisma.$extends({
|
|
5494
5928
|
name: 'prisma-sql-generated',
|
|
5495
|
-
|
|
5496
5929
|
client: {
|
|
5497
5930
|
$original: prisma,
|
|
5498
5931
|
$batch: batch as <T extends Record<string, DeferredQuery>>(
|
|
@@ -5501,7 +5934,6 @@ export function speedExtension(config: {
|
|
|
5501
5934
|
$batchCount: batchCount as (...args: any[]) => Promise<number[]>,
|
|
5502
5935
|
$transaction: transaction as (...args: any[]) => Promise<unknown[]>,
|
|
5503
5936
|
},
|
|
5504
|
-
|
|
5505
5937
|
model: {
|
|
5506
5938
|
$allModels: {
|
|
5507
5939
|
async findMany(args: any) {
|
|
@@ -5526,9 +5958,10 @@ export function speedExtension(config: {
|
|
|
5526
5958
|
},
|
|
5527
5959
|
})
|
|
5528
5960
|
}
|
|
5961
|
+
}`;
|
|
5529
5962
|
}
|
|
5530
|
-
|
|
5531
|
-
type SpeedExtensionReturn = ReturnType<ReturnType<typeof speedExtension>>
|
|
5963
|
+
function generateTypeExports() {
|
|
5964
|
+
return `type SpeedExtensionReturn = ReturnType<ReturnType<typeof speedExtension>>
|
|
5532
5965
|
|
|
5533
5966
|
export type SpeedClient<T> = T & {
|
|
5534
5967
|
$batch<T extends Record<string, DeferredQuery>>(
|
|
@@ -5538,8 +5971,22 @@ export type SpeedClient<T> = T & {
|
|
|
5538
5971
|
$transaction(queries: TransactionQuery[], options?: TransactionOptions): Promise<unknown[]>
|
|
5539
5972
|
}
|
|
5540
5973
|
|
|
5541
|
-
export type { BatchCountQuery, TransactionQuery, TransactionOptions }
|
|
5542
|
-
|
|
5974
|
+
export type { BatchCountQuery, TransactionQuery, TransactionOptions }`;
|
|
5975
|
+
}
|
|
5976
|
+
function generateCode(models, queries, dialect, datamodel) {
|
|
5977
|
+
const cleanModels = models.map((model) => __spreadProps(__spreadValues({}, model), {
|
|
5978
|
+
fields: model.fields.filter((f) => f !== void 0 && f !== null)
|
|
5979
|
+
}));
|
|
5980
|
+
const { mappings, fieldTypes } = extractEnumMappings(datamodel);
|
|
5981
|
+
return [
|
|
5982
|
+
generateImports(),
|
|
5983
|
+
generateCoreTypes(),
|
|
5984
|
+
generateHelpers(),
|
|
5985
|
+
generateDataConstants(cleanModels, mappings, fieldTypes, queries, dialect),
|
|
5986
|
+
generateTransformLogic(),
|
|
5987
|
+
generateExtension(),
|
|
5988
|
+
generateTypeExports()
|
|
5989
|
+
].join("\n\n");
|
|
5543
5990
|
}
|
|
5544
5991
|
function formatQueries(queries) {
|
|
5545
5992
|
if (queries.size === 0) {
|