prisma-sql 1.59.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 +1163 -673
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1163 -673
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1469 -1682
- 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 +1470 -1670
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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,155 +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
|
-
MAX_HAVING_DEPTH: 50
|
|
302
|
-
});
|
|
303
|
-
|
|
304
170
|
// src/utils/normalize-value.ts
|
|
305
171
|
var MAX_DEPTH = 20;
|
|
306
172
|
function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
@@ -308,45 +174,54 @@ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0)
|
|
|
308
174
|
throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
|
|
309
175
|
}
|
|
310
176
|
if (value instanceof Date) {
|
|
311
|
-
|
|
312
|
-
if (!Number.isFinite(t)) {
|
|
313
|
-
throw new Error("Invalid Date value in SQL params");
|
|
314
|
-
}
|
|
315
|
-
return value.toISOString();
|
|
177
|
+
return normalizeDateValue(value);
|
|
316
178
|
}
|
|
317
179
|
if (typeof value === "bigint") {
|
|
318
180
|
return value.toString();
|
|
319
181
|
}
|
|
320
182
|
if (Array.isArray(value)) {
|
|
321
|
-
|
|
322
|
-
if (seen.has(arrRef)) {
|
|
323
|
-
throw new Error("Circular reference in SQL params");
|
|
324
|
-
}
|
|
325
|
-
seen.add(arrRef);
|
|
326
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
327
|
-
seen.delete(arrRef);
|
|
328
|
-
return out;
|
|
183
|
+
return normalizeArrayValue(value, seen, depth);
|
|
329
184
|
}
|
|
330
185
|
if (value && typeof value === "object") {
|
|
331
|
-
|
|
332
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
333
|
-
const proto = Object.getPrototypeOf(value);
|
|
334
|
-
const isPlain = proto === Object.prototype || proto === null;
|
|
335
|
-
if (!isPlain) return value;
|
|
336
|
-
const obj = value;
|
|
337
|
-
if (seen.has(obj)) {
|
|
338
|
-
throw new Error("Circular reference in SQL params");
|
|
339
|
-
}
|
|
340
|
-
seen.add(obj);
|
|
341
|
-
const out = {};
|
|
342
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
343
|
-
out[k] = normalizeValue(v, seen, depth + 1);
|
|
344
|
-
}
|
|
345
|
-
seen.delete(obj);
|
|
346
|
-
return out;
|
|
186
|
+
return normalizeObjectValue(value, seen, depth);
|
|
347
187
|
}
|
|
348
188
|
return value;
|
|
349
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
|
+
}
|
|
350
225
|
|
|
351
226
|
// src/sql-builder-dialect.ts
|
|
352
227
|
var globalDialect = "postgres";
|
|
@@ -526,9 +401,158 @@ function prepareArrayParam(value, dialect) {
|
|
|
526
401
|
if (dialect === "postgres") {
|
|
527
402
|
return value.map((v) => normalizeValue(v));
|
|
528
403
|
}
|
|
529
|
-
return JSON.stringify(value);
|
|
404
|
+
return JSON.stringify(value.map((v) => normalizeValue(v)));
|
|
530
405
|
}
|
|
531
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
|
+
|
|
532
556
|
// src/builder/shared/validators/type-guards.ts
|
|
533
557
|
function isNotNullish(value) {
|
|
534
558
|
return value !== null && value !== void 0;
|
|
@@ -744,10 +768,13 @@ function quoteIdent(id) {
|
|
|
744
768
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
745
769
|
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
746
770
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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
|
+
}
|
|
751
778
|
}
|
|
752
779
|
if (needsQuoting(id)) {
|
|
753
780
|
return `"${id.replace(/"/g, '""')}"`;
|
|
@@ -812,7 +839,13 @@ function getQuotedColumn(model, fieldName) {
|
|
|
812
839
|
|
|
813
840
|
// src/builder/shared/sql-utils.ts
|
|
814
841
|
function containsControlChars(s) {
|
|
815
|
-
|
|
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;
|
|
816
849
|
}
|
|
817
850
|
function assertNoControlChars(label, s) {
|
|
818
851
|
if (containsControlChars(s)) {
|
|
@@ -887,20 +920,9 @@ function parseUnquotedPart(input, start) {
|
|
|
887
920
|
}
|
|
888
921
|
return i;
|
|
889
922
|
}
|
|
890
|
-
function
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (trimmed.length === 0) {
|
|
894
|
-
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
895
|
-
}
|
|
896
|
-
if (raw !== trimmed) {
|
|
897
|
-
throw new Error(
|
|
898
|
-
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
899
|
-
);
|
|
900
|
-
}
|
|
901
|
-
assertNoControlChars("tableName/tableRef", trimmed);
|
|
902
|
-
for (let i2 = 0; i2 < trimmed.length; i2++) {
|
|
903
|
-
const c = trimmed.charCodeAt(i2);
|
|
923
|
+
function validateQualifiedNameFormat(trimmed) {
|
|
924
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
925
|
+
const c = trimmed.charCodeAt(i);
|
|
904
926
|
if (c === 9 || c === 11 || c === 12 || c === 32) {
|
|
905
927
|
throw new Error(
|
|
906
928
|
`tableName/tableRef must not contain whitespace: ${JSON.stringify(trimmed)}`
|
|
@@ -917,6 +939,8 @@ function assertSafeQualifiedName(input) {
|
|
|
917
939
|
);
|
|
918
940
|
}
|
|
919
941
|
}
|
|
942
|
+
}
|
|
943
|
+
function parseQualifiedNameParts(trimmed) {
|
|
920
944
|
let i = 0;
|
|
921
945
|
const n = trimmed.length;
|
|
922
946
|
let parts = 0;
|
|
@@ -952,6 +976,21 @@ function assertSafeQualifiedName(input) {
|
|
|
952
976
|
}
|
|
953
977
|
}
|
|
954
978
|
}
|
|
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
|
+
}
|
|
955
994
|
function quote(id) {
|
|
956
995
|
if (isEmptyString(id)) {
|
|
957
996
|
throw new Error("quote: identifier is required and cannot be empty");
|
|
@@ -1041,7 +1080,7 @@ function assertSafeAlias(alias) {
|
|
|
1041
1080
|
if (a !== alias) {
|
|
1042
1081
|
throw new Error("Invalid alias: leading/trailing whitespace");
|
|
1043
1082
|
}
|
|
1044
|
-
if (
|
|
1083
|
+
if (containsControlChars(a)) {
|
|
1045
1084
|
throw new Error(
|
|
1046
1085
|
"Invalid alias: contains unsafe characters (control characters)"
|
|
1047
1086
|
);
|
|
@@ -1062,7 +1101,7 @@ function assertSafeAlias(alias) {
|
|
|
1062
1101
|
"Invalid alias: must be a simple identifier without whitespace"
|
|
1063
1102
|
);
|
|
1064
1103
|
}
|
|
1065
|
-
if (!/^[A-Za-z_]
|
|
1104
|
+
if (!/^[A-Za-z_]\w*$/.test(a)) {
|
|
1066
1105
|
throw new Error(
|
|
1067
1106
|
`Invalid alias: must be a simple identifier (alphanumeric with underscores): "${alias}"`
|
|
1068
1107
|
);
|
|
@@ -1110,8 +1149,7 @@ function isValidRelationField(field) {
|
|
|
1110
1149
|
return false;
|
|
1111
1150
|
const fk = normalizeKeyList(field.foreignKey);
|
|
1112
1151
|
if (fk.length === 0) return false;
|
|
1113
|
-
const
|
|
1114
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1152
|
+
const refs = normalizeKeyList(field.references);
|
|
1115
1153
|
if (refs.length === 0) {
|
|
1116
1154
|
return fk.length === 1;
|
|
1117
1155
|
}
|
|
@@ -1119,8 +1157,7 @@ function isValidRelationField(field) {
|
|
|
1119
1157
|
return true;
|
|
1120
1158
|
}
|
|
1121
1159
|
function getReferenceFieldNames(field, foreignKeyCount) {
|
|
1122
|
-
const
|
|
1123
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1160
|
+
const refs = normalizeKeyList(field.references);
|
|
1124
1161
|
if (refs.length === 0) {
|
|
1125
1162
|
if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
|
|
1126
1163
|
return [];
|
|
@@ -1281,29 +1318,35 @@ function normalizeOrderByInput(orderBy, parseValue) {
|
|
|
1281
1318
|
}
|
|
1282
1319
|
|
|
1283
1320
|
// src/builder/shared/order-by-determinism.ts
|
|
1284
|
-
function
|
|
1285
|
-
if (!model) return
|
|
1286
|
-
|
|
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;
|
|
1287
1329
|
}
|
|
1288
|
-
function
|
|
1330
|
+
function hasTiebreaker(orderBy, parse, field) {
|
|
1289
1331
|
if (!isNotNullish(orderBy)) return false;
|
|
1290
1332
|
const normalized = normalizeOrderByInput(orderBy, parse);
|
|
1291
1333
|
return normalized.some(
|
|
1292
|
-
(obj) => Object.prototype.hasOwnProperty.call(obj,
|
|
1334
|
+
(obj) => Object.prototype.hasOwnProperty.call(obj, field)
|
|
1293
1335
|
);
|
|
1294
1336
|
}
|
|
1295
|
-
function
|
|
1296
|
-
if (Array.isArray(orderBy)) return [...orderBy, {
|
|
1297
|
-
return [orderBy, {
|
|
1337
|
+
function addTiebreaker(orderBy, field) {
|
|
1338
|
+
if (Array.isArray(orderBy)) return [...orderBy, { [field]: "asc" }];
|
|
1339
|
+
return [orderBy, { [field]: "asc" }];
|
|
1298
1340
|
}
|
|
1299
1341
|
function ensureDeterministicOrderByInput(args) {
|
|
1300
1342
|
const { orderBy, model, parseValue } = args;
|
|
1301
|
-
|
|
1343
|
+
const tiebreaker = findTiebreakerField(model);
|
|
1344
|
+
if (!tiebreaker) return orderBy;
|
|
1302
1345
|
if (!isNotNullish(orderBy)) {
|
|
1303
|
-
return {
|
|
1346
|
+
return { [tiebreaker]: "asc" };
|
|
1304
1347
|
}
|
|
1305
|
-
if (
|
|
1306
|
-
return
|
|
1348
|
+
if (hasTiebreaker(orderBy, parseValue, tiebreaker)) return orderBy;
|
|
1349
|
+
return addTiebreaker(orderBy, tiebreaker);
|
|
1307
1350
|
}
|
|
1308
1351
|
|
|
1309
1352
|
// src/builder/shared/validators/field-assertions.ts
|
|
@@ -1450,11 +1493,9 @@ function defaultNullsFor(dialect, direction) {
|
|
|
1450
1493
|
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1451
1494
|
if (cursorEntries.length === 0) return orderEntries;
|
|
1452
1495
|
const existing = /* @__PURE__ */ new Set();
|
|
1453
|
-
for (
|
|
1454
|
-
existing.add(orderEntries[i].field);
|
|
1496
|
+
for (const entry of orderEntries) existing.add(entry.field);
|
|
1455
1497
|
let out = null;
|
|
1456
|
-
for (
|
|
1457
|
-
const field = cursorEntries[i][0];
|
|
1498
|
+
for (const [field] of cursorEntries) {
|
|
1458
1499
|
if (!existing.has(field)) {
|
|
1459
1500
|
if (!out) out = orderEntries.slice();
|
|
1460
1501
|
out.push({ field, direction: "asc" });
|
|
@@ -1694,7 +1735,44 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
|
1694
1735
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1695
1736
|
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1696
1737
|
}
|
|
1697
|
-
function
|
|
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 });
|
|
1746
|
+
}
|
|
1747
|
+
if ((op === Ops.IN || op === Ops.NOT_IN) && !isNotNullish(dialect)) {
|
|
1748
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1749
|
+
}
|
|
1750
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1751
|
+
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1752
|
+
operator: op
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
function routeOperatorHandler(expr, op, val, params, mode, dialect) {
|
|
1757
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1758
|
+
Ops.CONTAINS,
|
|
1759
|
+
Ops.STARTS_WITH,
|
|
1760
|
+
Ops.ENDS_WITH
|
|
1761
|
+
]);
|
|
1762
|
+
if (STRING_LIKE_OPS.has(op)) {
|
|
1763
|
+
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1764
|
+
}
|
|
1765
|
+
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1766
|
+
return handleInOperator(expr, op, val, params, dialect);
|
|
1767
|
+
}
|
|
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;
|
|
1698
1776
|
if (val === void 0) return "";
|
|
1699
1777
|
if (depth > MAX_NOT_DEPTH) {
|
|
1700
1778
|
throw new Error(
|
|
@@ -1711,33 +1789,17 @@ function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect, de
|
|
|
1711
1789
|
const placeholder = params.addAuto(val);
|
|
1712
1790
|
return `${expr} <> ${placeholder}`;
|
|
1713
1791
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
if (
|
|
1724
|
-
|
|
1725
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1726
|
-
operator: op
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1730
|
-
}
|
|
1731
|
-
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1732
|
-
if (!isNotNullish(dialect)) {
|
|
1733
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1734
|
-
}
|
|
1735
|
-
return handleInOperator(expr, op, val, params, dialect);
|
|
1736
|
-
}
|
|
1737
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1738
|
-
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1739
|
-
operator: op
|
|
1740
|
-
});
|
|
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;
|
|
1741
1803
|
}
|
|
1742
1804
|
return handleComparisonOperator(expr, op, val, params);
|
|
1743
1805
|
}
|
|
@@ -1761,38 +1823,32 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, dep
|
|
|
1761
1823
|
if (!isNotNullish(dialect)) {
|
|
1762
1824
|
const clauses = [];
|
|
1763
1825
|
for (const [subOp, subVal] of entries) {
|
|
1764
|
-
const sub = buildScalarOperator(
|
|
1765
|
-
|
|
1766
|
-
subOp,
|
|
1767
|
-
subVal,
|
|
1768
|
-
params,
|
|
1769
|
-
effectiveMode,
|
|
1826
|
+
const sub = buildScalarOperator(expr, subOp, subVal, params, {
|
|
1827
|
+
mode: effectiveMode,
|
|
1770
1828
|
fieldType,
|
|
1771
|
-
void 0,
|
|
1772
|
-
depth + 1
|
|
1773
|
-
);
|
|
1829
|
+
dialect: void 0,
|
|
1830
|
+
depth: depth + 1
|
|
1831
|
+
});
|
|
1774
1832
|
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1775
1833
|
}
|
|
1776
1834
|
if (clauses.length === 0) return "";
|
|
1777
1835
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1778
|
-
|
|
1836
|
+
const separator2 = ` ${SQL_TEMPLATES.AND} `;
|
|
1837
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator2)})`;
|
|
1779
1838
|
}
|
|
1839
|
+
const separator = ` ${SQL_TEMPLATES.AND} `;
|
|
1780
1840
|
return buildNotComposite(
|
|
1781
1841
|
expr,
|
|
1782
1842
|
val,
|
|
1783
1843
|
params,
|
|
1784
1844
|
dialect,
|
|
1785
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(
|
|
1786
|
-
|
|
1787
|
-
subOp,
|
|
1788
|
-
subVal,
|
|
1789
|
-
p,
|
|
1790
|
-
effectiveMode,
|
|
1845
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, {
|
|
1846
|
+
mode: effectiveMode,
|
|
1791
1847
|
fieldType,
|
|
1792
|
-
d,
|
|
1793
|
-
depth + 1
|
|
1794
|
-
),
|
|
1795
|
-
|
|
1848
|
+
dialect: d,
|
|
1849
|
+
depth: depth + 1
|
|
1850
|
+
}),
|
|
1851
|
+
separator
|
|
1796
1852
|
);
|
|
1797
1853
|
}
|
|
1798
1854
|
function buildDynamicLikePattern(op, placeholder) {
|
|
@@ -1989,24 +2045,42 @@ function handleArrayIsEmpty(expr, val, dialect) {
|
|
|
1989
2045
|
// src/builder/where/operators-json.ts
|
|
1990
2046
|
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1991
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
|
+
}
|
|
1992
2061
|
function validateJsonPathSegments(segments) {
|
|
1993
|
-
|
|
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];
|
|
1994
2069
|
if (typeof segment !== "string") {
|
|
1995
|
-
throw createError(
|
|
1996
|
-
operator: Ops.PATH
|
|
1997
|
-
value: segment
|
|
2070
|
+
throw createError(`JSON path segment at index ${i} must be string`, {
|
|
2071
|
+
operator: Ops.PATH
|
|
1998
2072
|
});
|
|
1999
2073
|
}
|
|
2000
2074
|
if (segment.length > MAX_PATH_SEGMENT_LENGTH) {
|
|
2001
2075
|
throw createError(
|
|
2002
|
-
`JSON path segment too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
2003
|
-
{ operator: Ops.PATH
|
|
2076
|
+
`JSON path segment at index ${i} too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
2077
|
+
{ operator: Ops.PATH }
|
|
2004
2078
|
);
|
|
2005
2079
|
}
|
|
2006
2080
|
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
2007
2081
|
throw createError(
|
|
2008
|
-
`Invalid JSON path segment: '${segment}'
|
|
2009
|
-
{ operator: Ops.PATH
|
|
2082
|
+
`Invalid JSON path segment at index ${i}: '${sanitizeForError(segment)}'`,
|
|
2083
|
+
{ operator: Ops.PATH }
|
|
2010
2084
|
);
|
|
2011
2085
|
}
|
|
2012
2086
|
}
|
|
@@ -2124,6 +2198,35 @@ function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
|
|
|
2124
2198
|
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2125
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})`;
|
|
2126
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
|
+
}
|
|
2127
2230
|
function buildListRelationFilters(args) {
|
|
2128
2231
|
const {
|
|
2129
2232
|
fieldName,
|
|
@@ -2144,21 +2247,16 @@ function buildListRelationFilters(args) {
|
|
|
2144
2247
|
isSubquery: true,
|
|
2145
2248
|
depth: ctx.depth + 1
|
|
2146
2249
|
}));
|
|
2147
|
-
const
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
clause: whereClause,
|
|
2158
|
-
joins: freezeJoins([leftJoinSql])
|
|
2159
|
-
});
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2250
|
+
const optimized = tryOptimizeNoneFilter(
|
|
2251
|
+
noneValue,
|
|
2252
|
+
ctx,
|
|
2253
|
+
relModel,
|
|
2254
|
+
relTable,
|
|
2255
|
+
relAlias,
|
|
2256
|
+
join3,
|
|
2257
|
+
sub
|
|
2258
|
+
);
|
|
2259
|
+
if (optimized) return optimized;
|
|
2162
2260
|
}
|
|
2163
2261
|
const filters = [
|
|
2164
2262
|
{
|
|
@@ -2179,17 +2277,8 @@ function buildListRelationFilters(args) {
|
|
|
2179
2277
|
];
|
|
2180
2278
|
const clauses = [];
|
|
2181
2279
|
for (const { key, wrap } of filters) {
|
|
2182
|
-
const
|
|
2183
|
-
if (
|
|
2184
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2185
|
-
alias: relAlias,
|
|
2186
|
-
model: relModel,
|
|
2187
|
-
path: [...ctx.path, fieldName, key],
|
|
2188
|
-
isSubquery: true,
|
|
2189
|
-
depth: ctx.depth + 1
|
|
2190
|
-
}));
|
|
2191
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2192
|
-
clauses.push(wrap(sub.clause, j));
|
|
2280
|
+
const clause = processRelationFilter(key, wrap, args);
|
|
2281
|
+
if (clause) clauses.push(clause);
|
|
2193
2282
|
}
|
|
2194
2283
|
if (clauses.length === 0) {
|
|
2195
2284
|
throw createError(
|
|
@@ -2591,15 +2680,11 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2591
2680
|
if (fieldType && isJsonType(fieldType) && JSON_OPS.has(op)) {
|
|
2592
2681
|
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2593
2682
|
}
|
|
2594
|
-
return buildScalarOperator(
|
|
2595
|
-
expr,
|
|
2596
|
-
op,
|
|
2597
|
-
val,
|
|
2598
|
-
ctx.params,
|
|
2683
|
+
return buildScalarOperator(expr, op, val, ctx.params, {
|
|
2599
2684
|
mode,
|
|
2600
2685
|
fieldType,
|
|
2601
|
-
ctx.dialect
|
|
2602
|
-
);
|
|
2686
|
+
dialect: ctx.dialect
|
|
2687
|
+
});
|
|
2603
2688
|
}
|
|
2604
2689
|
|
|
2605
2690
|
// src/builder/shared/alias-generator.ts
|
|
@@ -2713,47 +2798,50 @@ function validateState(params, mappings, index) {
|
|
|
2713
2798
|
validateMappings(mappings);
|
|
2714
2799
|
assertNextIndexMatches(mappings.length, index);
|
|
2715
2800
|
}
|
|
2716
|
-
function
|
|
2717
|
-
let index = startIndex;
|
|
2718
|
-
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2719
|
-
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2801
|
+
function buildDynamicNameIndex(mappings) {
|
|
2720
2802
|
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2721
|
-
for (
|
|
2722
|
-
const m = mappings[i];
|
|
2803
|
+
for (const m of mappings) {
|
|
2723
2804
|
if (typeof m.dynamicName === "string") {
|
|
2724
2805
|
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2725
2806
|
}
|
|
2726
2807
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
function format(position) {
|
|
2737
|
-
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
|
+
);
|
|
2738
2815
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
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");
|
|
2745
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;
|
|
2746
2834
|
function addDynamic(dynamicName) {
|
|
2747
|
-
const dn =
|
|
2835
|
+
const dn = validateDynamicName(dynamicName);
|
|
2748
2836
|
const existing = dynamicNameToIndex.get(dn);
|
|
2749
|
-
if (existing !== void 0) return
|
|
2837
|
+
if (existing !== void 0) return formatPosition(existing);
|
|
2750
2838
|
const position = index;
|
|
2751
2839
|
dynamicNameToIndex.set(dn, position);
|
|
2752
2840
|
params.push(void 0);
|
|
2753
2841
|
mappings.push({ index: position, dynamicName: dn });
|
|
2754
2842
|
index++;
|
|
2755
2843
|
dirty = true;
|
|
2756
|
-
return
|
|
2844
|
+
return formatPosition(position);
|
|
2757
2845
|
}
|
|
2758
2846
|
function addStatic(value) {
|
|
2759
2847
|
const position = index;
|
|
@@ -2762,10 +2850,10 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2762
2850
|
mappings.push({ index: position, value: normalizedValue });
|
|
2763
2851
|
index++;
|
|
2764
2852
|
dirty = true;
|
|
2765
|
-
return
|
|
2853
|
+
return formatPosition(position);
|
|
2766
2854
|
}
|
|
2767
2855
|
function add(value, dynamicName) {
|
|
2768
|
-
|
|
2856
|
+
assertCanAddParam(index);
|
|
2769
2857
|
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2770
2858
|
}
|
|
2771
2859
|
function addAuto(value) {
|
|
@@ -2986,6 +3074,17 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
|
|
|
2986
3074
|
var MAX_INCLUDE_DEPTH = 10;
|
|
2987
3075
|
var MAX_TOTAL_SUBQUERIES = 100;
|
|
2988
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
|
+
}
|
|
2989
3088
|
function getRelationTableReference(relModel, dialect) {
|
|
2990
3089
|
return buildTableReference(
|
|
2991
3090
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
@@ -3129,19 +3228,11 @@ function finalizeOrderByForInclude(args) {
|
|
|
3129
3228
|
}
|
|
3130
3229
|
function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
|
|
3131
3230
|
let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
|
|
3132
|
-
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
ctx.
|
|
3136
|
-
|
|
3137
|
-
relAlias,
|
|
3138
|
-
ctx.aliasGen,
|
|
3139
|
-
ctx.params,
|
|
3140
|
-
ctx.dialect,
|
|
3141
|
-
ctx.visitPath || [],
|
|
3142
|
-
(ctx.depth || 0) + 1,
|
|
3143
|
-
ctx.stats
|
|
3144
|
-
) : [];
|
|
3231
|
+
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(relArgs, __spreadProps(__spreadValues({}, ctx), {
|
|
3232
|
+
model: relModel,
|
|
3233
|
+
parentAlias: relAlias,
|
|
3234
|
+
depth: (ctx.depth || 0) + 1
|
|
3235
|
+
})) : [];
|
|
3145
3236
|
if (isNonEmptyArray(nestedIncludes)) {
|
|
3146
3237
|
const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3147
3238
|
const nestedSelects = nestedIncludes.map(
|
|
@@ -3196,6 +3287,7 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3196
3287
|
whereClause: args.whereClause
|
|
3197
3288
|
});
|
|
3198
3289
|
if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3290
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3199
3291
|
if (isNotNullish(args.takeVal)) {
|
|
3200
3292
|
return appendLimitOffset(
|
|
3201
3293
|
sql,
|
|
@@ -3203,15 +3295,10 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3203
3295
|
args.ctx.params,
|
|
3204
3296
|
args.takeVal,
|
|
3205
3297
|
args.skipVal,
|
|
3206
|
-
|
|
3298
|
+
scopeBase
|
|
3207
3299
|
);
|
|
3208
3300
|
}
|
|
3209
|
-
return limitOneSql(
|
|
3210
|
-
sql,
|
|
3211
|
-
args.ctx.params,
|
|
3212
|
-
args.skipVal,
|
|
3213
|
-
`include.${args.relName}`
|
|
3214
|
-
);
|
|
3301
|
+
return limitOneSql(sql, args.ctx.params, args.skipVal, scopeBase);
|
|
3215
3302
|
}
|
|
3216
3303
|
function buildListIncludeSpec(args) {
|
|
3217
3304
|
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
@@ -3239,13 +3326,14 @@ function buildListIncludeSpec(args) {
|
|
|
3239
3326
|
whereClause: args.whereClause
|
|
3240
3327
|
});
|
|
3241
3328
|
if (args.orderBySql) base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3329
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3242
3330
|
base = appendLimitOffset(
|
|
3243
3331
|
base,
|
|
3244
3332
|
args.ctx.dialect,
|
|
3245
3333
|
args.ctx.params,
|
|
3246
3334
|
args.takeVal,
|
|
3247
3335
|
args.skipVal,
|
|
3248
|
-
|
|
3336
|
+
scopeBase
|
|
3249
3337
|
);
|
|
3250
3338
|
const selectExpr = jsonAgg("row", args.ctx.dialect);
|
|
3251
3339
|
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
@@ -3294,7 +3382,6 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3294
3382
|
);
|
|
3295
3383
|
if (!isList) {
|
|
3296
3384
|
const sql = buildOneToOneIncludeSql({
|
|
3297
|
-
relName,
|
|
3298
3385
|
relTable,
|
|
3299
3386
|
relAlias,
|
|
3300
3387
|
joins: whereParts.joins,
|
|
@@ -3322,8 +3409,14 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3322
3409
|
ctx
|
|
3323
3410
|
});
|
|
3324
3411
|
}
|
|
3325
|
-
function buildIncludeSqlInternal(args,
|
|
3326
|
-
|
|
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 || [];
|
|
3327
3420
|
if (depth > MAX_INCLUDE_DEPTH) {
|
|
3328
3421
|
throw new Error(
|
|
3329
3422
|
`Maximum include depth of ${MAX_INCLUDE_DEPTH} exceeded. Path: ${visitPath.join(" -> ")}. Deep includes cause exponential SQL complexity and performance issues.`
|
|
@@ -3331,7 +3424,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3331
3424
|
}
|
|
3332
3425
|
stats.maxDepth = Math.max(stats.maxDepth, depth);
|
|
3333
3426
|
const includes = [];
|
|
3334
|
-
const entries = relationEntriesFromArgs(args, model);
|
|
3427
|
+
const entries = relationEntriesFromArgs(args, ctx.model);
|
|
3335
3428
|
for (const [relName, relArgs] of entries) {
|
|
3336
3429
|
if (relArgs === false) continue;
|
|
3337
3430
|
stats.totalIncludes++;
|
|
@@ -3346,8 +3439,12 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3346
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.`
|
|
3347
3440
|
);
|
|
3348
3441
|
}
|
|
3349
|
-
const resolved = resolveRelationOrThrow(
|
|
3350
|
-
|
|
3442
|
+
const resolved = resolveRelationOrThrow(
|
|
3443
|
+
ctx.model,
|
|
3444
|
+
ctx.schemaByName,
|
|
3445
|
+
relName
|
|
3446
|
+
);
|
|
3447
|
+
const relationPath = `${ctx.model.name}.${relName}`;
|
|
3351
3448
|
const currentPath = [...visitPath, relationPath];
|
|
3352
3449
|
if (visitPath.includes(relationPath)) {
|
|
3353
3450
|
throw new Error(
|
|
@@ -3362,19 +3459,14 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3362
3459
|
`Include too deeply nested: model '${resolved.relModel.name}' appears ${modelOccurrences} times in path: ${currentPath.join(" -> ")}`
|
|
3363
3460
|
);
|
|
3364
3461
|
}
|
|
3462
|
+
const nextIncludePath = [...ctx.includePath, relName];
|
|
3365
3463
|
includes.push(
|
|
3366
|
-
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, {
|
|
3367
|
-
|
|
3368
|
-
schemas,
|
|
3369
|
-
schemaByName,
|
|
3370
|
-
parentAlias,
|
|
3371
|
-
aliasGen,
|
|
3372
|
-
dialect,
|
|
3373
|
-
params,
|
|
3464
|
+
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
|
|
3465
|
+
includePath: nextIncludePath,
|
|
3374
3466
|
visitPath: currentPath,
|
|
3375
3467
|
depth: depth + 1,
|
|
3376
3468
|
stats
|
|
3377
|
-
})
|
|
3469
|
+
}))
|
|
3378
3470
|
);
|
|
3379
3471
|
}
|
|
3380
3472
|
return includes;
|
|
@@ -3388,8 +3480,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3388
3480
|
};
|
|
3389
3481
|
const schemaByName = /* @__PURE__ */ new Map();
|
|
3390
3482
|
for (const m of schemas) schemaByName.set(m.name, m);
|
|
3391
|
-
return buildIncludeSqlInternal(
|
|
3392
|
-
args,
|
|
3483
|
+
return buildIncludeSqlInternal(args, {
|
|
3393
3484
|
model,
|
|
3394
3485
|
schemas,
|
|
3395
3486
|
schemaByName,
|
|
@@ -3397,10 +3488,11 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3397
3488
|
aliasGen,
|
|
3398
3489
|
params,
|
|
3399
3490
|
dialect,
|
|
3400
|
-
[],
|
|
3401
|
-
|
|
3491
|
+
includePath: [],
|
|
3492
|
+
visitPath: [],
|
|
3493
|
+
depth: 0,
|
|
3402
3494
|
stats
|
|
3403
|
-
);
|
|
3495
|
+
});
|
|
3404
3496
|
}
|
|
3405
3497
|
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
3406
3498
|
const relationSet = getRelationFieldSet(model);
|
|
@@ -3445,8 +3537,7 @@ function resolveCountKeyPairs(field) {
|
|
|
3445
3537
|
if (fkFields.length === 0) {
|
|
3446
3538
|
throw new Error("Relation count requires foreignKey");
|
|
3447
3539
|
}
|
|
3448
|
-
const
|
|
3449
|
-
const refs = normalizeKeyList(refsRaw);
|
|
3540
|
+
const refs = normalizeKeyList(field.references);
|
|
3450
3541
|
const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
|
|
3451
3542
|
if (refFields.length !== fkFields.length) {
|
|
3452
3543
|
throw new Error(
|
|
@@ -3581,133 +3672,140 @@ function finalizeSql(sql, params, dialect) {
|
|
|
3581
3672
|
paramMappings: snapshot.mappings
|
|
3582
3673
|
};
|
|
3583
3674
|
}
|
|
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;
|
|
3696
|
+
i++;
|
|
3697
|
+
continue;
|
|
3698
|
+
}
|
|
3699
|
+
const next = i + 1;
|
|
3700
|
+
if (next < n && s.charCodeAt(next) === 34) {
|
|
3701
|
+
out += '"';
|
|
3702
|
+
saw = true;
|
|
3703
|
+
i += 2;
|
|
3704
|
+
continue;
|
|
3705
|
+
}
|
|
3706
|
+
if (!saw) {
|
|
3707
|
+
throw new Error(
|
|
3708
|
+
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3709
|
+
);
|
|
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;
|
|
3723
|
+
i++;
|
|
3724
|
+
}
|
|
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;
|
|
3796
|
+
}
|
|
3584
3797
|
function parseSimpleScalarSelect(select, fromAlias) {
|
|
3585
3798
|
const raw = select.trim();
|
|
3586
3799
|
if (raw.length === 0) return [];
|
|
3587
|
-
const fromLower = fromAlias.toLowerCase();
|
|
3588
3800
|
const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
|
|
3589
3801
|
const names = [];
|
|
3590
|
-
const
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
if (s.charCodeAt(start) === 34) {
|
|
3595
|
-
let i2 = start + 1;
|
|
3596
|
-
let out = "";
|
|
3597
|
-
let saw = false;
|
|
3598
|
-
while (i2 < n) {
|
|
3599
|
-
const c = s.charCodeAt(i2);
|
|
3600
|
-
if (c === 34) {
|
|
3601
|
-
const next = i2 + 1;
|
|
3602
|
-
if (next < n && s.charCodeAt(next) === 34) {
|
|
3603
|
-
out += '"';
|
|
3604
|
-
saw = true;
|
|
3605
|
-
i2 += 2;
|
|
3606
|
-
continue;
|
|
3607
|
-
}
|
|
3608
|
-
if (!saw)
|
|
3609
|
-
throw new Error(
|
|
3610
|
-
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3611
|
-
);
|
|
3612
|
-
return { text: out, next: i2 + 1, quoted: true };
|
|
3613
|
-
}
|
|
3614
|
-
out += s[i2];
|
|
3615
|
-
saw = true;
|
|
3616
|
-
i2++;
|
|
3617
|
-
}
|
|
3618
|
-
throw new Error(
|
|
3619
|
-
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3620
|
-
);
|
|
3621
|
-
}
|
|
3622
|
-
let i = start;
|
|
3623
|
-
while (i < n) {
|
|
3624
|
-
const c = s.charCodeAt(i);
|
|
3625
|
-
if (c === 32 || c === 9) break;
|
|
3626
|
-
if (c === 46) break;
|
|
3627
|
-
i++;
|
|
3628
|
-
}
|
|
3629
|
-
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3630
|
-
};
|
|
3631
|
-
const skipSpaces = (s, i) => {
|
|
3632
|
-
while (i < s.length) {
|
|
3633
|
-
const c = s.charCodeAt(i);
|
|
3634
|
-
if (c !== 32 && c !== 9) break;
|
|
3635
|
-
i++;
|
|
3636
|
-
}
|
|
3637
|
-
return i;
|
|
3638
|
-
};
|
|
3639
|
-
for (let idx = 0; idx < parts.length; idx++) {
|
|
3640
|
-
const p = parts[idx].trim();
|
|
3641
|
-
if (p.length === 0) continue;
|
|
3642
|
-
let i = 0;
|
|
3643
|
-
i = skipSpaces(p, i);
|
|
3644
|
-
const a = readIdentOrQuoted(p, i);
|
|
3645
|
-
const actualAlias = a.text.toLowerCase();
|
|
3646
|
-
if (!isIdent(a.text)) {
|
|
3647
|
-
throw new Error(
|
|
3648
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3649
|
-
);
|
|
3650
|
-
}
|
|
3651
|
-
if (actualAlias !== fromLower) {
|
|
3652
|
-
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3653
|
-
}
|
|
3654
|
-
i = a.next;
|
|
3655
|
-
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3656
|
-
throw new Error(
|
|
3657
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3658
|
-
);
|
|
3659
|
-
}
|
|
3660
|
-
i++;
|
|
3661
|
-
i = skipSpaces(p, i);
|
|
3662
|
-
const colPart = readIdentOrQuoted(p, i);
|
|
3663
|
-
const columnName = colPart.text.trim();
|
|
3664
|
-
if (columnName.length === 0) {
|
|
3665
|
-
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3666
|
-
}
|
|
3667
|
-
i = colPart.next;
|
|
3668
|
-
i = skipSpaces(p, i);
|
|
3669
|
-
let outAlias = "";
|
|
3670
|
-
if (i < p.length) {
|
|
3671
|
-
const rest = p.slice(i).trim();
|
|
3672
|
-
if (rest.length > 0) {
|
|
3673
|
-
const m = rest.match(/^AS\s+/i);
|
|
3674
|
-
if (!m) {
|
|
3675
|
-
throw new Error(
|
|
3676
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3677
|
-
);
|
|
3678
|
-
}
|
|
3679
|
-
let j = i;
|
|
3680
|
-
j = skipSpaces(p, j);
|
|
3681
|
-
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3682
|
-
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3683
|
-
}
|
|
3684
|
-
j += 2;
|
|
3685
|
-
j = skipSpaces(p, j);
|
|
3686
|
-
const out = readIdentOrQuoted(p, j);
|
|
3687
|
-
outAlias = out.text.trim();
|
|
3688
|
-
if (outAlias.length === 0) {
|
|
3689
|
-
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3690
|
-
}
|
|
3691
|
-
j = skipSpaces(p, out.next);
|
|
3692
|
-
if (j !== p.length) {
|
|
3693
|
-
throw new Error(
|
|
3694
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3695
|
-
);
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
}
|
|
3699
|
-
const name = outAlias.length > 0 ? outAlias : columnName;
|
|
3700
|
-
names.push(name);
|
|
3802
|
+
for (const p of parts) {
|
|
3803
|
+
const trimmed = p.trim();
|
|
3804
|
+
if (trimmed.length === 0) continue;
|
|
3805
|
+
names.push(parseSelectField(trimmed, fromAlias));
|
|
3701
3806
|
}
|
|
3702
3807
|
return names;
|
|
3703
3808
|
}
|
|
3704
|
-
function replaceOrderByAlias(orderBy, fromAlias, outerAlias) {
|
|
3705
|
-
const src = String(fromAlias);
|
|
3706
|
-
if (src.length === 0) return orderBy;
|
|
3707
|
-
const escaped = src.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3708
|
-
const re = new RegExp(`\\b${escaped}\\.`, "gi");
|
|
3709
|
-
return orderBy.replace(re, outerAlias + ".");
|
|
3710
|
-
}
|
|
3711
3809
|
function buildDistinctColumns(distinct, fromAlias, model) {
|
|
3712
3810
|
return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3713
3811
|
}
|
|
@@ -3730,6 +3828,52 @@ function buildWindowOrder(args) {
|
|
|
3730
3828
|
const idTiebreaker = idField ? ", " + col(fromAlias, "id", model) + " ASC" : "";
|
|
3731
3829
|
return baseOrder + idTiebreaker;
|
|
3732
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
|
+
}
|
|
3733
3877
|
function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
3734
3878
|
var _a, _b;
|
|
3735
3879
|
const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
|
|
@@ -3756,7 +3900,8 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3756
3900
|
fromAlias: from.alias,
|
|
3757
3901
|
model
|
|
3758
3902
|
});
|
|
3759
|
-
const
|
|
3903
|
+
const outerEntries = extractDistinctOrderEntries(spec);
|
|
3904
|
+
const outerOrder = buildFieldNameOrderBy(outerEntries, '"__tp_distinct"');
|
|
3760
3905
|
const joins = buildJoinsSql(whereJoins, countJoins);
|
|
3761
3906
|
const conditions = [];
|
|
3762
3907
|
if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
|
|
@@ -3791,17 +3936,33 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3791
3936
|
}
|
|
3792
3937
|
return outerParts.join(" ");
|
|
3793
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
|
+
}
|
|
3794
3954
|
function buildIncludeColumns(spec) {
|
|
3795
3955
|
var _a, _b;
|
|
3796
3956
|
const { select, includes, dialect, model, schemas, from, params } = spec;
|
|
3797
3957
|
const baseSelect = (select != null ? select : "").trim();
|
|
3798
3958
|
let countCols = "";
|
|
3799
3959
|
let countJoins = [];
|
|
3800
|
-
const
|
|
3801
|
-
if (
|
|
3802
|
-
|
|
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) {
|
|
3803
3964
|
const countBuild = buildRelationCountSql(
|
|
3804
|
-
|
|
3965
|
+
resolvedCountSelect,
|
|
3805
3966
|
model,
|
|
3806
3967
|
schemas,
|
|
3807
3968
|
from.alias,
|
|
@@ -4626,32 +4787,32 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4626
4787
|
paramMappings: allMappings
|
|
4627
4788
|
};
|
|
4628
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
|
+
}
|
|
4629
4812
|
function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
4630
4813
|
assertSafeAlias(alias);
|
|
4631
4814
|
assertSafeTableRef(tableName);
|
|
4632
|
-
|
|
4633
|
-
if (schemaParser.isDynamicParameter(skip)) {
|
|
4634
|
-
throw new Error(
|
|
4635
|
-
"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."
|
|
4636
|
-
);
|
|
4637
|
-
}
|
|
4638
|
-
if (typeof skip === "string") {
|
|
4639
|
-
const s = skip.trim();
|
|
4640
|
-
if (s.length > 0) {
|
|
4641
|
-
const n = Number(s);
|
|
4642
|
-
if (Number.isFinite(n) && Number.isInteger(n) && n > 0) {
|
|
4643
|
-
throw new Error(
|
|
4644
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4645
|
-
);
|
|
4646
|
-
}
|
|
4647
|
-
}
|
|
4648
|
-
}
|
|
4649
|
-
if (typeof skip === "number" && Number.isFinite(skip) && Number.isInteger(skip) && skip > 0) {
|
|
4650
|
-
throw new Error(
|
|
4651
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4652
|
-
);
|
|
4653
|
-
}
|
|
4654
|
-
}
|
|
4815
|
+
validateSkipParameter(skip);
|
|
4655
4816
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4656
4817
|
const parts = [
|
|
4657
4818
|
SQL_TEMPLATES.SELECT,
|
|
@@ -4875,6 +5036,257 @@ function generateSQL(directive) {
|
|
|
4875
5036
|
dialect
|
|
4876
5037
|
});
|
|
4877
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
|
|
4878
5290
|
function generateSQL2(directive) {
|
|
4879
5291
|
return generateSQL(directive);
|
|
4880
5292
|
}
|
|
@@ -4903,6 +5315,57 @@ function extractEnumMappings(datamodel) {
|
|
|
4903
5315
|
}
|
|
4904
5316
|
return { mappings, fieldTypes };
|
|
4905
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
|
+
}
|
|
4906
5369
|
function generateClient(options) {
|
|
4907
5370
|
return __async(this, null, function* () {
|
|
4908
5371
|
const { datamodel, outputDir, config } = options;
|
|
@@ -4915,48 +5378,22 @@ function generateClient(options) {
|
|
|
4915
5378
|
skipInvalid: config.skipInvalid
|
|
4916
5379
|
}
|
|
4917
5380
|
);
|
|
4918
|
-
const queries
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
queries.set(modelName, /* @__PURE__ */ new Map());
|
|
4923
|
-
}
|
|
4924
|
-
const modelQueries = queries.get(modelName);
|
|
4925
|
-
for (const directive of result.directives) {
|
|
4926
|
-
try {
|
|
4927
|
-
const method = directive.method;
|
|
4928
|
-
const sqlDirective = generateSQL2(directive);
|
|
4929
|
-
if (!modelQueries.has(method)) {
|
|
4930
|
-
modelQueries.set(method, /* @__PURE__ */ new Map());
|
|
4931
|
-
}
|
|
4932
|
-
const methodQueriesMap = modelQueries.get(method);
|
|
4933
|
-
const queryKey = createQueryKey(directive.query.processed);
|
|
4934
|
-
methodQueriesMap.set(queryKey, {
|
|
4935
|
-
sql: sqlDirective.sql,
|
|
4936
|
-
params: sqlDirective.staticParams,
|
|
4937
|
-
dynamicKeys: sqlDirective.dynamicKeys,
|
|
4938
|
-
paramMappings: sqlDirective.paramMappings
|
|
4939
|
-
});
|
|
4940
|
-
} catch (error) {
|
|
4941
|
-
if (!config.skipInvalid) throw error;
|
|
4942
|
-
}
|
|
4943
|
-
}
|
|
4944
|
-
}
|
|
5381
|
+
const { queries, skippedCount } = processAllModelDirectives(
|
|
5382
|
+
directiveResults,
|
|
5383
|
+
config
|
|
5384
|
+
);
|
|
4945
5385
|
const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
|
|
4946
5386
|
yield promises.mkdir(absoluteOutputDir, { recursive: true });
|
|
4947
5387
|
const code = generateCode(models, queries, config.dialect, datamodel);
|
|
4948
5388
|
const outputPath = path.join(absoluteOutputDir, "index.ts");
|
|
4949
5389
|
yield promises.writeFile(outputPath, code);
|
|
4950
|
-
const totalQueries =
|
|
4951
|
-
(sum, methodMap) => sum + Array.from(methodMap.values()).reduce(
|
|
4952
|
-
(s, queryMap) => s + queryMap.size,
|
|
4953
|
-
0
|
|
4954
|
-
),
|
|
4955
|
-
0
|
|
4956
|
-
);
|
|
5390
|
+
const totalQueries = countTotalQueries(queries);
|
|
4957
5391
|
console.log(
|
|
4958
5392
|
`\u2713 Generated ${queries.size} models, ${totalQueries} prebaked queries`
|
|
4959
5393
|
);
|
|
5394
|
+
if (skippedCount > 0) {
|
|
5395
|
+
console.log(`\u26A0 Skipped ${skippedCount} directive(s) due to errors`);
|
|
5396
|
+
}
|
|
4960
5397
|
console.log(`\u2713 Output: ${outputPath}`);
|
|
4961
5398
|
});
|
|
4962
5399
|
}
|
|
@@ -4972,16 +5409,13 @@ function createQueryKey(processedQuery) {
|
|
|
4972
5409
|
return value;
|
|
4973
5410
|
});
|
|
4974
5411
|
}
|
|
4975
|
-
function
|
|
4976
|
-
const cleanModels = models.map((model) => __spreadProps(__spreadValues({}, model), {
|
|
4977
|
-
fields: model.fields.filter((f) => f !== void 0 && f !== null)
|
|
4978
|
-
}));
|
|
4979
|
-
const { mappings, fieldTypes } = extractEnumMappings(datamodel);
|
|
5412
|
+
function generateImports() {
|
|
4980
5413
|
return `// Generated by @prisma-sql/generator - DO NOT EDIT
|
|
4981
5414
|
import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type BatchCountQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
|
|
4982
|
-
import { normalizeValue } from 'prisma-sql/utils/normalize-value'
|
|
4983
|
-
|
|
4984
|
-
|
|
5415
|
+
import { normalizeValue } from 'prisma-sql/utils/normalize-value'`;
|
|
5416
|
+
}
|
|
5417
|
+
function generateCoreTypes() {
|
|
5418
|
+
return `class DeferredQuery {
|
|
4985
5419
|
constructor(
|
|
4986
5420
|
public readonly model: string,
|
|
4987
5421
|
public readonly method: PrismaMethod,
|
|
@@ -5013,9 +5447,10 @@ const ACCELERATED_METHODS = new Set<PrismaMethod>([
|
|
|
5013
5447
|
'count',
|
|
5014
5448
|
'aggregate',
|
|
5015
5449
|
'groupBy',
|
|
5016
|
-
])
|
|
5017
|
-
|
|
5018
|
-
function
|
|
5450
|
+
])`;
|
|
5451
|
+
}
|
|
5452
|
+
function generateHelpers() {
|
|
5453
|
+
return `function createBatchProxy(): BatchProxy {
|
|
5019
5454
|
return new Proxy(
|
|
5020
5455
|
{},
|
|
5021
5456
|
{
|
|
@@ -5066,7 +5501,68 @@ function getByPath(obj: any, path: string): unknown {
|
|
|
5066
5501
|
return result
|
|
5067
5502
|
}
|
|
5068
5503
|
|
|
5069
|
-
|
|
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)}
|
|
5070
5566
|
|
|
5071
5567
|
const ENUM_MAPPINGS: Record<string, Record<string, string>> = ${JSON.stringify(mappings, null, 2)}
|
|
5072
5568
|
|
|
@@ -5081,12 +5577,19 @@ const QUERIES: Record<string, Record<string, Record<string, {
|
|
|
5081
5577
|
|
|
5082
5578
|
const DIALECT = ${JSON.stringify(dialect)}
|
|
5083
5579
|
|
|
5084
|
-
const MODEL_MAP = new Map(MODELS.map(m => [m.name, m]))
|
|
5085
|
-
|
|
5086
|
-
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 {
|
|
5087
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
|
|
5088
5588
|
if (path.includes('where')) return false
|
|
5089
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
|
|
5090
5593
|
return true
|
|
5091
5594
|
}
|
|
5092
5595
|
|
|
@@ -5113,6 +5616,14 @@ function transformEnumInValue(value: unknown, enumType: string | undefined): unk
|
|
|
5113
5616
|
return mapping[value]
|
|
5114
5617
|
}
|
|
5115
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
|
+
|
|
5116
5627
|
return value
|
|
5117
5628
|
}
|
|
5118
5629
|
|
|
@@ -5202,21 +5713,7 @@ function normalizeQuery(args: any): string {
|
|
|
5202
5713
|
|
|
5203
5714
|
const withMarkers = replaceDynamicParams(normalized)
|
|
5204
5715
|
|
|
5205
|
-
|
|
5206
|
-
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return obj
|
|
5207
|
-
const result: any = {}
|
|
5208
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
5209
|
-
if (value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) {
|
|
5210
|
-
continue
|
|
5211
|
-
}
|
|
5212
|
-
result[key] = removeEmptyObjects(value)
|
|
5213
|
-
}
|
|
5214
|
-
return result
|
|
5215
|
-
}
|
|
5216
|
-
|
|
5217
|
-
const cleaned = removeEmptyObjects(withMarkers)
|
|
5218
|
-
|
|
5219
|
-
return JSON.stringify(cleaned, (key, value) => {
|
|
5716
|
+
return JSON.stringify(withMarkers, (key, value) => {
|
|
5220
5717
|
if (typeof value === 'bigint') return '__bigint__' + value.toString()
|
|
5221
5718
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
5222
5719
|
const sorted: Record<string, unknown> = {}
|
|
@@ -5227,43 +5724,10 @@ function normalizeQuery(args: any): string {
|
|
|
5227
5724
|
}
|
|
5228
5725
|
return value
|
|
5229
5726
|
})
|
|
5727
|
+
}`;
|
|
5230
5728
|
}
|
|
5231
|
-
|
|
5232
|
-
function
|
|
5233
|
-
const params: unknown[] = []
|
|
5234
|
-
for (const key of dynamicKeys) {
|
|
5235
|
-
const parts = key.split(':')
|
|
5236
|
-
const lookupKey = parts.length === 2 ? parts[1] : key
|
|
5237
|
-
const value =
|
|
5238
|
-
lookupKey.includes('.') ? getByPath(args, lookupKey) : args?.[lookupKey]
|
|
5239
|
-
if (value === undefined) {
|
|
5240
|
-
throw new Error(\`Missing required parameter: \${key}\`)
|
|
5241
|
-
}
|
|
5242
|
-
params.push(normalizeValue(value))
|
|
5243
|
-
}
|
|
5244
|
-
return params
|
|
5245
|
-
}
|
|
5246
|
-
|
|
5247
|
-
async function executeQuery(client: any, sql: string, params: unknown[]): Promise<unknown[]> {
|
|
5248
|
-
const normalizedParams = normalizeParams(params)
|
|
5249
|
-
if (DIALECT === 'postgres') {
|
|
5250
|
-
return await client.unsafe(sql, normalizedParams)
|
|
5251
|
-
}
|
|
5252
|
-
const stmt = client.prepare(sql)
|
|
5253
|
-
if (sql.toUpperCase().includes('COUNT(*) AS')) {
|
|
5254
|
-
return [stmt.get(...normalizedParams)]
|
|
5255
|
-
}
|
|
5256
|
-
return stmt.all(...normalizedParams)
|
|
5257
|
-
}
|
|
5258
|
-
|
|
5259
|
-
async function executeRaw(client: any, sql: string, params?: unknown[]): Promise<unknown[]> {
|
|
5260
|
-
if (DIALECT === 'postgres') {
|
|
5261
|
-
return await client.unsafe(sql, (params || []) as any[])
|
|
5262
|
-
}
|
|
5263
|
-
throw new Error('Raw execution for sqlite not supported in transactions')
|
|
5264
|
-
}
|
|
5265
|
-
|
|
5266
|
-
export function speedExtension(config: {
|
|
5729
|
+
function generateExtension() {
|
|
5730
|
+
return `export function speedExtension(config: {
|
|
5267
5731
|
postgres?: any
|
|
5268
5732
|
sqlite?: any
|
|
5269
5733
|
debug?: boolean
|
|
@@ -5298,49 +5762,56 @@ export function speedExtension(config: {
|
|
|
5298
5762
|
const handleMethod = async function(this: any, method: PrismaMethod, args: any) {
|
|
5299
5763
|
const modelName = this?.name || this?.$name
|
|
5300
5764
|
const startTime = Date.now()
|
|
5301
|
-
const transformedArgs = transformEnumValuesByModel(modelName, args || {})
|
|
5302
|
-
const queryKey = normalizeQuery(transformedArgs)
|
|
5303
|
-
const prebakedQuery = QUERIES[modelName]?.[method]?.[queryKey]
|
|
5304
|
-
let sql: string
|
|
5305
|
-
let params: unknown[]
|
|
5306
|
-
let prebaked = false
|
|
5307
5765
|
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
]
|
|
5314
|
-
prebaked =
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
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[]
|
|
5319
5786
|
}
|
|
5320
|
-
const result = buildSQL(model, MODELS, method, transformedArgs, DIALECT)
|
|
5321
|
-
sql = result.sql
|
|
5322
|
-
params = normalizeParams(result.params as unknown[])
|
|
5323
|
-
}
|
|
5324
5787
|
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
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
|
+
}
|
|
5330
5793
|
|
|
5331
|
-
|
|
5332
|
-
|
|
5794
|
+
const results = await executeQuery(client, method, sql, params)
|
|
5795
|
+
const duration = Date.now() - startTime
|
|
5333
5796
|
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5797
|
+
onQuery?.({
|
|
5798
|
+
model: modelName,
|
|
5799
|
+
method,
|
|
5800
|
+
sql,
|
|
5801
|
+
params,
|
|
5802
|
+
duration,
|
|
5803
|
+
prebaked,
|
|
5804
|
+
})
|
|
5342
5805
|
|
|
5343
|
-
|
|
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)
|
|
5814
|
+
}
|
|
5344
5815
|
}
|
|
5345
5816
|
|
|
5346
5817
|
async function batch<T extends Record<string, DeferredQuery>>(
|
|
@@ -5358,7 +5829,7 @@ export function speedExtension(config: {
|
|
|
5358
5829
|
batchQueries[key] = {
|
|
5359
5830
|
model: deferred.model,
|
|
5360
5831
|
method: deferred.method,
|
|
5361
|
-
args: deferred.args || {},
|
|
5832
|
+
args: transformEnumValuesByModel(deferred.model, deferred.args || {}),
|
|
5362
5833
|
}
|
|
5363
5834
|
}
|
|
5364
5835
|
|
|
@@ -5379,7 +5850,7 @@ export function speedExtension(config: {
|
|
|
5379
5850
|
const normalizedParams = normalizeParams(params)
|
|
5380
5851
|
const rows = await client.unsafe(sql, normalizedParams as any[])
|
|
5381
5852
|
const row = rows[0] as Record<string, unknown>
|
|
5382
|
-
const results = parseBatchResults(row, keys, batchQueries, aliases)
|
|
5853
|
+
const results = parseBatchResults(row, keys, batchQueries, aliases, MODEL_MAP)
|
|
5383
5854
|
|
|
5384
5855
|
const duration = Date.now() - startTime
|
|
5385
5856
|
onQuery?.({
|
|
@@ -5436,7 +5907,11 @@ export function speedExtension(config: {
|
|
|
5436
5907
|
if (debug) {
|
|
5437
5908
|
console.log(\`[\${DIALECT}] $transaction (\${queries.length} queries)\`)
|
|
5438
5909
|
}
|
|
5439
|
-
const
|
|
5910
|
+
const transformedQueries = queries.map(q => ({
|
|
5911
|
+
...q,
|
|
5912
|
+
args: transformEnumValuesByModel(q.model, q.args || {}),
|
|
5913
|
+
}))
|
|
5914
|
+
const results = await txExecutor.execute(transformedQueries, options)
|
|
5440
5915
|
const duration = Date.now() - startTime
|
|
5441
5916
|
onQuery?.({
|
|
5442
5917
|
model: '_transaction',
|
|
@@ -5483,9 +5958,10 @@ export function speedExtension(config: {
|
|
|
5483
5958
|
},
|
|
5484
5959
|
})
|
|
5485
5960
|
}
|
|
5961
|
+
}`;
|
|
5486
5962
|
}
|
|
5487
|
-
|
|
5488
|
-
type SpeedExtensionReturn = ReturnType<ReturnType<typeof speedExtension>>
|
|
5963
|
+
function generateTypeExports() {
|
|
5964
|
+
return `type SpeedExtensionReturn = ReturnType<ReturnType<typeof speedExtension>>
|
|
5489
5965
|
|
|
5490
5966
|
export type SpeedClient<T> = T & {
|
|
5491
5967
|
$batch<T extends Record<string, DeferredQuery>>(
|
|
@@ -5495,8 +5971,22 @@ export type SpeedClient<T> = T & {
|
|
|
5495
5971
|
$transaction(queries: TransactionQuery[], options?: TransactionOptions): Promise<unknown[]>
|
|
5496
5972
|
}
|
|
5497
5973
|
|
|
5498
|
-
export type { BatchCountQuery, TransactionQuery, TransactionOptions }
|
|
5499
|
-
|
|
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");
|
|
5500
5990
|
}
|
|
5501
5991
|
function formatQueries(queries) {
|
|
5502
5992
|
if (queries.size === 0) {
|