prisma-sql 1.58.0 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generator.cjs +1323 -876
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1323 -876
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1612 -1599
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +28 -133
- package/dist/index.d.ts +28 -133
- package/dist/index.js +1684 -1658
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/readme.md +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { convertDMMFToModels, isDynamicParameter, extractDynamicName } from '@dee-wan/schema-parser';
|
|
2
|
-
export { convertDMMFToModels } from '@dee-wan/schema-parser';
|
|
3
2
|
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
5
4
|
var __defProps = Object.defineProperties;
|
|
@@ -7,6 +6,9 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
|
7
6
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
8
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
9
|
+
var __typeError = (msg) => {
|
|
10
|
+
throw TypeError(msg);
|
|
11
|
+
};
|
|
10
12
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
13
|
var __spreadValues = (a, b) => {
|
|
12
14
|
for (var prop in b || (b = {}))
|
|
@@ -20,6 +22,18 @@ var __spreadValues = (a, b) => {
|
|
|
20
22
|
return a;
|
|
21
23
|
};
|
|
22
24
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
26
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
27
|
+
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);
|
|
28
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
29
|
+
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
30
|
+
set _(value) {
|
|
31
|
+
__privateSet(obj, member, value);
|
|
32
|
+
},
|
|
33
|
+
get _() {
|
|
34
|
+
return __privateGet(obj, member, getter);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
23
37
|
var __async = (__this, __arguments, generator) => {
|
|
24
38
|
return new Promise((resolve, reject) => {
|
|
25
39
|
var fulfilled = (value) => {
|
|
@@ -41,154 +55,6 @@ var __async = (__this, __arguments, generator) => {
|
|
|
41
55
|
});
|
|
42
56
|
};
|
|
43
57
|
|
|
44
|
-
// src/builder/shared/constants.ts
|
|
45
|
-
var IS_PRODUCTION = process.env.NODE_ENV === "production";
|
|
46
|
-
var SQL_SEPARATORS = Object.freeze({
|
|
47
|
-
FIELD_LIST: ", ",
|
|
48
|
-
CONDITION_AND: " AND ",
|
|
49
|
-
CONDITION_OR: " OR ",
|
|
50
|
-
ORDER_BY: ", "
|
|
51
|
-
});
|
|
52
|
-
var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
53
|
-
"select",
|
|
54
|
-
"from",
|
|
55
|
-
"where",
|
|
56
|
-
"having",
|
|
57
|
-
"order",
|
|
58
|
-
"group",
|
|
59
|
-
"limit",
|
|
60
|
-
"offset",
|
|
61
|
-
"join",
|
|
62
|
-
"inner",
|
|
63
|
-
"left",
|
|
64
|
-
"right",
|
|
65
|
-
"outer",
|
|
66
|
-
"cross",
|
|
67
|
-
"full",
|
|
68
|
-
"and",
|
|
69
|
-
"or",
|
|
70
|
-
"not",
|
|
71
|
-
"by",
|
|
72
|
-
"as",
|
|
73
|
-
"on",
|
|
74
|
-
"union",
|
|
75
|
-
"intersect",
|
|
76
|
-
"except",
|
|
77
|
-
"case",
|
|
78
|
-
"when",
|
|
79
|
-
"then",
|
|
80
|
-
"else",
|
|
81
|
-
"end"
|
|
82
|
-
]);
|
|
83
|
-
var SQL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
84
|
-
...ALIAS_FORBIDDEN_KEYWORDS,
|
|
85
|
-
"user",
|
|
86
|
-
"users",
|
|
87
|
-
"table",
|
|
88
|
-
"column",
|
|
89
|
-
"index",
|
|
90
|
-
"values",
|
|
91
|
-
"in",
|
|
92
|
-
"like",
|
|
93
|
-
"between",
|
|
94
|
-
"is",
|
|
95
|
-
"exists",
|
|
96
|
-
"null",
|
|
97
|
-
"true",
|
|
98
|
-
"false",
|
|
99
|
-
"all",
|
|
100
|
-
"any",
|
|
101
|
-
"some",
|
|
102
|
-
"update",
|
|
103
|
-
"insert",
|
|
104
|
-
"delete",
|
|
105
|
-
"create",
|
|
106
|
-
"drop",
|
|
107
|
-
"alter",
|
|
108
|
-
"truncate",
|
|
109
|
-
"grant",
|
|
110
|
-
"revoke",
|
|
111
|
-
"exec",
|
|
112
|
-
"execute"
|
|
113
|
-
]);
|
|
114
|
-
var SQL_RESERVED_WORDS = SQL_KEYWORDS;
|
|
115
|
-
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
116
|
-
var SPECIAL_FIELDS = Object.freeze({
|
|
117
|
-
ID: "id"
|
|
118
|
-
});
|
|
119
|
-
var SQL_TEMPLATES = Object.freeze({
|
|
120
|
-
PUBLIC_SCHEMA: "public",
|
|
121
|
-
WHERE: "WHERE",
|
|
122
|
-
SELECT: "SELECT",
|
|
123
|
-
FROM: "FROM",
|
|
124
|
-
ORDER_BY: "ORDER BY",
|
|
125
|
-
GROUP_BY: "GROUP BY",
|
|
126
|
-
HAVING: "HAVING",
|
|
127
|
-
LIMIT: "LIMIT",
|
|
128
|
-
OFFSET: "OFFSET",
|
|
129
|
-
COUNT_ALL: "COUNT(*)",
|
|
130
|
-
AS: "AS",
|
|
131
|
-
DISTINCT_ON: "DISTINCT ON",
|
|
132
|
-
IS_NULL: "IS NULL",
|
|
133
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
134
|
-
LIKE: "LIKE",
|
|
135
|
-
AND: "AND",
|
|
136
|
-
OR: "OR",
|
|
137
|
-
NOT: "NOT"
|
|
138
|
-
});
|
|
139
|
-
var SCHEMA_PREFIXES = Object.freeze({
|
|
140
|
-
INTERNAL: "@",
|
|
141
|
-
COMMENT: "//"
|
|
142
|
-
});
|
|
143
|
-
var Ops = Object.freeze({
|
|
144
|
-
EQUALS: "equals",
|
|
145
|
-
NOT: "not",
|
|
146
|
-
GT: "gt",
|
|
147
|
-
GTE: "gte",
|
|
148
|
-
LT: "lt",
|
|
149
|
-
LTE: "lte",
|
|
150
|
-
IN: "in",
|
|
151
|
-
NOT_IN: "notIn",
|
|
152
|
-
CONTAINS: "contains",
|
|
153
|
-
STARTS_WITH: "startsWith",
|
|
154
|
-
ENDS_WITH: "endsWith",
|
|
155
|
-
HAS: "has",
|
|
156
|
-
HAS_SOME: "hasSome",
|
|
157
|
-
HAS_EVERY: "hasEvery",
|
|
158
|
-
IS_EMPTY: "isEmpty",
|
|
159
|
-
PATH: "path",
|
|
160
|
-
STRING_CONTAINS: "string_contains",
|
|
161
|
-
STRING_STARTS_WITH: "string_starts_with",
|
|
162
|
-
STRING_ENDS_WITH: "string_ends_with"
|
|
163
|
-
});
|
|
164
|
-
var LogicalOps = Object.freeze({
|
|
165
|
-
AND: "AND",
|
|
166
|
-
OR: "OR",
|
|
167
|
-
NOT: "NOT"
|
|
168
|
-
});
|
|
169
|
-
var RelationFilters = Object.freeze({
|
|
170
|
-
SOME: "some",
|
|
171
|
-
EVERY: "every",
|
|
172
|
-
NONE: "none"
|
|
173
|
-
});
|
|
174
|
-
var Modes = Object.freeze({
|
|
175
|
-
INSENSITIVE: "insensitive",
|
|
176
|
-
DEFAULT: "default"
|
|
177
|
-
});
|
|
178
|
-
var Wildcards = Object.freeze({
|
|
179
|
-
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
180
|
-
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
181
|
-
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
182
|
-
});
|
|
183
|
-
var REGEX_CACHE = {
|
|
184
|
-
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
185
|
-
};
|
|
186
|
-
var LIMITS = Object.freeze({
|
|
187
|
-
MAX_QUERY_DEPTH: 50,
|
|
188
|
-
MAX_ARRAY_SIZE: 1e4,
|
|
189
|
-
MAX_STRING_LENGTH: 1e4
|
|
190
|
-
});
|
|
191
|
-
|
|
192
58
|
// src/utils/normalize-value.ts
|
|
193
59
|
var MAX_DEPTH = 20;
|
|
194
60
|
function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
@@ -196,45 +62,54 @@ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0)
|
|
|
196
62
|
throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
|
|
197
63
|
}
|
|
198
64
|
if (value instanceof Date) {
|
|
199
|
-
|
|
200
|
-
if (!Number.isFinite(t)) {
|
|
201
|
-
throw new Error("Invalid Date value in SQL params");
|
|
202
|
-
}
|
|
203
|
-
return value.toISOString();
|
|
65
|
+
return normalizeDateValue(value);
|
|
204
66
|
}
|
|
205
67
|
if (typeof value === "bigint") {
|
|
206
68
|
return value.toString();
|
|
207
69
|
}
|
|
208
70
|
if (Array.isArray(value)) {
|
|
209
|
-
|
|
210
|
-
if (seen.has(arrRef)) {
|
|
211
|
-
throw new Error("Circular reference in SQL params");
|
|
212
|
-
}
|
|
213
|
-
seen.add(arrRef);
|
|
214
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
215
|
-
seen.delete(arrRef);
|
|
216
|
-
return out;
|
|
71
|
+
return normalizeArrayValue(value, seen, depth);
|
|
217
72
|
}
|
|
218
73
|
if (value && typeof value === "object") {
|
|
219
|
-
|
|
220
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
221
|
-
const proto = Object.getPrototypeOf(value);
|
|
222
|
-
const isPlain = proto === Object.prototype || proto === null;
|
|
223
|
-
if (!isPlain) return value;
|
|
224
|
-
const obj = value;
|
|
225
|
-
if (seen.has(obj)) {
|
|
226
|
-
throw new Error("Circular reference in SQL params");
|
|
227
|
-
}
|
|
228
|
-
seen.add(obj);
|
|
229
|
-
const out = {};
|
|
230
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
231
|
-
out[k] = normalizeValue(v, seen, depth + 1);
|
|
232
|
-
}
|
|
233
|
-
seen.delete(obj);
|
|
234
|
-
return out;
|
|
74
|
+
return normalizeObjectValue(value, seen, depth);
|
|
235
75
|
}
|
|
236
76
|
return value;
|
|
237
77
|
}
|
|
78
|
+
function normalizeDateValue(date) {
|
|
79
|
+
const t = date.getTime();
|
|
80
|
+
if (!Number.isFinite(t)) {
|
|
81
|
+
throw new Error("Invalid Date value in SQL params");
|
|
82
|
+
}
|
|
83
|
+
return date.toISOString();
|
|
84
|
+
}
|
|
85
|
+
function normalizeArrayValue(value, seen, depth) {
|
|
86
|
+
const arrRef = value;
|
|
87
|
+
if (seen.has(arrRef)) {
|
|
88
|
+
throw new Error("Circular reference in SQL params");
|
|
89
|
+
}
|
|
90
|
+
seen.add(arrRef);
|
|
91
|
+
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
92
|
+
seen.delete(arrRef);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
function normalizeObjectValue(value, seen, depth) {
|
|
96
|
+
if (value instanceof Uint8Array) return value;
|
|
97
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
98
|
+
const proto = Object.getPrototypeOf(value);
|
|
99
|
+
const isPlain = proto === Object.prototype || proto === null;
|
|
100
|
+
if (!isPlain) return value;
|
|
101
|
+
const obj = value;
|
|
102
|
+
if (seen.has(obj)) {
|
|
103
|
+
throw new Error("Circular reference in SQL params");
|
|
104
|
+
}
|
|
105
|
+
seen.add(obj);
|
|
106
|
+
const out = {};
|
|
107
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
108
|
+
out[k] = normalizeValue(v, seen, depth + 1);
|
|
109
|
+
}
|
|
110
|
+
seen.delete(obj);
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
238
113
|
|
|
239
114
|
// src/sql-builder-dialect.ts
|
|
240
115
|
var globalDialect = "postgres";
|
|
@@ -414,140 +289,222 @@ function prepareArrayParam(value, dialect) {
|
|
|
414
289
|
if (dialect === "postgres") {
|
|
415
290
|
return value.map((v) => normalizeValue(v));
|
|
416
291
|
}
|
|
417
|
-
return JSON.stringify(value);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// src/builder/shared/validators/type-guards.ts
|
|
421
|
-
function isNotNullish(value) {
|
|
422
|
-
return value !== null && value !== void 0;
|
|
423
|
-
}
|
|
424
|
-
function isNonEmptyString(value) {
|
|
425
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
426
|
-
}
|
|
427
|
-
function isEmptyString(value) {
|
|
428
|
-
return typeof value === "string" && value.trim().length === 0;
|
|
429
|
-
}
|
|
430
|
-
function isNonEmptyArray(value) {
|
|
431
|
-
return Array.isArray(value) && value.length > 0;
|
|
432
|
-
}
|
|
433
|
-
function isEmptyArray(value) {
|
|
434
|
-
return Array.isArray(value) && value.length === 0;
|
|
435
|
-
}
|
|
436
|
-
function isPlainObject(val) {
|
|
437
|
-
if (!isNotNullish(val)) return false;
|
|
438
|
-
if (Array.isArray(val)) return false;
|
|
439
|
-
if (typeof val !== "object") return false;
|
|
440
|
-
return Object.prototype.toString.call(val) === "[object Object]";
|
|
441
|
-
}
|
|
442
|
-
function hasProperty(obj, key) {
|
|
443
|
-
return isPlainObject(obj) && key in obj;
|
|
444
|
-
}
|
|
445
|
-
function isArrayType(t) {
|
|
446
|
-
if (!isNotNullish(t)) return false;
|
|
447
|
-
const normalized = t.replace(/\?$/, "");
|
|
448
|
-
return normalized.endsWith("[]");
|
|
449
|
-
}
|
|
450
|
-
function isJsonType(t) {
|
|
451
|
-
return isNotNullish(t) && (t === "Json" || t === "Json?");
|
|
452
|
-
}
|
|
453
|
-
function hasValidContent(sql) {
|
|
454
|
-
return isNotNullish(sql) && sql.trim().length > 0;
|
|
455
|
-
}
|
|
456
|
-
function hasRequiredKeywords(sql) {
|
|
457
|
-
const upper = sql.toUpperCase();
|
|
458
|
-
const hasSelect = upper.includes("SELECT");
|
|
459
|
-
const hasFrom = upper.includes("FROM");
|
|
460
|
-
return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// src/builder/shared/errors.ts
|
|
464
|
-
var SqlBuilderError = class extends Error {
|
|
465
|
-
constructor(message, code, context) {
|
|
466
|
-
super(message);
|
|
467
|
-
this.name = "SqlBuilderError";
|
|
468
|
-
this.code = code;
|
|
469
|
-
this.context = context;
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
function createError(message, ctx, code = "VALIDATION_ERROR") {
|
|
473
|
-
const parts = [message];
|
|
474
|
-
if (isNonEmptyArray(ctx.path)) {
|
|
475
|
-
parts.push(`Path: ${ctx.path.join(".")}`);
|
|
476
|
-
}
|
|
477
|
-
if (isNotNullish(ctx.modelName)) {
|
|
478
|
-
parts.push(`Model: ${ctx.modelName}`);
|
|
479
|
-
}
|
|
480
|
-
if (isNonEmptyArray(ctx.availableFields)) {
|
|
481
|
-
parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
|
|
482
|
-
}
|
|
483
|
-
return new SqlBuilderError(parts.join("\n"), code, ctx);
|
|
292
|
+
return JSON.stringify(value.map((v) => normalizeValue(v)));
|
|
484
293
|
}
|
|
485
294
|
|
|
486
|
-
// src/builder/shared/
|
|
487
|
-
var
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
295
|
+
// src/builder/shared/constants.ts
|
|
296
|
+
var IS_PRODUCTION = process.env.NODE_ENV === "production";
|
|
297
|
+
var SQL_SEPARATORS = Object.freeze({
|
|
298
|
+
FIELD_LIST: ", ",
|
|
299
|
+
CONDITION_AND: " AND ",
|
|
300
|
+
CONDITION_OR: " OR ",
|
|
301
|
+
ORDER_BY: ", "
|
|
302
|
+
});
|
|
303
|
+
var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
|
|
304
|
+
"select",
|
|
305
|
+
"from",
|
|
306
|
+
"where",
|
|
307
|
+
"having",
|
|
308
|
+
"order",
|
|
309
|
+
"group",
|
|
310
|
+
"limit",
|
|
311
|
+
"offset",
|
|
312
|
+
"join",
|
|
313
|
+
"inner",
|
|
314
|
+
"left",
|
|
315
|
+
"right",
|
|
316
|
+
"outer",
|
|
317
|
+
"cross",
|
|
318
|
+
"full",
|
|
319
|
+
"and",
|
|
320
|
+
"or",
|
|
321
|
+
"not",
|
|
322
|
+
"by",
|
|
323
|
+
"as",
|
|
324
|
+
"on",
|
|
325
|
+
"union",
|
|
326
|
+
"intersect",
|
|
327
|
+
"except",
|
|
328
|
+
"case",
|
|
329
|
+
"when",
|
|
330
|
+
"then",
|
|
331
|
+
"else",
|
|
332
|
+
"end"
|
|
333
|
+
]);
|
|
334
|
+
var SQL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
335
|
+
...ALIAS_FORBIDDEN_KEYWORDS,
|
|
336
|
+
"user",
|
|
337
|
+
"users",
|
|
338
|
+
"table",
|
|
339
|
+
"column",
|
|
340
|
+
"index",
|
|
341
|
+
"values",
|
|
342
|
+
"in",
|
|
343
|
+
"like",
|
|
344
|
+
"between",
|
|
345
|
+
"is",
|
|
346
|
+
"exists",
|
|
347
|
+
"null",
|
|
348
|
+
"true",
|
|
349
|
+
"false",
|
|
350
|
+
"all",
|
|
351
|
+
"any",
|
|
352
|
+
"some",
|
|
353
|
+
"update",
|
|
354
|
+
"insert",
|
|
355
|
+
"delete",
|
|
356
|
+
"create",
|
|
357
|
+
"drop",
|
|
358
|
+
"alter",
|
|
359
|
+
"truncate",
|
|
360
|
+
"grant",
|
|
361
|
+
"revoke",
|
|
362
|
+
"exec",
|
|
363
|
+
"execute"
|
|
364
|
+
]);
|
|
365
|
+
var SQL_RESERVED_WORDS = SQL_KEYWORDS;
|
|
366
|
+
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
367
|
+
var SPECIAL_FIELDS = Object.freeze({
|
|
368
|
+
ID: "id"
|
|
369
|
+
});
|
|
370
|
+
var SQL_TEMPLATES = Object.freeze({
|
|
371
|
+
PUBLIC_SCHEMA: "public",
|
|
372
|
+
WHERE: "WHERE",
|
|
373
|
+
SELECT: "SELECT",
|
|
374
|
+
FROM: "FROM",
|
|
375
|
+
ORDER_BY: "ORDER BY",
|
|
376
|
+
GROUP_BY: "GROUP BY",
|
|
377
|
+
HAVING: "HAVING",
|
|
378
|
+
LIMIT: "LIMIT",
|
|
379
|
+
OFFSET: "OFFSET",
|
|
380
|
+
COUNT_ALL: "COUNT(*)",
|
|
381
|
+
AS: "AS",
|
|
382
|
+
DISTINCT_ON: "DISTINCT ON",
|
|
383
|
+
IS_NULL: "IS NULL",
|
|
384
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
385
|
+
LIKE: "LIKE",
|
|
386
|
+
AND: "AND",
|
|
387
|
+
OR: "OR",
|
|
388
|
+
NOT: "NOT"
|
|
389
|
+
});
|
|
390
|
+
var SCHEMA_PREFIXES = Object.freeze({
|
|
391
|
+
INTERNAL: "@",
|
|
392
|
+
COMMENT: "//"
|
|
393
|
+
});
|
|
394
|
+
var Ops = Object.freeze({
|
|
395
|
+
EQUALS: "equals",
|
|
396
|
+
NOT: "not",
|
|
397
|
+
GT: "gt",
|
|
398
|
+
GTE: "gte",
|
|
399
|
+
LT: "lt",
|
|
400
|
+
LTE: "lte",
|
|
401
|
+
IN: "in",
|
|
402
|
+
NOT_IN: "notIn",
|
|
403
|
+
CONTAINS: "contains",
|
|
404
|
+
STARTS_WITH: "startsWith",
|
|
405
|
+
ENDS_WITH: "endsWith",
|
|
406
|
+
HAS: "has",
|
|
407
|
+
HAS_SOME: "hasSome",
|
|
408
|
+
HAS_EVERY: "hasEvery",
|
|
409
|
+
IS_EMPTY: "isEmpty",
|
|
410
|
+
PATH: "path",
|
|
411
|
+
STRING_CONTAINS: "string_contains",
|
|
412
|
+
STRING_STARTS_WITH: "string_starts_with",
|
|
413
|
+
STRING_ENDS_WITH: "string_ends_with"
|
|
414
|
+
});
|
|
415
|
+
var LogicalOps = Object.freeze({
|
|
416
|
+
AND: "AND",
|
|
417
|
+
OR: "OR",
|
|
418
|
+
NOT: "NOT"
|
|
419
|
+
});
|
|
420
|
+
var RelationFilters = Object.freeze({
|
|
421
|
+
SOME: "some",
|
|
422
|
+
EVERY: "every",
|
|
423
|
+
NONE: "none"
|
|
424
|
+
});
|
|
425
|
+
var Modes = Object.freeze({
|
|
426
|
+
INSENSITIVE: "insensitive",
|
|
427
|
+
DEFAULT: "default"
|
|
428
|
+
});
|
|
429
|
+
var Wildcards = Object.freeze({
|
|
430
|
+
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
431
|
+
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
432
|
+
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
433
|
+
});
|
|
434
|
+
var REGEX_CACHE = {
|
|
435
|
+
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
436
|
+
};
|
|
437
|
+
var LIMITS = Object.freeze({
|
|
438
|
+
MAX_QUERY_DEPTH: 50,
|
|
439
|
+
MAX_ARRAY_SIZE: 1e4,
|
|
440
|
+
MAX_STRING_LENGTH: 1e4,
|
|
441
|
+
MAX_HAVING_DEPTH: 50
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// src/builder/shared/validators/type-guards.ts
|
|
445
|
+
function isNotNullish(value) {
|
|
446
|
+
return value !== null && value !== void 0;
|
|
536
447
|
}
|
|
537
|
-
function
|
|
538
|
-
return
|
|
448
|
+
function isNonEmptyString(value) {
|
|
449
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
539
450
|
}
|
|
540
|
-
function
|
|
541
|
-
return
|
|
451
|
+
function isEmptyString(value) {
|
|
452
|
+
return typeof value === "string" && value.trim().length === 0;
|
|
542
453
|
}
|
|
543
|
-
function
|
|
544
|
-
return
|
|
454
|
+
function isNonEmptyArray(value) {
|
|
455
|
+
return Array.isArray(value) && value.length > 0;
|
|
545
456
|
}
|
|
546
|
-
function
|
|
547
|
-
return
|
|
457
|
+
function isEmptyArray(value) {
|
|
458
|
+
return Array.isArray(value) && value.length === 0;
|
|
548
459
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
460
|
+
function isPlainObject(val) {
|
|
461
|
+
if (!isNotNullish(val)) return false;
|
|
462
|
+
if (Array.isArray(val)) return false;
|
|
463
|
+
if (typeof val !== "object") return false;
|
|
464
|
+
return Object.prototype.toString.call(val) === "[object Object]";
|
|
465
|
+
}
|
|
466
|
+
function hasProperty(obj, key) {
|
|
467
|
+
return isPlainObject(obj) && key in obj;
|
|
468
|
+
}
|
|
469
|
+
function isArrayType(t) {
|
|
470
|
+
if (!isNotNullish(t)) return false;
|
|
471
|
+
const normalized = t.replace(/\?$/, "");
|
|
472
|
+
return normalized.endsWith("[]");
|
|
473
|
+
}
|
|
474
|
+
function isJsonType(t) {
|
|
475
|
+
return isNotNullish(t) && (t === "Json" || t === "Json?");
|
|
476
|
+
}
|
|
477
|
+
function hasValidContent(sql) {
|
|
478
|
+
return isNotNullish(sql) && sql.trim().length > 0;
|
|
479
|
+
}
|
|
480
|
+
function hasRequiredKeywords(sql) {
|
|
481
|
+
const upper = sql.toUpperCase();
|
|
482
|
+
const hasSelect = upper.includes("SELECT");
|
|
483
|
+
const hasFrom = upper.includes("FROM");
|
|
484
|
+
return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/builder/shared/errors.ts
|
|
488
|
+
var SqlBuilderError = class extends Error {
|
|
489
|
+
constructor(message, code, context) {
|
|
490
|
+
super(message);
|
|
491
|
+
this.name = "SqlBuilderError";
|
|
492
|
+
this.code = code;
|
|
493
|
+
this.context = context;
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
function createError(message, ctx, code = "VALIDATION_ERROR") {
|
|
497
|
+
const parts = [message];
|
|
498
|
+
if (isNonEmptyArray(ctx.path)) {
|
|
499
|
+
parts.push(`Path: ${ctx.path.join(".")}`);
|
|
500
|
+
}
|
|
501
|
+
if (isNotNullish(ctx.modelName)) {
|
|
502
|
+
parts.push(`Model: ${ctx.modelName}`);
|
|
503
|
+
}
|
|
504
|
+
if (isNonEmptyArray(ctx.availableFields)) {
|
|
505
|
+
parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
|
|
506
|
+
}
|
|
507
|
+
return new SqlBuilderError(parts.join("\n"), code, ctx);
|
|
551
508
|
}
|
|
552
509
|
|
|
553
510
|
// src/builder/shared/validators/sql-validators.ts
|
|
@@ -693,23 +650,104 @@ function needsQuoting(identifier) {
|
|
|
693
650
|
return false;
|
|
694
651
|
}
|
|
695
652
|
|
|
696
|
-
// src/builder/shared/
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
if (containsControlChars(s)) {
|
|
702
|
-
throw new Error(
|
|
703
|
-
`${label} contains invalid characters: ${JSON.stringify(s)}`
|
|
704
|
-
);
|
|
653
|
+
// src/builder/shared/model-field-cache.ts
|
|
654
|
+
var MODEL_CACHE = /* @__PURE__ */ new WeakMap();
|
|
655
|
+
function quoteIdent(id) {
|
|
656
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
657
|
+
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
705
658
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
659
|
+
for (let i = 0; i < id.length; i++) {
|
|
660
|
+
const code = id.charCodeAt(i);
|
|
661
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
662
|
+
throw new Error(
|
|
663
|
+
`quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (needsQuoting(id)) {
|
|
668
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
669
|
+
}
|
|
670
|
+
return id;
|
|
671
|
+
}
|
|
672
|
+
function ensureFullCache(model) {
|
|
673
|
+
let cache = MODEL_CACHE.get(model);
|
|
674
|
+
if (!cache) {
|
|
675
|
+
const fieldInfo = /* @__PURE__ */ new Map();
|
|
676
|
+
const scalarFields = /* @__PURE__ */ new Set();
|
|
677
|
+
const relationFields = /* @__PURE__ */ new Set();
|
|
678
|
+
const columnMap = /* @__PURE__ */ new Map();
|
|
679
|
+
const fieldByName = /* @__PURE__ */ new Map();
|
|
680
|
+
const quotedColumns = /* @__PURE__ */ new Map();
|
|
681
|
+
for (const f of model.fields) {
|
|
682
|
+
const info = {
|
|
683
|
+
name: f.name,
|
|
684
|
+
dbName: f.dbName || f.name,
|
|
685
|
+
type: f.type,
|
|
686
|
+
isRelation: !!f.isRelation,
|
|
687
|
+
isRequired: !!f.isRequired
|
|
688
|
+
};
|
|
689
|
+
fieldInfo.set(f.name, info);
|
|
690
|
+
fieldByName.set(f.name, f);
|
|
691
|
+
if (info.isRelation) {
|
|
692
|
+
relationFields.add(f.name);
|
|
693
|
+
} else {
|
|
694
|
+
scalarFields.add(f.name);
|
|
695
|
+
const dbName = info.dbName;
|
|
696
|
+
columnMap.set(f.name, dbName);
|
|
697
|
+
quotedColumns.set(f.name, quoteIdent(dbName));
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
cache = {
|
|
701
|
+
fieldInfo,
|
|
702
|
+
scalarFields,
|
|
703
|
+
relationFields,
|
|
704
|
+
columnMap,
|
|
705
|
+
fieldByName,
|
|
706
|
+
quotedColumns
|
|
707
|
+
};
|
|
708
|
+
MODEL_CACHE.set(model, cache);
|
|
709
|
+
}
|
|
710
|
+
return cache;
|
|
711
|
+
}
|
|
712
|
+
function getFieldInfo(model, fieldName) {
|
|
713
|
+
return ensureFullCache(model).fieldInfo.get(fieldName);
|
|
714
|
+
}
|
|
715
|
+
function getScalarFieldSet(model) {
|
|
716
|
+
return ensureFullCache(model).scalarFields;
|
|
717
|
+
}
|
|
718
|
+
function getRelationFieldSet(model) {
|
|
719
|
+
return ensureFullCache(model).relationFields;
|
|
720
|
+
}
|
|
721
|
+
function getColumnMap(model) {
|
|
722
|
+
return ensureFullCache(model).columnMap;
|
|
723
|
+
}
|
|
724
|
+
function getQuotedColumn(model, fieldName) {
|
|
725
|
+
return ensureFullCache(model).quotedColumns.get(fieldName);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// src/builder/shared/sql-utils.ts
|
|
729
|
+
function containsControlChars(s) {
|
|
730
|
+
for (let i = 0; i < s.length; i++) {
|
|
731
|
+
const code = s.charCodeAt(i);
|
|
732
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
function assertNoControlChars(label, s) {
|
|
739
|
+
if (containsControlChars(s)) {
|
|
740
|
+
throw new Error(
|
|
741
|
+
`${label} contains invalid characters: ${JSON.stringify(s)}`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function quoteRawIdent(id) {
|
|
746
|
+
return `"${id.replace(/"/g, '""')}"`;
|
|
747
|
+
}
|
|
748
|
+
function isIdentCharCode(c) {
|
|
749
|
+
return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
|
|
750
|
+
}
|
|
713
751
|
function isIdentStartCharCode(c) {
|
|
714
752
|
return c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
|
|
715
753
|
}
|
|
@@ -770,20 +808,9 @@ function parseUnquotedPart(input, start) {
|
|
|
770
808
|
}
|
|
771
809
|
return i;
|
|
772
810
|
}
|
|
773
|
-
function
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
if (trimmed.length === 0) {
|
|
777
|
-
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
778
|
-
}
|
|
779
|
-
if (raw !== trimmed) {
|
|
780
|
-
throw new Error(
|
|
781
|
-
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
assertNoControlChars("tableName/tableRef", trimmed);
|
|
785
|
-
for (let i2 = 0; i2 < trimmed.length; i2++) {
|
|
786
|
-
const c = trimmed.charCodeAt(i2);
|
|
811
|
+
function validateQualifiedNameFormat(trimmed) {
|
|
812
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
813
|
+
const c = trimmed.charCodeAt(i);
|
|
787
814
|
if (c === 9 || c === 11 || c === 12 || c === 32) {
|
|
788
815
|
throw new Error(
|
|
789
816
|
`tableName/tableRef must not contain whitespace: ${JSON.stringify(trimmed)}`
|
|
@@ -800,6 +827,8 @@ function assertSafeQualifiedName(input) {
|
|
|
800
827
|
);
|
|
801
828
|
}
|
|
802
829
|
}
|
|
830
|
+
}
|
|
831
|
+
function parseQualifiedNameParts(trimmed) {
|
|
803
832
|
let i = 0;
|
|
804
833
|
const n = trimmed.length;
|
|
805
834
|
let parts = 0;
|
|
@@ -835,7 +864,22 @@ function assertSafeQualifiedName(input) {
|
|
|
835
864
|
}
|
|
836
865
|
}
|
|
837
866
|
}
|
|
838
|
-
function
|
|
867
|
+
function assertSafeQualifiedName(input) {
|
|
868
|
+
const raw = String(input);
|
|
869
|
+
const trimmed = raw.trim();
|
|
870
|
+
if (trimmed.length === 0) {
|
|
871
|
+
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
872
|
+
}
|
|
873
|
+
if (raw !== trimmed) {
|
|
874
|
+
throw new Error(
|
|
875
|
+
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
assertNoControlChars("tableName/tableRef", trimmed);
|
|
879
|
+
validateQualifiedNameFormat(trimmed);
|
|
880
|
+
parseQualifiedNameParts(trimmed);
|
|
881
|
+
}
|
|
882
|
+
function quote(id) {
|
|
839
883
|
if (isEmptyString(id)) {
|
|
840
884
|
throw new Error("quote: identifier is required and cannot be empty");
|
|
841
885
|
}
|
|
@@ -855,13 +899,13 @@ function resolveColumnName(model, fieldName) {
|
|
|
855
899
|
return columnMap.get(fieldName) || fieldName;
|
|
856
900
|
}
|
|
857
901
|
function quoteColumn(model, fieldName) {
|
|
858
|
-
if (!model) return
|
|
902
|
+
if (!model) return quote(fieldName);
|
|
859
903
|
const cached = getQuotedColumn(model, fieldName);
|
|
860
|
-
return cached ||
|
|
904
|
+
return cached || quote(fieldName);
|
|
861
905
|
}
|
|
862
906
|
function col(alias, field, model) {
|
|
863
907
|
const columnName = model ? getColumnMap(model).get(field) || field : field;
|
|
864
|
-
const quoted = model ? getQuotedColumn(model, field) ||
|
|
908
|
+
const quoted = model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName);
|
|
865
909
|
return alias + "." + quoted;
|
|
866
910
|
}
|
|
867
911
|
function colWithAlias(alias, field, model) {
|
|
@@ -872,9 +916,9 @@ function colWithAlias(alias, field, model) {
|
|
|
872
916
|
throw new Error("colWithAlias: field is required and cannot be empty");
|
|
873
917
|
}
|
|
874
918
|
const columnName = resolveColumnName(model, field);
|
|
875
|
-
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) ||
|
|
919
|
+
const columnRef = alias + "." + (model ? getQuotedColumn(model, field) || quote(columnName) : quote(columnName));
|
|
876
920
|
if (columnName !== field) {
|
|
877
|
-
return columnRef + " AS " +
|
|
921
|
+
return columnRef + " AS " + quote(field);
|
|
878
922
|
}
|
|
879
923
|
return columnRef;
|
|
880
924
|
}
|
|
@@ -897,7 +941,7 @@ function buildTableReference(schemaName, tableName, dialect) {
|
|
|
897
941
|
}
|
|
898
942
|
const d = dialect != null ? dialect : "postgres";
|
|
899
943
|
if (d === "sqlite") {
|
|
900
|
-
return
|
|
944
|
+
return quote(tableName);
|
|
901
945
|
}
|
|
902
946
|
if (isEmptyString(schemaName)) {
|
|
903
947
|
throw new Error(
|
|
@@ -924,7 +968,7 @@ function assertSafeAlias(alias) {
|
|
|
924
968
|
if (a !== alias) {
|
|
925
969
|
throw new Error("Invalid alias: leading/trailing whitespace");
|
|
926
970
|
}
|
|
927
|
-
if (
|
|
971
|
+
if (containsControlChars(a)) {
|
|
928
972
|
throw new Error(
|
|
929
973
|
"Invalid alias: contains unsafe characters (control characters)"
|
|
930
974
|
);
|
|
@@ -945,7 +989,7 @@ function assertSafeAlias(alias) {
|
|
|
945
989
|
"Invalid alias: must be a simple identifier without whitespace"
|
|
946
990
|
);
|
|
947
991
|
}
|
|
948
|
-
if (!/^[A-Za-z_]
|
|
992
|
+
if (!/^[A-Za-z_]\w*$/.test(a)) {
|
|
949
993
|
throw new Error(
|
|
950
994
|
`Invalid alias: must be a simple identifier (alphanumeric with underscores): "${alias}"`
|
|
951
995
|
);
|
|
@@ -993,8 +1037,7 @@ function isValidRelationField(field) {
|
|
|
993
1037
|
return false;
|
|
994
1038
|
const fk = normalizeKeyList(field.foreignKey);
|
|
995
1039
|
if (fk.length === 0) return false;
|
|
996
|
-
const
|
|
997
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1040
|
+
const refs = normalizeKeyList(field.references);
|
|
998
1041
|
if (refs.length === 0) {
|
|
999
1042
|
return fk.length === 1;
|
|
1000
1043
|
}
|
|
@@ -1002,8 +1045,7 @@ function isValidRelationField(field) {
|
|
|
1002
1045
|
return true;
|
|
1003
1046
|
}
|
|
1004
1047
|
function getReferenceFieldNames(field, foreignKeyCount) {
|
|
1005
|
-
const
|
|
1006
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1048
|
+
const refs = normalizeKeyList(field.references);
|
|
1007
1049
|
if (refs.length === 0) {
|
|
1008
1050
|
if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
|
|
1009
1051
|
return [];
|
|
@@ -1164,29 +1206,35 @@ function normalizeOrderByInput(orderBy, parseValue) {
|
|
|
1164
1206
|
}
|
|
1165
1207
|
|
|
1166
1208
|
// src/builder/shared/order-by-determinism.ts
|
|
1167
|
-
function
|
|
1168
|
-
if (!model) return
|
|
1169
|
-
|
|
1209
|
+
function findTiebreakerField(model) {
|
|
1210
|
+
if (!model) return null;
|
|
1211
|
+
const scalarSet = getScalarFieldSet(model);
|
|
1212
|
+
for (const f of model.fields) {
|
|
1213
|
+
if (f.isId && !f.isRelation && scalarSet.has(f.name)) return f.name;
|
|
1214
|
+
}
|
|
1215
|
+
if (scalarSet.has("id")) return "id";
|
|
1216
|
+
return null;
|
|
1170
1217
|
}
|
|
1171
|
-
function
|
|
1218
|
+
function hasTiebreaker(orderBy, parse, field) {
|
|
1172
1219
|
if (!isNotNullish(orderBy)) return false;
|
|
1173
1220
|
const normalized = normalizeOrderByInput(orderBy, parse);
|
|
1174
1221
|
return normalized.some(
|
|
1175
|
-
(obj) => Object.prototype.hasOwnProperty.call(obj,
|
|
1222
|
+
(obj) => Object.prototype.hasOwnProperty.call(obj, field)
|
|
1176
1223
|
);
|
|
1177
1224
|
}
|
|
1178
|
-
function
|
|
1179
|
-
if (Array.isArray(orderBy)) return [...orderBy, {
|
|
1180
|
-
return [orderBy, {
|
|
1225
|
+
function addTiebreaker(orderBy, field) {
|
|
1226
|
+
if (Array.isArray(orderBy)) return [...orderBy, { [field]: "asc" }];
|
|
1227
|
+
return [orderBy, { [field]: "asc" }];
|
|
1181
1228
|
}
|
|
1182
1229
|
function ensureDeterministicOrderByInput(args) {
|
|
1183
1230
|
const { orderBy, model, parseValue } = args;
|
|
1184
|
-
|
|
1231
|
+
const tiebreaker = findTiebreakerField(model);
|
|
1232
|
+
if (!tiebreaker) return orderBy;
|
|
1185
1233
|
if (!isNotNullish(orderBy)) {
|
|
1186
|
-
return {
|
|
1234
|
+
return { [tiebreaker]: "asc" };
|
|
1187
1235
|
}
|
|
1188
|
-
if (
|
|
1189
|
-
return
|
|
1236
|
+
if (hasTiebreaker(orderBy, parseValue, tiebreaker)) return orderBy;
|
|
1237
|
+
return addTiebreaker(orderBy, tiebreaker);
|
|
1190
1238
|
}
|
|
1191
1239
|
|
|
1192
1240
|
// src/builder/shared/validators/field-assertions.ts
|
|
@@ -1333,11 +1381,9 @@ function defaultNullsFor(dialect, direction) {
|
|
|
1333
1381
|
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1334
1382
|
if (cursorEntries.length === 0) return orderEntries;
|
|
1335
1383
|
const existing = /* @__PURE__ */ new Set();
|
|
1336
|
-
for (
|
|
1337
|
-
existing.add(orderEntries[i].field);
|
|
1384
|
+
for (const entry of orderEntries) existing.add(entry.field);
|
|
1338
1385
|
let out = null;
|
|
1339
|
-
for (
|
|
1340
|
-
const field = cursorEntries[i][0];
|
|
1386
|
+
for (const [field] of cursorEntries) {
|
|
1341
1387
|
if (!existing.has(field)) {
|
|
1342
1388
|
if (!out) out = orderEntries.slice();
|
|
1343
1389
|
out.push({ field, direction: "asc" });
|
|
@@ -1562,6 +1608,7 @@ function buildOrderEntries(orderBy) {
|
|
|
1562
1608
|
}
|
|
1563
1609
|
return entries;
|
|
1564
1610
|
}
|
|
1611
|
+
var MAX_NOT_DEPTH = 50;
|
|
1565
1612
|
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1566
1613
|
const entries = Object.entries(val).filter(
|
|
1567
1614
|
([k, v]) => k !== "mode" && v !== void 0
|
|
@@ -1576,45 +1623,71 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
|
1576
1623
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1577
1624
|
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1578
1625
|
}
|
|
1579
|
-
function
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1626
|
+
function validateOperatorRequirements(op, mode, dialect) {
|
|
1627
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1628
|
+
Ops.CONTAINS,
|
|
1629
|
+
Ops.STARTS_WITH,
|
|
1630
|
+
Ops.ENDS_WITH
|
|
1631
|
+
]);
|
|
1632
|
+
if (STRING_LIKE_OPS.has(op) && !isNotNullish(dialect)) {
|
|
1633
|
+
throw createError(`Like operators require a SQL dialect`, { operator: op });
|
|
1586
1634
|
}
|
|
1587
|
-
if (op === Ops.
|
|
1588
|
-
|
|
1589
|
-
return `${expr} <> ${placeholder}`;
|
|
1635
|
+
if ((op === Ops.IN || op === Ops.NOT_IN) && !isNotNullish(dialect)) {
|
|
1636
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1590
1637
|
}
|
|
1591
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1592
|
-
|
|
1593
|
-
|
|
1638
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1639
|
+
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1640
|
+
operator: op
|
|
1641
|
+
});
|
|
1594
1642
|
}
|
|
1643
|
+
}
|
|
1644
|
+
function routeOperatorHandler(expr, op, val, params, mode, dialect) {
|
|
1595
1645
|
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1596
1646
|
Ops.CONTAINS,
|
|
1597
1647
|
Ops.STARTS_WITH,
|
|
1598
1648
|
Ops.ENDS_WITH
|
|
1599
1649
|
]);
|
|
1600
1650
|
if (STRING_LIKE_OPS.has(op)) {
|
|
1601
|
-
if (!isNotNullish(dialect)) {
|
|
1602
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1603
|
-
operator: op
|
|
1604
|
-
});
|
|
1605
|
-
}
|
|
1606
1651
|
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1607
1652
|
}
|
|
1608
1653
|
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1609
|
-
if (!isNotNullish(dialect)) {
|
|
1610
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1611
|
-
}
|
|
1612
1654
|
return handleInOperator(expr, op, val, params, dialect);
|
|
1613
1655
|
}
|
|
1614
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE &&
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1656
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1657
|
+
const placeholder = params.addAuto(val);
|
|
1658
|
+
return caseInsensitiveEquals(expr, placeholder);
|
|
1659
|
+
}
|
|
1660
|
+
return null;
|
|
1661
|
+
}
|
|
1662
|
+
function buildScalarOperator(expr, op, val, params, options = {}) {
|
|
1663
|
+
const { mode, fieldType, dialect, depth = 0 } = options;
|
|
1664
|
+
if (val === void 0) return "";
|
|
1665
|
+
if (depth > MAX_NOT_DEPTH) {
|
|
1666
|
+
throw new Error(
|
|
1667
|
+
`NOT operator nesting too deep (max ${MAX_NOT_DEPTH} levels). This usually indicates a circular reference or adversarial input.`
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
if (val === null) {
|
|
1671
|
+
return handleNullValue(expr, op);
|
|
1672
|
+
}
|
|
1673
|
+
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1674
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect, depth);
|
|
1675
|
+
}
|
|
1676
|
+
if (op === Ops.NOT) {
|
|
1677
|
+
const placeholder = params.addAuto(val);
|
|
1678
|
+
return `${expr} <> ${placeholder}`;
|
|
1679
|
+
}
|
|
1680
|
+
validateOperatorRequirements(op, mode, dialect);
|
|
1681
|
+
const specialHandler = routeOperatorHandler(
|
|
1682
|
+
expr,
|
|
1683
|
+
op,
|
|
1684
|
+
val,
|
|
1685
|
+
params,
|
|
1686
|
+
mode,
|
|
1687
|
+
dialect
|
|
1688
|
+
);
|
|
1689
|
+
if (specialHandler !== null) {
|
|
1690
|
+
return specialHandler;
|
|
1618
1691
|
}
|
|
1619
1692
|
return handleComparisonOperator(expr, op, val, params);
|
|
1620
1693
|
}
|
|
@@ -1628,7 +1701,7 @@ function normalizeMode(v) {
|
|
|
1628
1701
|
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1629
1702
|
return void 0;
|
|
1630
1703
|
}
|
|
1631
|
-
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1704
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, depth = 0) {
|
|
1632
1705
|
const innerMode = normalizeMode(val.mode);
|
|
1633
1706
|
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1634
1707
|
const entries = Object.entries(val).filter(
|
|
@@ -1638,43 +1711,35 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
|
1638
1711
|
if (!isNotNullish(dialect)) {
|
|
1639
1712
|
const clauses = [];
|
|
1640
1713
|
for (const [subOp, subVal] of entries) {
|
|
1641
|
-
const sub = buildScalarOperator(
|
|
1642
|
-
|
|
1643
|
-
subOp,
|
|
1644
|
-
subVal,
|
|
1645
|
-
params,
|
|
1646
|
-
effectiveMode,
|
|
1714
|
+
const sub = buildScalarOperator(expr, subOp, subVal, params, {
|
|
1715
|
+
mode: effectiveMode,
|
|
1647
1716
|
fieldType,
|
|
1648
|
-
void 0
|
|
1649
|
-
|
|
1717
|
+
dialect: void 0,
|
|
1718
|
+
depth: depth + 1
|
|
1719
|
+
});
|
|
1650
1720
|
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1651
1721
|
}
|
|
1652
1722
|
if (clauses.length === 0) return "";
|
|
1653
1723
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1654
|
-
|
|
1724
|
+
const separator2 = ` ${SQL_TEMPLATES.AND} `;
|
|
1725
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator2)})`;
|
|
1655
1726
|
}
|
|
1727
|
+
const separator = ` ${SQL_TEMPLATES.AND} `;
|
|
1656
1728
|
return buildNotComposite(
|
|
1657
1729
|
expr,
|
|
1658
1730
|
val,
|
|
1659
1731
|
params,
|
|
1660
1732
|
dialect,
|
|
1661
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p,
|
|
1662
|
-
|
|
1733
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, {
|
|
1734
|
+
mode: effectiveMode,
|
|
1735
|
+
fieldType,
|
|
1736
|
+
dialect: d,
|
|
1737
|
+
depth: depth + 1
|
|
1738
|
+
}),
|
|
1739
|
+
separator
|
|
1663
1740
|
);
|
|
1664
1741
|
}
|
|
1665
|
-
function buildDynamicLikePattern(op, placeholder
|
|
1666
|
-
if (dialect === "postgres") {
|
|
1667
|
-
switch (op) {
|
|
1668
|
-
case Ops.CONTAINS:
|
|
1669
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1670
|
-
case Ops.STARTS_WITH:
|
|
1671
|
-
return `(${placeholder} || '%')`;
|
|
1672
|
-
case Ops.ENDS_WITH:
|
|
1673
|
-
return `('%' || ${placeholder})`;
|
|
1674
|
-
default:
|
|
1675
|
-
return placeholder;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1742
|
+
function buildDynamicLikePattern(op, placeholder) {
|
|
1678
1743
|
switch (op) {
|
|
1679
1744
|
case Ops.CONTAINS:
|
|
1680
1745
|
return `('%' || ${placeholder} || '%')`;
|
|
@@ -1690,7 +1755,7 @@ function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
|
1690
1755
|
if (val === void 0) return "";
|
|
1691
1756
|
if (isDynamicParameter(val)) {
|
|
1692
1757
|
const placeholder2 = params.addAuto(val);
|
|
1693
|
-
const patternExpr = buildDynamicLikePattern(op, placeholder2
|
|
1758
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2);
|
|
1694
1759
|
if (mode === Modes.INSENSITIVE) {
|
|
1695
1760
|
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1696
1761
|
}
|
|
@@ -1868,24 +1933,42 @@ function handleArrayIsEmpty(expr, val, dialect) {
|
|
|
1868
1933
|
// src/builder/where/operators-json.ts
|
|
1869
1934
|
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1870
1935
|
var MAX_PATH_SEGMENT_LENGTH = 255;
|
|
1936
|
+
var MAX_PATH_SEGMENTS = 100;
|
|
1937
|
+
function sanitizeForError(s) {
|
|
1938
|
+
let result = "";
|
|
1939
|
+
for (let i = 0; i < s.length; i++) {
|
|
1940
|
+
const code = s.charCodeAt(i);
|
|
1941
|
+
if (code >= 0 && code <= 31 || code === 127) {
|
|
1942
|
+
result += `\\x${code.toString(16).padStart(2, "0")}`;
|
|
1943
|
+
} else {
|
|
1944
|
+
result += s[i];
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
return result;
|
|
1948
|
+
}
|
|
1871
1949
|
function validateJsonPathSegments(segments) {
|
|
1872
|
-
|
|
1950
|
+
if (segments.length > MAX_PATH_SEGMENTS) {
|
|
1951
|
+
throw createError(`JSON path too long: max ${MAX_PATH_SEGMENTS} segments`, {
|
|
1952
|
+
operator: Ops.PATH
|
|
1953
|
+
});
|
|
1954
|
+
}
|
|
1955
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1956
|
+
const segment = segments[i];
|
|
1873
1957
|
if (typeof segment !== "string") {
|
|
1874
|
-
throw createError(
|
|
1875
|
-
operator: Ops.PATH
|
|
1876
|
-
value: segment
|
|
1958
|
+
throw createError(`JSON path segment at index ${i} must be string`, {
|
|
1959
|
+
operator: Ops.PATH
|
|
1877
1960
|
});
|
|
1878
1961
|
}
|
|
1879
1962
|
if (segment.length > MAX_PATH_SEGMENT_LENGTH) {
|
|
1880
1963
|
throw createError(
|
|
1881
|
-
`JSON path segment too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
1882
|
-
{ operator: Ops.PATH
|
|
1964
|
+
`JSON path segment at index ${i} too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
1965
|
+
{ operator: Ops.PATH }
|
|
1883
1966
|
);
|
|
1884
1967
|
}
|
|
1885
1968
|
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1886
1969
|
throw createError(
|
|
1887
|
-
`Invalid JSON path segment: '${segment}'
|
|
1888
|
-
{ operator: Ops.PATH
|
|
1970
|
+
`Invalid JSON path segment at index ${i}: '${sanitizeForError(segment)}'`,
|
|
1971
|
+
{ operator: Ops.PATH }
|
|
1889
1972
|
);
|
|
1890
1973
|
}
|
|
1891
1974
|
}
|
|
@@ -1976,7 +2059,7 @@ function freezeJoins(items) {
|
|
|
1976
2059
|
function isListRelation(fieldType) {
|
|
1977
2060
|
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1978
2061
|
}
|
|
1979
|
-
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join, wantNull) {
|
|
2062
|
+
function buildToOneNullCheck(field, parentModel, parentAlias, relTable, relAlias, join, wantNull) {
|
|
1980
2063
|
const isLocal = field.isForeignKeyLocal === true;
|
|
1981
2064
|
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1982
2065
|
if (isLocal) {
|
|
@@ -1986,8 +2069,7 @@ function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join, wantN
|
|
|
1986
2069
|
});
|
|
1987
2070
|
}
|
|
1988
2071
|
const parts = fkFields.map((fk) => {
|
|
1989
|
-
const
|
|
1990
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
2072
|
+
const expr = `${parentAlias}.${quoteColumn(parentModel, fk)}`;
|
|
1991
2073
|
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1992
2074
|
});
|
|
1993
2075
|
if (parts.length === 1) return parts[0];
|
|
@@ -2004,6 +2086,35 @@ function buildToOneNotExistsMatch(relTable, relAlias, join, sub) {
|
|
|
2004
2086
|
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2005
2087
|
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
2006
2088
|
}
|
|
2089
|
+
function tryOptimizeNoneFilter(noneValue, ctx, relModel, relTable, relAlias, join, sub) {
|
|
2090
|
+
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
2091
|
+
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
2092
|
+
if (!canOptimize) return null;
|
|
2093
|
+
const checkField = relModel.fields.find(
|
|
2094
|
+
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
2095
|
+
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
2096
|
+
if (!checkField) return null;
|
|
2097
|
+
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join}`;
|
|
2098
|
+
const whereClause = `${relAlias}.${quoteColumn(relModel, checkField.name)} IS NULL`;
|
|
2099
|
+
return Object.freeze({
|
|
2100
|
+
clause: whereClause,
|
|
2101
|
+
joins: freezeJoins([leftJoinSql])
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
function processRelationFilter(key, wrap, args) {
|
|
2105
|
+
const { value, fieldName, ctx, relAlias, relModel, whereBuilder } = args;
|
|
2106
|
+
const raw = value[key];
|
|
2107
|
+
if (raw === void 0 || raw === null) return null;
|
|
2108
|
+
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2109
|
+
alias: relAlias,
|
|
2110
|
+
model: relModel,
|
|
2111
|
+
path: [...ctx.path, fieldName, key],
|
|
2112
|
+
isSubquery: true,
|
|
2113
|
+
depth: ctx.depth + 1
|
|
2114
|
+
}));
|
|
2115
|
+
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2116
|
+
return wrap(sub.clause, j);
|
|
2117
|
+
}
|
|
2007
2118
|
function buildListRelationFilters(args) {
|
|
2008
2119
|
const {
|
|
2009
2120
|
fieldName,
|
|
@@ -2024,21 +2135,16 @@ function buildListRelationFilters(args) {
|
|
|
2024
2135
|
isSubquery: true,
|
|
2025
2136
|
depth: ctx.depth + 1
|
|
2026
2137
|
}));
|
|
2027
|
-
const
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
clause: whereClause,
|
|
2038
|
-
joins: freezeJoins([leftJoinSql])
|
|
2039
|
-
});
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2138
|
+
const optimized = tryOptimizeNoneFilter(
|
|
2139
|
+
noneValue,
|
|
2140
|
+
ctx,
|
|
2141
|
+
relModel,
|
|
2142
|
+
relTable,
|
|
2143
|
+
relAlias,
|
|
2144
|
+
join,
|
|
2145
|
+
sub
|
|
2146
|
+
);
|
|
2147
|
+
if (optimized) return optimized;
|
|
2042
2148
|
}
|
|
2043
2149
|
const filters = [
|
|
2044
2150
|
{
|
|
@@ -2059,17 +2165,8 @@ function buildListRelationFilters(args) {
|
|
|
2059
2165
|
];
|
|
2060
2166
|
const clauses = [];
|
|
2061
2167
|
for (const { key, wrap } of filters) {
|
|
2062
|
-
const
|
|
2063
|
-
if (
|
|
2064
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2065
|
-
alias: relAlias,
|
|
2066
|
-
model: relModel,
|
|
2067
|
-
path: [...ctx.path, fieldName, key],
|
|
2068
|
-
isSubquery: true,
|
|
2069
|
-
depth: ctx.depth + 1
|
|
2070
|
-
}));
|
|
2071
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2072
|
-
clauses.push(wrap(sub.clause, j));
|
|
2168
|
+
const clause = processRelationFilter(key, wrap, args);
|
|
2169
|
+
if (clause) clauses.push(clause);
|
|
2073
2170
|
}
|
|
2074
2171
|
if (clauses.length === 0) {
|
|
2075
2172
|
throw createError(
|
|
@@ -2125,6 +2222,7 @@ function buildToOneRelationFilters(args) {
|
|
|
2125
2222
|
const wantNull = filterKey === "is";
|
|
2126
2223
|
const clause2 = buildToOneNullCheck(
|
|
2127
2224
|
field,
|
|
2225
|
+
ctx.model,
|
|
2128
2226
|
ctx.alias,
|
|
2129
2227
|
relTable,
|
|
2130
2228
|
relAlias,
|
|
@@ -2354,7 +2452,8 @@ function buildWhereInternal(where, ctx, builder) {
|
|
|
2354
2452
|
}
|
|
2355
2453
|
const allJoins = [];
|
|
2356
2454
|
const clauses = [];
|
|
2357
|
-
for (const
|
|
2455
|
+
for (const key in where) {
|
|
2456
|
+
const value = where[key];
|
|
2358
2457
|
if (value === void 0) continue;
|
|
2359
2458
|
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2360
2459
|
appendResult(result, clauses, allJoins);
|
|
@@ -2469,15 +2568,11 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2469
2568
|
if (fieldType && isJsonType(fieldType) && JSON_OPS.has(op)) {
|
|
2470
2569
|
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2471
2570
|
}
|
|
2472
|
-
return buildScalarOperator(
|
|
2473
|
-
expr,
|
|
2474
|
-
op,
|
|
2475
|
-
val,
|
|
2476
|
-
ctx.params,
|
|
2571
|
+
return buildScalarOperator(expr, op, val, ctx.params, {
|
|
2477
2572
|
mode,
|
|
2478
2573
|
fieldType,
|
|
2479
|
-
ctx.dialect
|
|
2480
|
-
);
|
|
2574
|
+
dialect: ctx.dialect
|
|
2575
|
+
});
|
|
2481
2576
|
}
|
|
2482
2577
|
|
|
2483
2578
|
// src/builder/shared/alias-generator.ts
|
|
@@ -2591,49 +2686,50 @@ function validateState(params, mappings, index) {
|
|
|
2591
2686
|
validateMappings(mappings);
|
|
2592
2687
|
assertNextIndexMatches(mappings.length, index);
|
|
2593
2688
|
}
|
|
2594
|
-
function
|
|
2595
|
-
let index = startIndex;
|
|
2596
|
-
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2597
|
-
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2689
|
+
function buildDynamicNameIndex(mappings) {
|
|
2598
2690
|
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2599
|
-
for (
|
|
2600
|
-
const m = mappings[i];
|
|
2691
|
+
for (const m of mappings) {
|
|
2601
2692
|
if (typeof m.dynamicName === "string") {
|
|
2602
2693
|
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2603
2694
|
}
|
|
2604
2695
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2613
|
-
);
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
function format(position) {
|
|
2617
|
-
return `$${position}`;
|
|
2696
|
+
return dynamicNameToIndex;
|
|
2697
|
+
}
|
|
2698
|
+
function assertCanAddParam(currentIndex) {
|
|
2699
|
+
if (currentIndex > MAX_PARAM_INDEX) {
|
|
2700
|
+
throw new Error(
|
|
2701
|
+
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${currentIndex}`
|
|
2702
|
+
);
|
|
2618
2703
|
}
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2704
|
+
}
|
|
2705
|
+
function formatPosition(position) {
|
|
2706
|
+
return `$${position}`;
|
|
2707
|
+
}
|
|
2708
|
+
function validateDynamicName(dynamicName) {
|
|
2709
|
+
const dn = dynamicName.trim();
|
|
2710
|
+
if (dn.length === 0) {
|
|
2711
|
+
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2625
2712
|
}
|
|
2713
|
+
return dn;
|
|
2714
|
+
}
|
|
2715
|
+
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2716
|
+
let index = startIndex;
|
|
2717
|
+
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2718
|
+
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2719
|
+
const dynamicNameToIndex = buildDynamicNameIndex(mappings);
|
|
2720
|
+
let dirty = true;
|
|
2721
|
+
let cachedSnapshot = null;
|
|
2626
2722
|
function addDynamic(dynamicName) {
|
|
2627
|
-
const dn =
|
|
2723
|
+
const dn = validateDynamicName(dynamicName);
|
|
2628
2724
|
const existing = dynamicNameToIndex.get(dn);
|
|
2629
|
-
if (existing !== void 0) return
|
|
2725
|
+
if (existing !== void 0) return formatPosition(existing);
|
|
2630
2726
|
const position = index;
|
|
2631
2727
|
dynamicNameToIndex.set(dn, position);
|
|
2632
2728
|
params.push(void 0);
|
|
2633
2729
|
mappings.push({ index: position, dynamicName: dn });
|
|
2634
2730
|
index++;
|
|
2635
2731
|
dirty = true;
|
|
2636
|
-
return
|
|
2732
|
+
return formatPosition(position);
|
|
2637
2733
|
}
|
|
2638
2734
|
function addStatic(value) {
|
|
2639
2735
|
const position = index;
|
|
@@ -2642,10 +2738,10 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2642
2738
|
mappings.push({ index: position, value: normalizedValue });
|
|
2643
2739
|
index++;
|
|
2644
2740
|
dirty = true;
|
|
2645
|
-
return
|
|
2741
|
+
return formatPosition(position);
|
|
2646
2742
|
}
|
|
2647
2743
|
function add(value, dynamicName) {
|
|
2648
|
-
|
|
2744
|
+
assertCanAddParam(index);
|
|
2649
2745
|
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2650
2746
|
}
|
|
2651
2747
|
function addAuto(value) {
|
|
@@ -2657,17 +2753,13 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2657
2753
|
}
|
|
2658
2754
|
function snapshot() {
|
|
2659
2755
|
if (!dirty && cachedSnapshot) return cachedSnapshot;
|
|
2660
|
-
if (!frozenParams) frozenParams = Object.freeze(params.slice());
|
|
2661
|
-
if (!frozenMappings) frozenMappings = Object.freeze(mappings.slice());
|
|
2662
2756
|
const snap = {
|
|
2663
2757
|
index,
|
|
2664
|
-
params:
|
|
2665
|
-
mappings:
|
|
2758
|
+
params: params.slice(),
|
|
2759
|
+
mappings: mappings.slice()
|
|
2666
2760
|
};
|
|
2667
2761
|
cachedSnapshot = snap;
|
|
2668
2762
|
dirty = false;
|
|
2669
|
-
frozenParams = null;
|
|
2670
|
-
frozenMappings = null;
|
|
2671
2763
|
return snap;
|
|
2672
2764
|
}
|
|
2673
2765
|
return {
|
|
@@ -2870,6 +2962,17 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
|
|
|
2870
2962
|
var MAX_INCLUDE_DEPTH = 10;
|
|
2871
2963
|
var MAX_TOTAL_SUBQUERIES = 100;
|
|
2872
2964
|
var MAX_TOTAL_INCLUDES = 50;
|
|
2965
|
+
function buildIncludeScope(includePath) {
|
|
2966
|
+
if (includePath.length === 0) return "include";
|
|
2967
|
+
let scope = "include";
|
|
2968
|
+
for (let i = 0; i < includePath.length; i++) {
|
|
2969
|
+
scope += `.${includePath[i]}`;
|
|
2970
|
+
if (i < includePath.length - 1) {
|
|
2971
|
+
scope += ".include";
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
return scope;
|
|
2975
|
+
}
|
|
2873
2976
|
function getRelationTableReference(relModel, dialect) {
|
|
2874
2977
|
return buildTableReference(
|
|
2875
2978
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
@@ -3013,19 +3116,11 @@ function finalizeOrderByForInclude(args) {
|
|
|
3013
3116
|
}
|
|
3014
3117
|
function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
|
|
3015
3118
|
let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
|
|
3016
|
-
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
ctx.
|
|
3020
|
-
|
|
3021
|
-
relAlias,
|
|
3022
|
-
ctx.aliasGen,
|
|
3023
|
-
ctx.params,
|
|
3024
|
-
ctx.dialect,
|
|
3025
|
-
ctx.visitPath || [],
|
|
3026
|
-
(ctx.depth || 0) + 1,
|
|
3027
|
-
ctx.stats
|
|
3028
|
-
) : [];
|
|
3119
|
+
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(relArgs, __spreadProps(__spreadValues({}, ctx), {
|
|
3120
|
+
model: relModel,
|
|
3121
|
+
parentAlias: relAlias,
|
|
3122
|
+
depth: (ctx.depth || 0) + 1
|
|
3123
|
+
})) : [];
|
|
3029
3124
|
if (isNonEmptyArray(nestedIncludes)) {
|
|
3030
3125
|
const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3031
3126
|
const nestedSelects = nestedIncludes.map(
|
|
@@ -3080,6 +3175,7 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3080
3175
|
whereClause: args.whereClause
|
|
3081
3176
|
});
|
|
3082
3177
|
if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3178
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3083
3179
|
if (isNotNullish(args.takeVal)) {
|
|
3084
3180
|
return appendLimitOffset(
|
|
3085
3181
|
sql,
|
|
@@ -3087,15 +3183,10 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3087
3183
|
args.ctx.params,
|
|
3088
3184
|
args.takeVal,
|
|
3089
3185
|
args.skipVal,
|
|
3090
|
-
|
|
3186
|
+
scopeBase
|
|
3091
3187
|
);
|
|
3092
3188
|
}
|
|
3093
|
-
return limitOneSql(
|
|
3094
|
-
sql,
|
|
3095
|
-
args.ctx.params,
|
|
3096
|
-
args.skipVal,
|
|
3097
|
-
`include.${args.relName}`
|
|
3098
|
-
);
|
|
3189
|
+
return limitOneSql(sql, args.ctx.params, args.skipVal, scopeBase);
|
|
3099
3190
|
}
|
|
3100
3191
|
function buildListIncludeSpec(args) {
|
|
3101
3192
|
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
@@ -3123,13 +3214,14 @@ function buildListIncludeSpec(args) {
|
|
|
3123
3214
|
whereClause: args.whereClause
|
|
3124
3215
|
});
|
|
3125
3216
|
if (args.orderBySql) base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3217
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3126
3218
|
base = appendLimitOffset(
|
|
3127
3219
|
base,
|
|
3128
3220
|
args.ctx.dialect,
|
|
3129
3221
|
args.ctx.params,
|
|
3130
3222
|
args.takeVal,
|
|
3131
3223
|
args.skipVal,
|
|
3132
|
-
|
|
3224
|
+
scopeBase
|
|
3133
3225
|
);
|
|
3134
3226
|
const selectExpr = jsonAgg("row", args.ctx.dialect);
|
|
3135
3227
|
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
@@ -3178,7 +3270,6 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3178
3270
|
);
|
|
3179
3271
|
if (!isList) {
|
|
3180
3272
|
const sql = buildOneToOneIncludeSql({
|
|
3181
|
-
relName,
|
|
3182
3273
|
relTable,
|
|
3183
3274
|
relAlias,
|
|
3184
3275
|
joins: whereParts.joins,
|
|
@@ -3206,16 +3297,22 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3206
3297
|
ctx
|
|
3207
3298
|
});
|
|
3208
3299
|
}
|
|
3209
|
-
function buildIncludeSqlInternal(args,
|
|
3210
|
-
|
|
3211
|
-
|
|
3300
|
+
function buildIncludeSqlInternal(args, ctx) {
|
|
3301
|
+
const stats = ctx.stats || {
|
|
3302
|
+
totalIncludes: 0,
|
|
3303
|
+
totalSubqueries: 0,
|
|
3304
|
+
maxDepth: 0
|
|
3305
|
+
};
|
|
3306
|
+
const depth = ctx.depth || 0;
|
|
3307
|
+
const visitPath = ctx.visitPath || [];
|
|
3308
|
+
if (depth > MAX_INCLUDE_DEPTH) {
|
|
3212
3309
|
throw new Error(
|
|
3213
3310
|
`Maximum include depth of ${MAX_INCLUDE_DEPTH} exceeded. Path: ${visitPath.join(" -> ")}. Deep includes cause exponential SQL complexity and performance issues.`
|
|
3214
3311
|
);
|
|
3215
3312
|
}
|
|
3216
3313
|
stats.maxDepth = Math.max(stats.maxDepth, depth);
|
|
3217
3314
|
const includes = [];
|
|
3218
|
-
const entries = relationEntriesFromArgs(args, model);
|
|
3315
|
+
const entries = relationEntriesFromArgs(args, ctx.model);
|
|
3219
3316
|
for (const [relName, relArgs] of entries) {
|
|
3220
3317
|
if (relArgs === false) continue;
|
|
3221
3318
|
stats.totalIncludes++;
|
|
@@ -3230,8 +3327,12 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3230
3327
|
`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.`
|
|
3231
3328
|
);
|
|
3232
3329
|
}
|
|
3233
|
-
const resolved = resolveRelationOrThrow(
|
|
3234
|
-
|
|
3330
|
+
const resolved = resolveRelationOrThrow(
|
|
3331
|
+
ctx.model,
|
|
3332
|
+
ctx.schemaByName,
|
|
3333
|
+
relName
|
|
3334
|
+
);
|
|
3335
|
+
const relationPath = `${ctx.model.name}.${relName}`;
|
|
3235
3336
|
const currentPath = [...visitPath, relationPath];
|
|
3236
3337
|
if (visitPath.includes(relationPath)) {
|
|
3237
3338
|
throw new Error(
|
|
@@ -3246,19 +3347,14 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3246
3347
|
`Include too deeply nested: model '${resolved.relModel.name}' appears ${modelOccurrences} times in path: ${currentPath.join(" -> ")}`
|
|
3247
3348
|
);
|
|
3248
3349
|
}
|
|
3350
|
+
const nextIncludePath = [...ctx.includePath, relName];
|
|
3249
3351
|
includes.push(
|
|
3250
|
-
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, {
|
|
3251
|
-
|
|
3252
|
-
schemas,
|
|
3253
|
-
schemaByName,
|
|
3254
|
-
parentAlias,
|
|
3255
|
-
aliasGen,
|
|
3256
|
-
dialect,
|
|
3257
|
-
params,
|
|
3352
|
+
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
|
|
3353
|
+
includePath: nextIncludePath,
|
|
3258
3354
|
visitPath: currentPath,
|
|
3259
3355
|
depth: depth + 1,
|
|
3260
3356
|
stats
|
|
3261
|
-
})
|
|
3357
|
+
}))
|
|
3262
3358
|
);
|
|
3263
3359
|
}
|
|
3264
3360
|
return includes;
|
|
@@ -3272,8 +3368,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3272
3368
|
};
|
|
3273
3369
|
const schemaByName = /* @__PURE__ */ new Map();
|
|
3274
3370
|
for (const m of schemas) schemaByName.set(m.name, m);
|
|
3275
|
-
return buildIncludeSqlInternal(
|
|
3276
|
-
args,
|
|
3371
|
+
return buildIncludeSqlInternal(args, {
|
|
3277
3372
|
model,
|
|
3278
3373
|
schemas,
|
|
3279
3374
|
schemaByName,
|
|
@@ -3281,10 +3376,11 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3281
3376
|
aliasGen,
|
|
3282
3377
|
params,
|
|
3283
3378
|
dialect,
|
|
3284
|
-
[],
|
|
3285
|
-
|
|
3379
|
+
includePath: [],
|
|
3380
|
+
visitPath: [],
|
|
3381
|
+
depth: 0,
|
|
3286
3382
|
stats
|
|
3287
|
-
);
|
|
3383
|
+
});
|
|
3288
3384
|
}
|
|
3289
3385
|
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
3290
3386
|
const relationSet = getRelationFieldSet(model);
|
|
@@ -3329,8 +3425,7 @@ function resolveCountKeyPairs(field) {
|
|
|
3329
3425
|
if (fkFields.length === 0) {
|
|
3330
3426
|
throw new Error("Relation count requires foreignKey");
|
|
3331
3427
|
}
|
|
3332
|
-
const
|
|
3333
|
-
const refs = normalizeKeyList(refsRaw);
|
|
3428
|
+
const refs = normalizeKeyList(field.references);
|
|
3334
3429
|
const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
|
|
3335
3430
|
if (refFields.length !== fkFields.length) {
|
|
3336
3431
|
throw new Error(
|
|
@@ -3465,139 +3560,146 @@ function finalizeSql(sql, params, dialect) {
|
|
|
3465
3560
|
paramMappings: snapshot.mappings
|
|
3466
3561
|
};
|
|
3467
3562
|
}
|
|
3468
|
-
function
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
i2 += 2;
|
|
3490
|
-
continue;
|
|
3491
|
-
}
|
|
3492
|
-
if (!saw)
|
|
3493
|
-
throw new Error(
|
|
3494
|
-
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3495
|
-
);
|
|
3496
|
-
return { text: out, next: i2 + 1, quoted: true };
|
|
3497
|
-
}
|
|
3498
|
-
out += s[i2];
|
|
3499
|
-
saw = true;
|
|
3500
|
-
i2++;
|
|
3501
|
-
}
|
|
3502
|
-
throw new Error(
|
|
3503
|
-
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3504
|
-
);
|
|
3505
|
-
}
|
|
3506
|
-
let i = start;
|
|
3507
|
-
while (i < n) {
|
|
3508
|
-
const c = s.charCodeAt(i);
|
|
3509
|
-
if (c === 32 || c === 9) break;
|
|
3510
|
-
if (c === 46) break;
|
|
3511
|
-
i++;
|
|
3512
|
-
}
|
|
3513
|
-
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3514
|
-
};
|
|
3515
|
-
const skipSpaces = (s, i) => {
|
|
3516
|
-
while (i < s.length) {
|
|
3517
|
-
const c = s.charCodeAt(i);
|
|
3518
|
-
if (c !== 32 && c !== 9) break;
|
|
3563
|
+
function isIdent(s) {
|
|
3564
|
+
return /^[A-Za-z_]\w*$/.test(s);
|
|
3565
|
+
}
|
|
3566
|
+
function skipSpaces(s, i) {
|
|
3567
|
+
while (i < s.length) {
|
|
3568
|
+
const c = s.charCodeAt(i);
|
|
3569
|
+
if (c !== 32 && c !== 9) break;
|
|
3570
|
+
i++;
|
|
3571
|
+
}
|
|
3572
|
+
return i;
|
|
3573
|
+
}
|
|
3574
|
+
function parseQuotedIdentifier(s, start) {
|
|
3575
|
+
const n = s.length;
|
|
3576
|
+
let i = start + 1;
|
|
3577
|
+
let out = "";
|
|
3578
|
+
let saw = false;
|
|
3579
|
+
while (i < n) {
|
|
3580
|
+
const c = s.charCodeAt(i);
|
|
3581
|
+
if (c !== 34) {
|
|
3582
|
+
out += s[i];
|
|
3583
|
+
saw = true;
|
|
3519
3584
|
i++;
|
|
3585
|
+
continue;
|
|
3520
3586
|
}
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
i = skipSpaces(p, i);
|
|
3528
|
-
const a = readIdentOrQuoted(p, i);
|
|
3529
|
-
const actualAlias = a.text.toLowerCase();
|
|
3530
|
-
if (!isIdent(a.text)) {
|
|
3531
|
-
throw new Error(
|
|
3532
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3533
|
-
);
|
|
3534
|
-
}
|
|
3535
|
-
if (actualAlias !== fromLower) {
|
|
3536
|
-
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3587
|
+
const next = i + 1;
|
|
3588
|
+
if (next < n && s.charCodeAt(next) === 34) {
|
|
3589
|
+
out += '"';
|
|
3590
|
+
saw = true;
|
|
3591
|
+
i += 2;
|
|
3592
|
+
continue;
|
|
3537
3593
|
}
|
|
3538
|
-
|
|
3539
|
-
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3594
|
+
if (!saw) {
|
|
3540
3595
|
throw new Error(
|
|
3541
|
-
`sqlite distinct emulation
|
|
3596
|
+
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3542
3597
|
);
|
|
3543
3598
|
}
|
|
3599
|
+
return { text: out, next: i + 1, quoted: true };
|
|
3600
|
+
}
|
|
3601
|
+
throw new Error(
|
|
3602
|
+
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3603
|
+
);
|
|
3604
|
+
}
|
|
3605
|
+
function parseUnquotedIdentifier(s, start) {
|
|
3606
|
+
const n = s.length;
|
|
3607
|
+
let i = start;
|
|
3608
|
+
while (i < n) {
|
|
3609
|
+
const c = s.charCodeAt(i);
|
|
3610
|
+
if (c === 32 || c === 9 || c === 46) break;
|
|
3544
3611
|
i++;
|
|
3545
|
-
i = skipSpaces(p, i);
|
|
3546
|
-
const colPart = readIdentOrQuoted(p, i);
|
|
3547
|
-
const columnName = colPart.text.trim();
|
|
3548
|
-
if (columnName.length === 0) {
|
|
3549
|
-
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3550
|
-
}
|
|
3551
|
-
i = colPart.next;
|
|
3552
|
-
i = skipSpaces(p, i);
|
|
3553
|
-
let outAlias = "";
|
|
3554
|
-
if (i < p.length) {
|
|
3555
|
-
const rest = p.slice(i).trim();
|
|
3556
|
-
if (rest.length > 0) {
|
|
3557
|
-
const m = rest.match(/^AS\s+/i);
|
|
3558
|
-
if (!m) {
|
|
3559
|
-
throw new Error(
|
|
3560
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3561
|
-
);
|
|
3562
|
-
}
|
|
3563
|
-
let j = i;
|
|
3564
|
-
j = skipSpaces(p, j);
|
|
3565
|
-
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3566
|
-
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3567
|
-
}
|
|
3568
|
-
j += 2;
|
|
3569
|
-
j = skipSpaces(p, j);
|
|
3570
|
-
const out = readIdentOrQuoted(p, j);
|
|
3571
|
-
outAlias = out.text.trim();
|
|
3572
|
-
if (outAlias.length === 0) {
|
|
3573
|
-
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3574
|
-
}
|
|
3575
|
-
j = skipSpaces(p, out.next);
|
|
3576
|
-
if (j !== p.length) {
|
|
3577
|
-
throw new Error(
|
|
3578
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3579
|
-
);
|
|
3580
|
-
}
|
|
3581
|
-
}
|
|
3582
|
-
}
|
|
3583
|
-
const name = outAlias.length > 0 ? outAlias : columnName;
|
|
3584
|
-
names.push(name);
|
|
3585
3612
|
}
|
|
3586
|
-
return
|
|
3613
|
+
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3614
|
+
}
|
|
3615
|
+
function readIdentOrQuoted(s, start) {
|
|
3616
|
+
const n = s.length;
|
|
3617
|
+
if (start >= n) return { text: "", next: start, quoted: false };
|
|
3618
|
+
if (s.charCodeAt(start) === 34) {
|
|
3619
|
+
return parseQuotedIdentifier(s, start);
|
|
3620
|
+
}
|
|
3621
|
+
return parseUnquotedIdentifier(s, start);
|
|
3622
|
+
}
|
|
3623
|
+
function parseOutputAlias(p, i) {
|
|
3624
|
+
if (i >= p.length) return "";
|
|
3625
|
+
const rest = p.slice(i).trim();
|
|
3626
|
+
if (rest.length === 0) return "";
|
|
3627
|
+
const m = rest.match(/^AS\s+/i);
|
|
3628
|
+
if (!m) {
|
|
3629
|
+
throw new Error(
|
|
3630
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3631
|
+
);
|
|
3632
|
+
}
|
|
3633
|
+
let j = i;
|
|
3634
|
+
j = skipSpaces(p, j);
|
|
3635
|
+
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3636
|
+
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3637
|
+
}
|
|
3638
|
+
j += 2;
|
|
3639
|
+
j = skipSpaces(p, j);
|
|
3640
|
+
const out = readIdentOrQuoted(p, j);
|
|
3641
|
+
const outAlias = out.text.trim();
|
|
3642
|
+
if (outAlias.length === 0) {
|
|
3643
|
+
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3644
|
+
}
|
|
3645
|
+
j = skipSpaces(p, out.next);
|
|
3646
|
+
if (j !== p.length) {
|
|
3647
|
+
throw new Error(
|
|
3648
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3651
|
+
return outAlias;
|
|
3652
|
+
}
|
|
3653
|
+
function parseSelectField(p, fromAlias) {
|
|
3654
|
+
const fromLower = fromAlias.toLowerCase();
|
|
3655
|
+
let i = 0;
|
|
3656
|
+
i = skipSpaces(p, i);
|
|
3657
|
+
const a = readIdentOrQuoted(p, i);
|
|
3658
|
+
const actualAlias = a.text.toLowerCase();
|
|
3659
|
+
if (!isIdent(a.text)) {
|
|
3660
|
+
throw new Error(
|
|
3661
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3662
|
+
);
|
|
3663
|
+
}
|
|
3664
|
+
if (actualAlias !== fromLower) {
|
|
3665
|
+
throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
|
|
3666
|
+
}
|
|
3667
|
+
i = a.next;
|
|
3668
|
+
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3669
|
+
throw new Error(
|
|
3670
|
+
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
i++;
|
|
3674
|
+
i = skipSpaces(p, i);
|
|
3675
|
+
const colPart = readIdentOrQuoted(p, i);
|
|
3676
|
+
const columnName = colPart.text.trim();
|
|
3677
|
+
if (columnName.length === 0) {
|
|
3678
|
+
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3679
|
+
}
|
|
3680
|
+
i = colPart.next;
|
|
3681
|
+
i = skipSpaces(p, i);
|
|
3682
|
+
const outAlias = parseOutputAlias(p, i);
|
|
3683
|
+
return outAlias.length > 0 ? outAlias : columnName;
|
|
3587
3684
|
}
|
|
3588
|
-
function
|
|
3589
|
-
const
|
|
3590
|
-
if (
|
|
3591
|
-
const
|
|
3592
|
-
const
|
|
3593
|
-
|
|
3685
|
+
function parseSimpleScalarSelect(select, fromAlias) {
|
|
3686
|
+
const raw = select.trim();
|
|
3687
|
+
if (raw.length === 0) return [];
|
|
3688
|
+
const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
|
|
3689
|
+
const names = [];
|
|
3690
|
+
for (const p of parts) {
|
|
3691
|
+
const trimmed = p.trim();
|
|
3692
|
+
if (trimmed.length === 0) continue;
|
|
3693
|
+
names.push(parseSelectField(trimmed, fromAlias));
|
|
3694
|
+
}
|
|
3695
|
+
return names;
|
|
3594
3696
|
}
|
|
3595
3697
|
function buildDistinctColumns(distinct, fromAlias, model) {
|
|
3596
3698
|
return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3597
3699
|
}
|
|
3598
3700
|
function buildOutputColumns(scalarNames, includeNames, hasCount) {
|
|
3599
3701
|
const outputCols = hasCount ? [...scalarNames, ...includeNames, "_count"] : [...scalarNames, ...includeNames];
|
|
3600
|
-
const formatted = outputCols.map((n) =>
|
|
3702
|
+
const formatted = outputCols.map((n) => quote(n)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3601
3703
|
if (!isNonEmptyString(formatted)) {
|
|
3602
3704
|
throw new Error("distinct emulation requires at least one output column");
|
|
3603
3705
|
}
|
|
@@ -3614,6 +3716,52 @@ function buildWindowOrder(args) {
|
|
|
3614
3716
|
const idTiebreaker = idField ? ", " + col(fromAlias, "id", model) + " ASC" : "";
|
|
3615
3717
|
return baseOrder + idTiebreaker;
|
|
3616
3718
|
}
|
|
3719
|
+
function extractDistinctOrderEntries(spec) {
|
|
3720
|
+
if (isNotNullish(spec.args.orderBy)) {
|
|
3721
|
+
const normalized = normalizeOrderByInput(
|
|
3722
|
+
spec.args.orderBy,
|
|
3723
|
+
parseOrderByValue
|
|
3724
|
+
);
|
|
3725
|
+
const entries = [];
|
|
3726
|
+
for (const item of normalized) {
|
|
3727
|
+
for (const field in item) {
|
|
3728
|
+
if (!Object.prototype.hasOwnProperty.call(item, field)) continue;
|
|
3729
|
+
const value = item[field];
|
|
3730
|
+
if (typeof value === "string") {
|
|
3731
|
+
entries.push({ field, direction: value });
|
|
3732
|
+
} else {
|
|
3733
|
+
const obj = value;
|
|
3734
|
+
entries.push({ field, direction: obj.sort, nulls: obj.nulls });
|
|
3735
|
+
}
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
if (entries.length > 0) return entries;
|
|
3739
|
+
}
|
|
3740
|
+
if (isNotNullish(spec.distinct) && isNonEmptyArray(spec.distinct)) {
|
|
3741
|
+
return [...spec.distinct].map((f) => ({
|
|
3742
|
+
field: f,
|
|
3743
|
+
direction: "asc"
|
|
3744
|
+
}));
|
|
3745
|
+
}
|
|
3746
|
+
return [];
|
|
3747
|
+
}
|
|
3748
|
+
function buildFieldNameOrderBy(entries, alias) {
|
|
3749
|
+
if (entries.length === 0) return "";
|
|
3750
|
+
const out = [];
|
|
3751
|
+
for (const e of entries) {
|
|
3752
|
+
const dir = e.direction.toUpperCase();
|
|
3753
|
+
const c = `${alias}.${quote(e.field)}`;
|
|
3754
|
+
if (isNotNullish(e.nulls)) {
|
|
3755
|
+
const isNullExpr = `(${c} IS NULL)`;
|
|
3756
|
+
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
3757
|
+
out.push(isNullExpr + " " + nullRankDir);
|
|
3758
|
+
out.push(c + " " + dir);
|
|
3759
|
+
continue;
|
|
3760
|
+
}
|
|
3761
|
+
out.push(c + " " + dir);
|
|
3762
|
+
}
|
|
3763
|
+
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
3764
|
+
}
|
|
3617
3765
|
function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
3618
3766
|
var _a, _b;
|
|
3619
3767
|
const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
|
|
@@ -3640,7 +3788,8 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3640
3788
|
fromAlias: from.alias,
|
|
3641
3789
|
model
|
|
3642
3790
|
});
|
|
3643
|
-
const
|
|
3791
|
+
const outerEntries = extractDistinctOrderEntries(spec);
|
|
3792
|
+
const outerOrder = buildFieldNameOrderBy(outerEntries, '"__tp_distinct"');
|
|
3644
3793
|
const joins = buildJoinsSql(whereJoins, countJoins);
|
|
3645
3794
|
const conditions = [];
|
|
3646
3795
|
if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
|
|
@@ -3675,17 +3824,33 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3675
3824
|
}
|
|
3676
3825
|
return outerParts.join(" ");
|
|
3677
3826
|
}
|
|
3827
|
+
function resolveCountSelect(countSelectRaw, model) {
|
|
3828
|
+
if (countSelectRaw === true) {
|
|
3829
|
+
const relationSet = getRelationFieldSet(model);
|
|
3830
|
+
if (relationSet.size === 0) return null;
|
|
3831
|
+
const allRelations = {};
|
|
3832
|
+
for (const name of relationSet) {
|
|
3833
|
+
allRelations[name] = true;
|
|
3834
|
+
}
|
|
3835
|
+
return allRelations;
|
|
3836
|
+
}
|
|
3837
|
+
if (isPlainObject(countSelectRaw) && "select" in countSelectRaw) {
|
|
3838
|
+
return countSelectRaw.select;
|
|
3839
|
+
}
|
|
3840
|
+
return null;
|
|
3841
|
+
}
|
|
3678
3842
|
function buildIncludeColumns(spec) {
|
|
3679
3843
|
var _a, _b;
|
|
3680
3844
|
const { select, includes, dialect, model, schemas, from, params } = spec;
|
|
3681
3845
|
const baseSelect = (select != null ? select : "").trim();
|
|
3682
3846
|
let countCols = "";
|
|
3683
3847
|
let countJoins = [];
|
|
3684
|
-
const
|
|
3685
|
-
if (
|
|
3686
|
-
|
|
3848
|
+
const countSelectRaw = (_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count;
|
|
3849
|
+
if (countSelectRaw) {
|
|
3850
|
+
const resolvedCountSelect = resolveCountSelect(countSelectRaw, model);
|
|
3851
|
+
if (resolvedCountSelect && Object.keys(resolvedCountSelect).length > 0) {
|
|
3687
3852
|
const countBuild = buildRelationCountSql(
|
|
3688
|
-
|
|
3853
|
+
resolvedCountSelect,
|
|
3689
3854
|
model,
|
|
3690
3855
|
schemas,
|
|
3691
3856
|
from.alias,
|
|
@@ -3693,7 +3858,7 @@ function buildIncludeColumns(spec) {
|
|
|
3693
3858
|
dialect
|
|
3694
3859
|
);
|
|
3695
3860
|
if (countBuild.jsonPairs) {
|
|
3696
|
-
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " +
|
|
3861
|
+
countCols = jsonBuildObject(countBuild.jsonPairs, dialect) + " " + SQL_TEMPLATES.AS + " " + quote("_count");
|
|
3697
3862
|
}
|
|
3698
3863
|
countJoins = countBuild.joins;
|
|
3699
3864
|
}
|
|
@@ -3706,7 +3871,7 @@ function buildIncludeColumns(spec) {
|
|
|
3706
3871
|
const emptyJson = dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3707
3872
|
const includeCols = hasIncludes ? includes.map((inc) => {
|
|
3708
3873
|
const expr = inc.isOneToOne ? "(" + inc.sql + ")" : "COALESCE((" + inc.sql + "), " + emptyJson + ")";
|
|
3709
|
-
return expr + " " + SQL_TEMPLATES.AS + " " +
|
|
3874
|
+
return expr + " " + SQL_TEMPLATES.AS + " " + quote(inc.name);
|
|
3710
3875
|
}).join(SQL_SEPARATORS.FIELD_LIST) : "";
|
|
3711
3876
|
const allCols = joinNonEmpty(
|
|
3712
3877
|
[includeCols, countCols],
|
|
@@ -4071,6 +4236,7 @@ function buildComparisons(expr, filter, params, dialect, builder, excludeKeys =
|
|
|
4071
4236
|
}
|
|
4072
4237
|
|
|
4073
4238
|
// src/builder/aggregates.ts
|
|
4239
|
+
var MAX_NOT_DEPTH2 = 50;
|
|
4074
4240
|
var AGGREGATES = [
|
|
4075
4241
|
["_sum", "SUM"],
|
|
4076
4242
|
["_avg", "AVG"],
|
|
@@ -4185,8 +4351,13 @@ function buildBinaryComparison(expr, op, val, params) {
|
|
|
4185
4351
|
const placeholder = addHavingParam(params, op, val);
|
|
4186
4352
|
return expr + " " + sqlOp + " " + placeholder;
|
|
4187
4353
|
}
|
|
4188
|
-
function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
4354
|
+
function buildSimpleComparison(expr, op, val, params, dialect, depth = 0) {
|
|
4189
4355
|
assertHavingOp(op);
|
|
4356
|
+
if (depth > MAX_NOT_DEPTH2) {
|
|
4357
|
+
throw new Error(
|
|
4358
|
+
`NOT operator nesting too deep in HAVING (max ${MAX_NOT_DEPTH2} levels).`
|
|
4359
|
+
);
|
|
4360
|
+
}
|
|
4190
4361
|
if (val === null) return buildNullComparison(expr, op);
|
|
4191
4362
|
if (op === Ops.NOT && isPlainObject(val)) {
|
|
4192
4363
|
return buildNotComposite(
|
|
@@ -4194,7 +4365,7 @@ function buildSimpleComparison(expr, op, val, params, dialect) {
|
|
|
4194
4365
|
val,
|
|
4195
4366
|
params,
|
|
4196
4367
|
dialect,
|
|
4197
|
-
buildSimpleComparison,
|
|
4368
|
+
(e, subOp, subVal, p, d) => buildSimpleComparison(e, subOp, subVal, p, d, depth + 1),
|
|
4198
4369
|
SQL_SEPARATORS.CONDITION_AND
|
|
4199
4370
|
);
|
|
4200
4371
|
}
|
|
@@ -4211,23 +4382,36 @@ function combineLogical(key, subClauses) {
|
|
|
4211
4382
|
if (key === LogicalOps.NOT) return negateClauses(subClauses);
|
|
4212
4383
|
return subClauses.join(" " + key + " ");
|
|
4213
4384
|
}
|
|
4214
|
-
function buildHavingNode(node, alias, params, dialect, model) {
|
|
4385
|
+
function buildHavingNode(node, alias, params, dialect, model, depth = 0) {
|
|
4386
|
+
if (depth > LIMITS.MAX_HAVING_DEPTH) {
|
|
4387
|
+
throw new Error(
|
|
4388
|
+
`HAVING clause nesting too deep (max ${LIMITS.MAX_HAVING_DEPTH} levels). This usually indicates a circular reference.`
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4215
4391
|
const clauses = [];
|
|
4216
4392
|
for (const key in node) {
|
|
4217
4393
|
if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
|
|
4218
4394
|
const value = node[key];
|
|
4219
|
-
const built = buildHavingEntry(
|
|
4395
|
+
const built = buildHavingEntry(
|
|
4396
|
+
key,
|
|
4397
|
+
value,
|
|
4398
|
+
alias,
|
|
4399
|
+
params,
|
|
4400
|
+
dialect,
|
|
4401
|
+
model,
|
|
4402
|
+
depth
|
|
4403
|
+
);
|
|
4220
4404
|
for (const c of built) {
|
|
4221
4405
|
if (c && c.length > 0) clauses.push(c);
|
|
4222
4406
|
}
|
|
4223
4407
|
}
|
|
4224
4408
|
return clauses.join(SQL_SEPARATORS.CONDITION_AND);
|
|
4225
4409
|
}
|
|
4226
|
-
function buildLogicalClause2(key, value, alias, params, dialect, model) {
|
|
4410
|
+
function buildLogicalClause2(key, value, alias, params, dialect, model, depth = 0) {
|
|
4227
4411
|
const items = normalizeLogicalValue2(key, value);
|
|
4228
4412
|
const subClauses = [];
|
|
4229
4413
|
for (const it of items) {
|
|
4230
|
-
const c = buildHavingNode(it, alias, params, dialect, model);
|
|
4414
|
+
const c = buildHavingNode(it, alias, params, dialect, model, depth + 1);
|
|
4231
4415
|
if (c && c.length > 0) subClauses.push("(" + c + ")");
|
|
4232
4416
|
}
|
|
4233
4417
|
if (subClauses.length === 0) return "";
|
|
@@ -4286,7 +4470,7 @@ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect
|
|
|
4286
4470
|
}
|
|
4287
4471
|
return out;
|
|
4288
4472
|
}
|
|
4289
|
-
function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
4473
|
+
function buildHavingEntry(key, value, alias, params, dialect, model, depth = 0) {
|
|
4290
4474
|
if (isLogicalKey(key)) {
|
|
4291
4475
|
const logical = buildLogicalClause2(
|
|
4292
4476
|
key,
|
|
@@ -4294,7 +4478,8 @@ function buildHavingEntry(key, value, alias, params, dialect, model) {
|
|
|
4294
4478
|
alias,
|
|
4295
4479
|
params,
|
|
4296
4480
|
dialect,
|
|
4297
|
-
model
|
|
4481
|
+
model,
|
|
4482
|
+
depth
|
|
4298
4483
|
);
|
|
4299
4484
|
return logical ? [logical] : [];
|
|
4300
4485
|
}
|
|
@@ -4321,7 +4506,7 @@ function buildHavingClause(having, alias, params, model, dialect) {
|
|
|
4321
4506
|
if (!isNotNullish(having)) return "";
|
|
4322
4507
|
const d = dialect != null ? dialect : getGlobalDialect();
|
|
4323
4508
|
if (!isPlainObject(having)) throw new Error("having must be an object");
|
|
4324
|
-
return buildHavingNode(having, alias, params, d, model);
|
|
4509
|
+
return buildHavingNode(having, alias, params, d, model, 0);
|
|
4325
4510
|
}
|
|
4326
4511
|
function normalizeCountArg(v) {
|
|
4327
4512
|
if (!isNotNullish(v)) return void 0;
|
|
@@ -4331,13 +4516,13 @@ function normalizeCountArg(v) {
|
|
|
4331
4516
|
}
|
|
4332
4517
|
function pushCountAllField(fields) {
|
|
4333
4518
|
fields.push(
|
|
4334
|
-
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " +
|
|
4519
|
+
SQL_TEMPLATES.COUNT_ALL + " " + SQL_TEMPLATES.AS + " " + quote("_count._all")
|
|
4335
4520
|
);
|
|
4336
4521
|
}
|
|
4337
4522
|
function pushCountField(fields, alias, fieldName, model) {
|
|
4338
4523
|
const outAlias = "_count." + fieldName;
|
|
4339
4524
|
fields.push(
|
|
4340
|
-
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4525
|
+
"COUNT(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4341
4526
|
);
|
|
4342
4527
|
}
|
|
4343
4528
|
function addCountFields(fields, countArg, alias, model) {
|
|
@@ -4374,7 +4559,7 @@ function assertAggregatableScalarField(model, agg, fieldName) {
|
|
|
4374
4559
|
function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model) {
|
|
4375
4560
|
const outAlias = agg + "." + fieldName;
|
|
4376
4561
|
fields.push(
|
|
4377
|
-
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " +
|
|
4562
|
+
aggFn + "(" + col(alias, fieldName, model) + ") " + SQL_TEMPLATES.AS + " " + quote(outAlias)
|
|
4378
4563
|
);
|
|
4379
4564
|
}
|
|
4380
4565
|
function addAggregateFields(fields, args, alias, model) {
|
|
@@ -4490,38 +4675,38 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4490
4675
|
paramMappings: allMappings
|
|
4491
4676
|
};
|
|
4492
4677
|
}
|
|
4678
|
+
function isPositiveInteger(value) {
|
|
4679
|
+
return Number.isFinite(value) && Number.isInteger(value) && value > 0;
|
|
4680
|
+
}
|
|
4681
|
+
function parseSkipValue(skip) {
|
|
4682
|
+
return typeof skip === "string" ? Number(skip.trim()) : skip;
|
|
4683
|
+
}
|
|
4684
|
+
function validateSkipParameter(skip) {
|
|
4685
|
+
if (skip === void 0 || skip === null) {
|
|
4686
|
+
return;
|
|
4687
|
+
}
|
|
4688
|
+
if (isDynamicParameter(skip)) {
|
|
4689
|
+
throw new Error(
|
|
4690
|
+
"count() with skip is not supported because it produces nondeterministic results. Dynamic skip cannot be validated at build time. Use findMany().length or add explicit orderBy + cursor/skip logic in a deterministic query."
|
|
4691
|
+
);
|
|
4692
|
+
}
|
|
4693
|
+
const skipValue = parseSkipValue(skip);
|
|
4694
|
+
if (isPositiveInteger(skipValue)) {
|
|
4695
|
+
throw new Error(
|
|
4696
|
+
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4697
|
+
);
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4493
4700
|
function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
4494
4701
|
assertSafeAlias(alias);
|
|
4495
4702
|
assertSafeTableRef(tableName);
|
|
4496
|
-
|
|
4497
|
-
if (isDynamicParameter(skip)) {
|
|
4498
|
-
throw new Error(
|
|
4499
|
-
"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."
|
|
4500
|
-
);
|
|
4501
|
-
}
|
|
4502
|
-
if (typeof skip === "string") {
|
|
4503
|
-
const s = skip.trim();
|
|
4504
|
-
if (s.length > 0) {
|
|
4505
|
-
const n = Number(s);
|
|
4506
|
-
if (Number.isFinite(n) && Number.isInteger(n) && n > 0) {
|
|
4507
|
-
throw new Error(
|
|
4508
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4509
|
-
);
|
|
4510
|
-
}
|
|
4511
|
-
}
|
|
4512
|
-
}
|
|
4513
|
-
if (typeof skip === "number" && Number.isFinite(skip) && Number.isInteger(skip) && skip > 0) {
|
|
4514
|
-
throw new Error(
|
|
4515
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4516
|
-
);
|
|
4517
|
-
}
|
|
4518
|
-
}
|
|
4703
|
+
validateSkipParameter(skip);
|
|
4519
4704
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4520
4705
|
const parts = [
|
|
4521
4706
|
SQL_TEMPLATES.SELECT,
|
|
4522
4707
|
SQL_TEMPLATES.COUNT_ALL,
|
|
4523
4708
|
SQL_TEMPLATES.AS,
|
|
4524
|
-
|
|
4709
|
+
quote("_count._all"),
|
|
4525
4710
|
SQL_TEMPLATES.FROM,
|
|
4526
4711
|
tableName,
|
|
4527
4712
|
alias
|
|
@@ -4562,15 +4747,21 @@ function buildSqlResult(args) {
|
|
|
4562
4747
|
return buildAggregateSql(processed, whereResult, tableName, alias, modelDef);
|
|
4563
4748
|
}
|
|
4564
4749
|
if (method === "groupBy") {
|
|
4565
|
-
return buildGroupBySql(
|
|
4750
|
+
return buildGroupBySql(
|
|
4751
|
+
processed,
|
|
4752
|
+
whereResult,
|
|
4753
|
+
tableName,
|
|
4754
|
+
alias,
|
|
4755
|
+
modelDef,
|
|
4756
|
+
dialect
|
|
4757
|
+
);
|
|
4566
4758
|
}
|
|
4567
4759
|
if (method === "count") {
|
|
4568
4760
|
return buildCountSql(
|
|
4569
4761
|
whereResult,
|
|
4570
4762
|
tableName,
|
|
4571
4763
|
alias,
|
|
4572
|
-
processed.skip
|
|
4573
|
-
);
|
|
4764
|
+
processed.skip);
|
|
4574
4765
|
}
|
|
4575
4766
|
return buildSelectSql({
|
|
4576
4767
|
method,
|
|
@@ -4610,22 +4801,23 @@ function normalizeSqlAndMappingsForDialect(sql, paramMappings, dialect) {
|
|
|
4610
4801
|
}
|
|
4611
4802
|
function buildParamsFromMappings(mappings) {
|
|
4612
4803
|
const sorted = [...mappings].sort((a, b) => a.index - b.index);
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4804
|
+
const staticParams = [];
|
|
4805
|
+
const dynamicKeys = [];
|
|
4806
|
+
let paramOrder = "";
|
|
4807
|
+
for (const m of sorted) {
|
|
4808
|
+
if (m.dynamicName !== void 0) {
|
|
4809
|
+
dynamicKeys.push(m.dynamicName);
|
|
4810
|
+
paramOrder += "d";
|
|
4811
|
+
} else if (m.value !== void 0) {
|
|
4812
|
+
staticParams.push(m.value);
|
|
4813
|
+
paramOrder += "s";
|
|
4814
|
+
} else {
|
|
4623
4815
|
throw new Error(
|
|
4624
4816
|
`CRITICAL: ParamMap ${m.index} has neither dynamicName nor value`
|
|
4625
4817
|
);
|
|
4626
|
-
}
|
|
4627
|
-
|
|
4628
|
-
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
return { staticParams, dynamicKeys, paramOrder };
|
|
4629
4821
|
}
|
|
4630
4822
|
function resolveModelContext(directive) {
|
|
4631
4823
|
const { model, datamodel } = directive.context;
|
|
@@ -4691,12 +4883,13 @@ function finalizeDirective(args) {
|
|
|
4691
4883
|
return (_a = m.value) != null ? _a : void 0;
|
|
4692
4884
|
});
|
|
4693
4885
|
validateParamConsistencyByDialect(normalizedSql, params, dialect);
|
|
4694
|
-
const { staticParams, dynamicKeys } = buildParamsFromMappings(normalizedMappings);
|
|
4886
|
+
const { staticParams, dynamicKeys, paramOrder } = buildParamsFromMappings(normalizedMappings);
|
|
4695
4887
|
return {
|
|
4696
4888
|
method: directive.method,
|
|
4697
4889
|
sql: normalizedSql,
|
|
4698
4890
|
staticParams,
|
|
4699
4891
|
dynamicKeys,
|
|
4892
|
+
paramOrder,
|
|
4700
4893
|
paramMappings: normalizedMappings,
|
|
4701
4894
|
originalDirective: directive
|
|
4702
4895
|
};
|
|
@@ -4732,62 +4925,6 @@ function generateSQL(directive) {
|
|
|
4732
4925
|
});
|
|
4733
4926
|
}
|
|
4734
4927
|
|
|
4735
|
-
// src/result-transformers.ts
|
|
4736
|
-
function parseAggregateValue(value) {
|
|
4737
|
-
if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
|
|
4738
|
-
return parseFloat(value);
|
|
4739
|
-
}
|
|
4740
|
-
return value;
|
|
4741
|
-
}
|
|
4742
|
-
function transformGroupByResults(results) {
|
|
4743
|
-
return results.map((row) => {
|
|
4744
|
-
const raw = row;
|
|
4745
|
-
const parsed = {};
|
|
4746
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
4747
|
-
const parts = key.split(".");
|
|
4748
|
-
if (parts.length === 2) {
|
|
4749
|
-
const [group, field] = parts;
|
|
4750
|
-
if (!parsed[group]) parsed[group] = {};
|
|
4751
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
4752
|
-
} else {
|
|
4753
|
-
parsed[key] = value;
|
|
4754
|
-
}
|
|
4755
|
-
}
|
|
4756
|
-
return parsed;
|
|
4757
|
-
});
|
|
4758
|
-
}
|
|
4759
|
-
function transformCountResults(results) {
|
|
4760
|
-
const result = results[0];
|
|
4761
|
-
const count = (result == null ? void 0 : result["_count._all"]) || (result == null ? void 0 : result.count) || 0;
|
|
4762
|
-
return typeof count === "string" ? parseInt(count, 10) : count;
|
|
4763
|
-
}
|
|
4764
|
-
function transformAggregateResults(results) {
|
|
4765
|
-
const raw = results[0] || {};
|
|
4766
|
-
const parsed = {};
|
|
4767
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
4768
|
-
const parts = key.split(".");
|
|
4769
|
-
if (parts.length === 2) {
|
|
4770
|
-
const [group, field] = parts;
|
|
4771
|
-
if (!parsed[group]) parsed[group] = {};
|
|
4772
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
4773
|
-
} else {
|
|
4774
|
-
parsed[key] = value;
|
|
4775
|
-
}
|
|
4776
|
-
}
|
|
4777
|
-
return parsed;
|
|
4778
|
-
}
|
|
4779
|
-
var RESULT_TRANSFORMERS = {
|
|
4780
|
-
findFirst: (results) => results[0] || null,
|
|
4781
|
-
findUnique: (results) => results[0] || null,
|
|
4782
|
-
count: transformCountResults,
|
|
4783
|
-
aggregate: transformAggregateResults,
|
|
4784
|
-
groupBy: transformGroupByResults
|
|
4785
|
-
};
|
|
4786
|
-
function transformQueryResults(method, results) {
|
|
4787
|
-
const transformer = RESULT_TRANSFORMERS[method];
|
|
4788
|
-
return transformer ? transformer(results) : results;
|
|
4789
|
-
}
|
|
4790
|
-
|
|
4791
4928
|
// src/utils/s3-fifo.ts
|
|
4792
4929
|
function withDispose(it) {
|
|
4793
4930
|
const anyIt = it;
|
|
@@ -5007,107 +5144,374 @@ function createBoundedCache(maxSize) {
|
|
|
5007
5144
|
return new BoundedCache(maxSize);
|
|
5008
5145
|
}
|
|
5009
5146
|
|
|
5010
|
-
// src/
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
const reorderedParams = [];
|
|
5019
|
-
let lastIndex = 0;
|
|
5020
|
-
const parts = [];
|
|
5021
|
-
for (let i = 0; i < sql.length; i++) {
|
|
5022
|
-
if (sql[i] === "$" && i + 1 < sql.length) {
|
|
5023
|
-
let num = 0;
|
|
5024
|
-
let j = i + 1;
|
|
5025
|
-
while (j < sql.length && sql[j] >= "0" && sql[j] <= "9") {
|
|
5026
|
-
num = num * 10 + (sql.charCodeAt(j) - 48);
|
|
5027
|
-
j++;
|
|
5028
|
-
}
|
|
5029
|
-
if (j > i + 1) {
|
|
5030
|
-
parts.push(sql.substring(lastIndex, i));
|
|
5031
|
-
parts.push("?");
|
|
5032
|
-
reorderedParams.push(params[num - 1]);
|
|
5033
|
-
i = j - 1;
|
|
5034
|
-
lastIndex = j;
|
|
5035
|
-
}
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
parts.push(sql.substring(lastIndex));
|
|
5039
|
-
return { sql: parts.join(""), params: reorderedParams };
|
|
5147
|
+
// src/fast-path.ts
|
|
5148
|
+
function getIdField(model) {
|
|
5149
|
+
const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
|
|
5150
|
+
if (!idField) return null;
|
|
5151
|
+
return {
|
|
5152
|
+
name: "id",
|
|
5153
|
+
dbName: idField.dbName || "id"
|
|
5154
|
+
};
|
|
5040
5155
|
}
|
|
5041
|
-
function
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
if (
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
if (
|
|
5048
|
-
|
|
5049
|
-
}
|
|
5050
|
-
|
|
5051
|
-
const sorted = {};
|
|
5052
|
-
for (const key of Object.keys(obj).sort()) {
|
|
5053
|
-
const value = obj[key];
|
|
5054
|
-
sorted[key] = isDynamicParameter(value) ? "__DYN__" : normalize(value);
|
|
5055
|
-
}
|
|
5056
|
-
return sorted;
|
|
5156
|
+
function buildColumnList(model, alias) {
|
|
5157
|
+
const cols = [];
|
|
5158
|
+
for (const f of model.fields) {
|
|
5159
|
+
if (f.isRelation) continue;
|
|
5160
|
+
if (f.name.startsWith("@") || f.name.startsWith("//")) continue;
|
|
5161
|
+
const dbName = f.dbName || f.name;
|
|
5162
|
+
if (dbName !== f.name) {
|
|
5163
|
+
cols.push(`${alias}.${quote(dbName)} AS ${quote(f.name)}`);
|
|
5164
|
+
} else {
|
|
5165
|
+
cols.push(`${alias}.${quote(dbName)}`);
|
|
5057
5166
|
}
|
|
5058
|
-
return obj;
|
|
5059
5167
|
}
|
|
5060
|
-
|
|
5061
|
-
return `${dialect}:${modelName}:${method}:${JSON.stringify(canonical)}`;
|
|
5168
|
+
return cols.join(", ");
|
|
5062
5169
|
}
|
|
5063
|
-
function
|
|
5170
|
+
function getTableAndAlias(model, dialect) {
|
|
5064
5171
|
const tableName = buildTableReference(
|
|
5065
5172
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5066
5173
|
model.tableName,
|
|
5067
5174
|
dialect
|
|
5068
5175
|
);
|
|
5069
5176
|
const alias = makeAlias(model.tableName);
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5177
|
+
return { tableName, alias };
|
|
5178
|
+
}
|
|
5179
|
+
function buildSimpleQuery(model, dialect, where, params, suffix = "") {
|
|
5180
|
+
const { tableName, alias } = getTableAndAlias(model, dialect);
|
|
5181
|
+
const columns = buildColumnList(model, alias);
|
|
5182
|
+
const sql = `SELECT ${columns} FROM ${tableName} ${alias} ${where}${suffix}`;
|
|
5183
|
+
return { sql, params };
|
|
5184
|
+
}
|
|
5185
|
+
function norm(value) {
|
|
5186
|
+
return normalizeValue(value);
|
|
5187
|
+
}
|
|
5188
|
+
function isScalar(value) {
|
|
5189
|
+
return value !== null && typeof value !== "object";
|
|
5190
|
+
}
|
|
5191
|
+
function tryFindUniqueById(model, args, dialect) {
|
|
5192
|
+
const where = args.where;
|
|
5193
|
+
if (!isPlainObject(where) || Object.keys(where).length !== 1 || !("id" in where) || !isScalar(where.id) || args.select || args.include) {
|
|
5194
|
+
return null;
|
|
5195
|
+
}
|
|
5196
|
+
const idField = getIdField(model);
|
|
5197
|
+
if (!idField) return null;
|
|
5198
|
+
const { alias } = getTableAndAlias(model, dialect);
|
|
5199
|
+
const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(idField.dbName)} = ?` : `WHERE ${alias}.${quote(idField.dbName)} = $1`;
|
|
5200
|
+
return buildSimpleQuery(
|
|
5201
|
+
model,
|
|
5202
|
+
dialect,
|
|
5203
|
+
whereClause,
|
|
5204
|
+
[norm(where.id)],
|
|
5205
|
+
" LIMIT 1"
|
|
5080
5206
|
);
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5207
|
+
}
|
|
5208
|
+
function tryFindManyById(model, args, dialect) {
|
|
5209
|
+
const where = args.where;
|
|
5210
|
+
if (!isPlainObject(where) || Object.keys(where).length !== 1 || !("id" in where) || !isScalar(where.id) || args.select || args.include || args.orderBy || args.take || args.skip || args.distinct || args.cursor) {
|
|
5211
|
+
return null;
|
|
5212
|
+
}
|
|
5213
|
+
const idField = getIdField(model);
|
|
5214
|
+
if (!idField) return null;
|
|
5215
|
+
const { alias } = getTableAndAlias(model, dialect);
|
|
5216
|
+
const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(idField.dbName)} = ?` : `WHERE ${alias}.${quote(idField.dbName)} = $1`;
|
|
5217
|
+
return buildSimpleQuery(model, dialect, whereClause, [norm(where.id)]);
|
|
5218
|
+
}
|
|
5219
|
+
function tryCountAll(model, args, dialect) {
|
|
5220
|
+
const where = args.where;
|
|
5221
|
+
if (where && Object.keys(where).length > 0 || args.select || args.skip) {
|
|
5222
|
+
return null;
|
|
5223
|
+
}
|
|
5224
|
+
const { tableName, alias } = getTableAndAlias(model, dialect);
|
|
5225
|
+
const sql = `SELECT COUNT(*) AS ${quote("_count._all")} FROM ${tableName} ${alias}`;
|
|
5226
|
+
return { sql, params: [] };
|
|
5227
|
+
}
|
|
5228
|
+
function tryFindManyWithLimit(model, args, dialect) {
|
|
5229
|
+
const where = args.where;
|
|
5230
|
+
if (where && Object.keys(where).length > 0 || args.select || args.include || args.orderBy || args.skip || args.distinct || args.cursor || typeof args.take !== "number" || !Number.isInteger(args.take) || args.take <= 0) {
|
|
5231
|
+
return null;
|
|
5232
|
+
}
|
|
5233
|
+
const { tableName, alias } = getTableAndAlias(model, dialect);
|
|
5234
|
+
const columns = buildColumnList(model, alias);
|
|
5235
|
+
const sql = dialect === "sqlite" ? `SELECT ${columns} FROM ${tableName} ${alias} LIMIT ?` : `SELECT ${columns} FROM ${tableName} ${alias} LIMIT $1`;
|
|
5236
|
+
return { sql, params: [norm(args.take)] };
|
|
5237
|
+
}
|
|
5238
|
+
function tryFindFirstBySingleField(model, args, dialect) {
|
|
5239
|
+
const where = args.where;
|
|
5240
|
+
if (!isPlainObject(where) || Object.keys(where).length !== 1 || args.select || args.include || args.orderBy || args.skip) {
|
|
5241
|
+
return null;
|
|
5242
|
+
}
|
|
5243
|
+
const field = Object.keys(where)[0];
|
|
5244
|
+
const value = where[field];
|
|
5245
|
+
if (value === null || typeof value === "object") {
|
|
5246
|
+
return null;
|
|
5247
|
+
}
|
|
5248
|
+
const fieldDef = model.fields.find((f) => f.name === field);
|
|
5249
|
+
if (!fieldDef || fieldDef.isRelation) {
|
|
5250
|
+
return null;
|
|
5251
|
+
}
|
|
5252
|
+
const { alias } = getTableAndAlias(model, dialect);
|
|
5253
|
+
const columnName = fieldDef.dbName || field;
|
|
5254
|
+
const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(columnName)} = ?` : `WHERE ${alias}.${quote(columnName)} = $1`;
|
|
5255
|
+
return buildSimpleQuery(
|
|
5256
|
+
model,
|
|
5257
|
+
dialect,
|
|
5258
|
+
whereClause,
|
|
5259
|
+
[norm(value)],
|
|
5260
|
+
" LIMIT 1"
|
|
5261
|
+
);
|
|
5262
|
+
}
|
|
5263
|
+
function tryFindManyBySingleField(model, args, dialect) {
|
|
5264
|
+
const where = args.where;
|
|
5265
|
+
if (!isPlainObject(where) || Object.keys(where).length !== 1 || args.select || args.include || args.orderBy || args.take || args.skip || args.distinct || args.cursor) {
|
|
5266
|
+
return null;
|
|
5267
|
+
}
|
|
5268
|
+
const field = Object.keys(where)[0];
|
|
5269
|
+
const value = where[field];
|
|
5270
|
+
if (value === null || typeof value === "object") {
|
|
5271
|
+
return null;
|
|
5272
|
+
}
|
|
5273
|
+
const fieldDef = model.fields.find((f) => f.name === field);
|
|
5274
|
+
if (!fieldDef || fieldDef.isRelation) {
|
|
5275
|
+
return null;
|
|
5276
|
+
}
|
|
5277
|
+
const { alias } = getTableAndAlias(model, dialect);
|
|
5278
|
+
const columnName = fieldDef.dbName || field;
|
|
5279
|
+
const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(columnName)} = ?` : `WHERE ${alias}.${quote(columnName)} = $1`;
|
|
5280
|
+
return buildSimpleQuery(model, dialect, whereClause, [norm(value)]);
|
|
5281
|
+
}
|
|
5282
|
+
function tryFindManyAll(model, args, dialect) {
|
|
5283
|
+
const where = args.where;
|
|
5284
|
+
if (where && Object.keys(where).length > 0 || args.select || args.include || args.orderBy || args.take !== void 0 || args.skip !== void 0 || args.distinct || args.cursor) {
|
|
5285
|
+
return null;
|
|
5286
|
+
}
|
|
5287
|
+
const { tableName, alias } = getTableAndAlias(model, dialect);
|
|
5288
|
+
const columns = buildColumnList(model, alias);
|
|
5289
|
+
const sql = `SELECT ${columns} FROM ${tableName} ${alias}`;
|
|
5290
|
+
return { sql, params: [] };
|
|
5291
|
+
}
|
|
5292
|
+
function tryFastPath(model, method, args, dialect) {
|
|
5293
|
+
if (method === "findUnique") {
|
|
5294
|
+
return tryFindUniqueById(model, args, dialect);
|
|
5295
|
+
}
|
|
5296
|
+
if (method === "count") {
|
|
5297
|
+
return tryCountAll(model, args, dialect);
|
|
5298
|
+
}
|
|
5299
|
+
if (method === "findFirst") {
|
|
5300
|
+
return tryFindFirstBySingleField(model, args, dialect);
|
|
5301
|
+
}
|
|
5302
|
+
if (method === "findMany") {
|
|
5303
|
+
return tryFindManyById(model, args, dialect) || tryFindManyWithLimit(model, args, dialect) || tryFindManyBySingleField(model, args, dialect) || tryFindManyAll(model, args, dialect);
|
|
5304
|
+
}
|
|
5305
|
+
return null;
|
|
5306
|
+
}
|
|
5307
|
+
|
|
5308
|
+
// src/query-cache.ts
|
|
5309
|
+
var _hits, _misses;
|
|
5310
|
+
var QueryCacheStats = class {
|
|
5311
|
+
constructor() {
|
|
5312
|
+
__privateAdd(this, _hits, 0);
|
|
5313
|
+
__privateAdd(this, _misses, 0);
|
|
5314
|
+
}
|
|
5315
|
+
hit() {
|
|
5316
|
+
__privateWrapper(this, _hits)._++;
|
|
5317
|
+
}
|
|
5318
|
+
miss() {
|
|
5319
|
+
__privateWrapper(this, _misses)._++;
|
|
5320
|
+
}
|
|
5321
|
+
reset() {
|
|
5322
|
+
__privateSet(this, _hits, 0);
|
|
5323
|
+
__privateSet(this, _misses, 0);
|
|
5324
|
+
}
|
|
5325
|
+
get snapshot() {
|
|
5326
|
+
return Object.freeze({
|
|
5327
|
+
hits: __privateGet(this, _hits),
|
|
5328
|
+
misses: __privateGet(this, _misses),
|
|
5329
|
+
size: queryCache.size
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
5332
|
+
};
|
|
5333
|
+
_hits = new WeakMap();
|
|
5334
|
+
_misses = new WeakMap();
|
|
5335
|
+
var queryCache = createBoundedCache(1e3);
|
|
5336
|
+
var queryCacheStats = new QueryCacheStats();
|
|
5337
|
+
function makeAlias(name) {
|
|
5338
|
+
const base = name.toLowerCase().replace(/[^a-z0-9_]/g, "_").slice(0, 50);
|
|
5339
|
+
const safe = /^[a-z_]/.test(base) ? base : `_${base}`;
|
|
5340
|
+
return SQL_RESERVED_WORDS.has(safe) ? `${safe}_t` : safe;
|
|
5341
|
+
}
|
|
5342
|
+
function handleSingleQuote(sql, state) {
|
|
5343
|
+
const n = sql.length;
|
|
5344
|
+
let i = state.position;
|
|
5345
|
+
let out = state.output + sql[i];
|
|
5346
|
+
i++;
|
|
5347
|
+
while (i < n) {
|
|
5348
|
+
out += sql[i];
|
|
5349
|
+
if (sql.charCodeAt(i) === 39) {
|
|
5350
|
+
if (i + 1 < n && sql.charCodeAt(i + 1) === 39) {
|
|
5351
|
+
out += sql[i + 1];
|
|
5352
|
+
i += 2;
|
|
5353
|
+
continue;
|
|
5354
|
+
}
|
|
5355
|
+
return __spreadProps(__spreadValues({}, state), { mode: "normal", position: i + 1, output: out });
|
|
5356
|
+
}
|
|
5357
|
+
i++;
|
|
5358
|
+
}
|
|
5359
|
+
return __spreadProps(__spreadValues({}, state), { position: i, output: out });
|
|
5360
|
+
}
|
|
5361
|
+
function handleDoubleQuote(sql, state) {
|
|
5362
|
+
const n = sql.length;
|
|
5363
|
+
let i = state.position;
|
|
5364
|
+
let out = state.output + sql[i];
|
|
5365
|
+
i++;
|
|
5366
|
+
while (i < n) {
|
|
5367
|
+
out += sql[i];
|
|
5368
|
+
if (sql.charCodeAt(i) === 34) {
|
|
5369
|
+
if (i + 1 < n && sql.charCodeAt(i + 1) === 34) {
|
|
5370
|
+
out += sql[i + 1];
|
|
5371
|
+
i += 2;
|
|
5372
|
+
continue;
|
|
5373
|
+
}
|
|
5374
|
+
return __spreadProps(__spreadValues({}, state), { mode: "normal", position: i + 1, output: out });
|
|
5375
|
+
}
|
|
5376
|
+
i++;
|
|
5377
|
+
}
|
|
5378
|
+
return __spreadProps(__spreadValues({}, state), { position: i, output: out });
|
|
5379
|
+
}
|
|
5380
|
+
function extractParameterNumber(sql, startPos) {
|
|
5381
|
+
const n = sql.length;
|
|
5382
|
+
let j = startPos + 1;
|
|
5383
|
+
let num = 0;
|
|
5384
|
+
let hasDigit = false;
|
|
5385
|
+
while (j < n) {
|
|
5386
|
+
const d = sql.charCodeAt(j);
|
|
5387
|
+
if (d >= 48 && d <= 57) {
|
|
5388
|
+
num = num * 10 + (d - 48);
|
|
5389
|
+
hasDigit = true;
|
|
5390
|
+
j++;
|
|
5391
|
+
} else {
|
|
5392
|
+
break;
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
if (hasDigit && num >= 1) {
|
|
5396
|
+
return { num, nextPos: j };
|
|
5397
|
+
}
|
|
5398
|
+
return null;
|
|
5399
|
+
}
|
|
5400
|
+
function handleParameterSubstitution(sql, params, state) {
|
|
5401
|
+
const result = extractParameterNumber(sql, state.position);
|
|
5402
|
+
if (result) {
|
|
5403
|
+
return __spreadProps(__spreadValues({}, state), {
|
|
5404
|
+
position: result.nextPos,
|
|
5405
|
+
output: state.output + "?",
|
|
5406
|
+
reorderedParams: [...state.reorderedParams, params[result.num - 1]]
|
|
5407
|
+
});
|
|
5408
|
+
}
|
|
5409
|
+
return __spreadProps(__spreadValues({}, state), {
|
|
5410
|
+
position: state.position + 1,
|
|
5411
|
+
output: state.output + sql[state.position]
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
function toSqliteParams(sql, params) {
|
|
5415
|
+
const n = sql.length;
|
|
5416
|
+
let state = {
|
|
5417
|
+
mode: "normal",
|
|
5418
|
+
position: 0,
|
|
5419
|
+
output: "",
|
|
5420
|
+
reorderedParams: []
|
|
5421
|
+
};
|
|
5422
|
+
while (state.position < n) {
|
|
5423
|
+
const ch = sql.charCodeAt(state.position);
|
|
5424
|
+
if (state.mode === "single") {
|
|
5425
|
+
state = handleSingleQuote(sql, state);
|
|
5426
|
+
continue;
|
|
5427
|
+
}
|
|
5428
|
+
if (state.mode === "double") {
|
|
5429
|
+
state = handleDoubleQuote(sql, state);
|
|
5430
|
+
continue;
|
|
5431
|
+
}
|
|
5432
|
+
if (ch === 39) {
|
|
5433
|
+
state = __spreadProps(__spreadValues({}, state), { mode: "single" });
|
|
5434
|
+
continue;
|
|
5435
|
+
}
|
|
5436
|
+
if (ch === 34) {
|
|
5437
|
+
state = __spreadProps(__spreadValues({}, state), { mode: "double" });
|
|
5438
|
+
continue;
|
|
5439
|
+
}
|
|
5440
|
+
if (ch === 36) {
|
|
5441
|
+
state = handleParameterSubstitution(sql, params, state);
|
|
5442
|
+
continue;
|
|
5443
|
+
}
|
|
5444
|
+
state = __spreadProps(__spreadValues({}, state), {
|
|
5445
|
+
position: state.position + 1,
|
|
5446
|
+
output: state.output + sql[state.position]
|
|
5447
|
+
});
|
|
5448
|
+
}
|
|
5449
|
+
return { sql: state.output, params: state.reorderedParams };
|
|
5450
|
+
}
|
|
5451
|
+
function canonicalizeReplacer(key, value) {
|
|
5452
|
+
if (typeof value === "bigint") return `__bigint__${value.toString()}`;
|
|
5453
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
5454
|
+
const obj = value;
|
|
5455
|
+
const sorted = {};
|
|
5456
|
+
for (const k of Object.keys(obj).sort()) {
|
|
5457
|
+
sorted[k] = obj[k];
|
|
5458
|
+
}
|
|
5459
|
+
return sorted;
|
|
5460
|
+
}
|
|
5461
|
+
return value;
|
|
5462
|
+
}
|
|
5463
|
+
function canonicalizeQuery(modelName, method, args, dialect) {
|
|
5464
|
+
if (!args) return `${dialect}:${modelName}:${method}:{}`;
|
|
5465
|
+
return `${dialect}:${modelName}:${method}:${JSON.stringify(args, canonicalizeReplacer)}`;
|
|
5466
|
+
}
|
|
5467
|
+
function buildSQLFull(model, models, method, args, dialect) {
|
|
5468
|
+
const tableName = buildTableReference(
|
|
5469
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5470
|
+
model.tableName,
|
|
5471
|
+
dialect
|
|
5472
|
+
);
|
|
5473
|
+
const alias = makeAlias(model.tableName);
|
|
5474
|
+
const whereResult = buildWhereClause(
|
|
5475
|
+
args.where || {},
|
|
5476
|
+
{
|
|
5477
|
+
alias,
|
|
5478
|
+
schemaModels: models,
|
|
5479
|
+
model,
|
|
5480
|
+
path: ["where"],
|
|
5481
|
+
isSubquery: false,
|
|
5482
|
+
dialect
|
|
5483
|
+
}
|
|
5484
|
+
);
|
|
5485
|
+
const withMethod = __spreadProps(__spreadValues({}, args), { method });
|
|
5486
|
+
let result;
|
|
5487
|
+
switch (method) {
|
|
5488
|
+
case "aggregate":
|
|
5489
|
+
result = buildAggregateSql(
|
|
5490
|
+
withMethod,
|
|
5491
|
+
whereResult,
|
|
5492
|
+
tableName,
|
|
5493
|
+
alias,
|
|
5494
|
+
model
|
|
5495
|
+
);
|
|
5496
|
+
break;
|
|
5497
|
+
case "groupBy":
|
|
5498
|
+
result = buildGroupBySql(
|
|
5499
|
+
withMethod,
|
|
5500
|
+
whereResult,
|
|
5501
|
+
tableName,
|
|
5502
|
+
alias,
|
|
5503
|
+
model,
|
|
5504
|
+
dialect
|
|
5505
|
+
);
|
|
5506
|
+
break;
|
|
5507
|
+
case "count":
|
|
5508
|
+
result = buildCountSql(
|
|
5509
|
+
whereResult,
|
|
5510
|
+
tableName,
|
|
5511
|
+
alias,
|
|
5512
|
+
args.skip);
|
|
5513
|
+
break;
|
|
5514
|
+
default:
|
|
5111
5515
|
result = buildSelectSql({
|
|
5112
5516
|
method,
|
|
5113
5517
|
args: withMethod,
|
|
@@ -5122,207 +5526,211 @@ function buildSQLFull(model, models, method, args, dialect) {
|
|
|
5122
5526
|
}
|
|
5123
5527
|
function buildSQLWithCache(model, models, method, args, dialect) {
|
|
5124
5528
|
const cacheKey = canonicalizeQuery(model.name, method, args, dialect);
|
|
5125
|
-
const
|
|
5126
|
-
if (
|
|
5127
|
-
|
|
5128
|
-
return { sql:
|
|
5529
|
+
const cached = queryCache.get(cacheKey);
|
|
5530
|
+
if (cached) {
|
|
5531
|
+
queryCacheStats.hit();
|
|
5532
|
+
return { sql: cached.sql, params: [...cached.params] };
|
|
5533
|
+
}
|
|
5534
|
+
queryCacheStats.miss();
|
|
5535
|
+
const fastResult = tryFastPath(model, method, args, dialect);
|
|
5536
|
+
if (fastResult) {
|
|
5537
|
+
queryCache.set(cacheKey, {
|
|
5538
|
+
sql: fastResult.sql,
|
|
5539
|
+
params: [...fastResult.params]
|
|
5540
|
+
});
|
|
5541
|
+
return fastResult;
|
|
5129
5542
|
}
|
|
5130
5543
|
const result = buildSQLFull(model, models, method, args, dialect);
|
|
5131
|
-
queryCache.set(cacheKey, result.sql);
|
|
5132
|
-
queryCache.size;
|
|
5544
|
+
queryCache.set(cacheKey, { sql: result.sql, params: [...result.params] });
|
|
5133
5545
|
return result;
|
|
5134
5546
|
}
|
|
5135
5547
|
|
|
5136
5548
|
// src/batch.ts
|
|
5137
|
-
function
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5549
|
+
function quoteBatchIdent(id) {
|
|
5550
|
+
const raw = String(id);
|
|
5551
|
+
assertSafeAlias(raw);
|
|
5552
|
+
return `"${raw.replace(/"/g, '""')}"`;
|
|
5553
|
+
}
|
|
5554
|
+
function makeBatchAlias(i) {
|
|
5555
|
+
return `k${i}`;
|
|
5556
|
+
}
|
|
5557
|
+
function isDigit(charCode) {
|
|
5558
|
+
return charCode >= 48 && charCode <= 57;
|
|
5559
|
+
}
|
|
5560
|
+
function isAlphaNumericOrUnderscore(charCode) {
|
|
5561
|
+
return isDigit(charCode) || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122 || charCode === 95;
|
|
5562
|
+
}
|
|
5563
|
+
function readDollarTag(s, pos, n) {
|
|
5564
|
+
if (s.charCodeAt(pos) !== 36) return null;
|
|
5565
|
+
let j = pos + 1;
|
|
5566
|
+
while (j < n && isAlphaNumericOrUnderscore(s.charCodeAt(j))) {
|
|
5567
|
+
j++;
|
|
5568
|
+
}
|
|
5569
|
+
if (j < n && s.charCodeAt(j) === 36 && j > pos) {
|
|
5570
|
+
return s.slice(pos, j + 1);
|
|
5571
|
+
}
|
|
5572
|
+
if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
|
|
5573
|
+
return "$$";
|
|
5143
5574
|
}
|
|
5575
|
+
return null;
|
|
5144
5576
|
}
|
|
5145
|
-
function
|
|
5146
|
-
|
|
5147
|
-
if (
|
|
5148
|
-
|
|
5577
|
+
function parseParamPlaceholder(s, i, n, replace) {
|
|
5578
|
+
let j = i + 1;
|
|
5579
|
+
if (j >= n) {
|
|
5580
|
+
return { consumed: 1, output: s[i] };
|
|
5149
5581
|
}
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
throw new Error(`${label} must not contain whitespace`);
|
|
5582
|
+
const c1 = s.charCodeAt(j);
|
|
5583
|
+
if (!isDigit(c1)) {
|
|
5584
|
+
return { consumed: 1, output: s[i] };
|
|
5154
5585
|
}
|
|
5155
|
-
|
|
5156
|
-
|
|
5586
|
+
while (j < n && isDigit(s.charCodeAt(j))) {
|
|
5587
|
+
j++;
|
|
5157
5588
|
}
|
|
5158
|
-
|
|
5159
|
-
|
|
5589
|
+
const numStr = s.slice(i + 1, j);
|
|
5590
|
+
const oldIndex = Number(numStr);
|
|
5591
|
+
if (!Number.isInteger(oldIndex) || oldIndex < 1) {
|
|
5592
|
+
throw new Error(`Invalid param placeholder: $${numStr}`);
|
|
5160
5593
|
}
|
|
5594
|
+
return { consumed: j - i, output: replace(oldIndex) };
|
|
5161
5595
|
}
|
|
5162
|
-
function
|
|
5163
|
-
const
|
|
5164
|
-
|
|
5165
|
-
|
|
5596
|
+
function handleDollarInNormalMode(s, i, n, state, replace) {
|
|
5597
|
+
const tag = readDollarTag(s, i, n);
|
|
5598
|
+
if (tag) {
|
|
5599
|
+
return {
|
|
5600
|
+
consumed: tag.length,
|
|
5601
|
+
output: tag,
|
|
5602
|
+
newState: { mode: "dollar", dollarTag: tag }
|
|
5603
|
+
};
|
|
5604
|
+
}
|
|
5605
|
+
const placeholder = parseParamPlaceholder(s, i, n, replace);
|
|
5606
|
+
return __spreadProps(__spreadValues({}, placeholder), { newState: state });
|
|
5166
5607
|
}
|
|
5167
|
-
function
|
|
5168
|
-
|
|
5608
|
+
function handleCommentStart(s, i, n, ch) {
|
|
5609
|
+
if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
|
|
5610
|
+
return {
|
|
5611
|
+
consumed: 2,
|
|
5612
|
+
output: "--",
|
|
5613
|
+
newState: { mode: "lineComment", dollarTag: null }
|
|
5614
|
+
};
|
|
5615
|
+
}
|
|
5616
|
+
if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
|
|
5617
|
+
return {
|
|
5618
|
+
consumed: 2,
|
|
5619
|
+
output: "/*",
|
|
5620
|
+
newState: { mode: "blockComment", dollarTag: null }
|
|
5621
|
+
};
|
|
5622
|
+
}
|
|
5623
|
+
return null;
|
|
5624
|
+
}
|
|
5625
|
+
function processNormalMode(s, i, n, state, replace) {
|
|
5626
|
+
const ch = s.charCodeAt(i);
|
|
5627
|
+
if (ch === 39) {
|
|
5628
|
+
return {
|
|
5629
|
+
consumed: 1,
|
|
5630
|
+
output: s[i],
|
|
5631
|
+
newState: { mode: "single", dollarTag: null }
|
|
5632
|
+
};
|
|
5633
|
+
}
|
|
5634
|
+
if (ch === 34) {
|
|
5635
|
+
return {
|
|
5636
|
+
consumed: 1,
|
|
5637
|
+
output: s[i],
|
|
5638
|
+
newState: { mode: "double", dollarTag: null }
|
|
5639
|
+
};
|
|
5640
|
+
}
|
|
5641
|
+
const commentResult = handleCommentStart(s, i, n, ch);
|
|
5642
|
+
if (commentResult) return commentResult;
|
|
5643
|
+
if (ch === 36) {
|
|
5644
|
+
return handleDollarInNormalMode(s, i, n, state, replace);
|
|
5645
|
+
}
|
|
5646
|
+
return { consumed: 1, output: s[i], newState: state };
|
|
5647
|
+
}
|
|
5648
|
+
function processQuoteMode(s, i, n, quoteChar) {
|
|
5649
|
+
const ch = s.charCodeAt(i);
|
|
5650
|
+
if (ch === quoteChar) {
|
|
5651
|
+
if (i + 1 < n && s.charCodeAt(i + 1) === quoteChar) {
|
|
5652
|
+
return { consumed: 2, output: s[i] + s[i + 1], shouldExitMode: false };
|
|
5653
|
+
}
|
|
5654
|
+
return { consumed: 1, output: s[i], shouldExitMode: true };
|
|
5655
|
+
}
|
|
5656
|
+
return { consumed: 1, output: s[i], shouldExitMode: false };
|
|
5657
|
+
}
|
|
5658
|
+
function processBlockCommentMode(s, i, n) {
|
|
5659
|
+
const ch = s.charCodeAt(i);
|
|
5660
|
+
if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
|
|
5661
|
+
return { consumed: 2, output: "*/", shouldExitMode: true };
|
|
5662
|
+
}
|
|
5663
|
+
return { consumed: 1, output: s[i], shouldExitMode: false };
|
|
5664
|
+
}
|
|
5665
|
+
function processDollarMode(s, i, dollarTag) {
|
|
5666
|
+
if (s.slice(i, i + dollarTag.length) === dollarTag) {
|
|
5667
|
+
return {
|
|
5668
|
+
consumed: dollarTag.length,
|
|
5669
|
+
output: dollarTag,
|
|
5670
|
+
shouldExitMode: true
|
|
5671
|
+
};
|
|
5672
|
+
}
|
|
5673
|
+
return { consumed: 1, output: s[i], shouldExitMode: false };
|
|
5674
|
+
}
|
|
5675
|
+
function processLineCommentMode(ch) {
|
|
5676
|
+
return { shouldExitMode: ch === 10 };
|
|
5677
|
+
}
|
|
5678
|
+
function processCharacter(s, i, n, state, replace) {
|
|
5679
|
+
const ch = s.charCodeAt(i);
|
|
5680
|
+
switch (state.mode) {
|
|
5681
|
+
case "normal":
|
|
5682
|
+
return processNormalMode(s, i, n, state, replace);
|
|
5683
|
+
case "single":
|
|
5684
|
+
return processQuoteMode(s, i, n, 39);
|
|
5685
|
+
case "double":
|
|
5686
|
+
return processQuoteMode(s, i, n, 34);
|
|
5687
|
+
case "lineComment": {
|
|
5688
|
+
const result = processLineCommentMode(ch);
|
|
5689
|
+
return {
|
|
5690
|
+
consumed: 1,
|
|
5691
|
+
output: s[i],
|
|
5692
|
+
shouldExitMode: result.shouldExitMode
|
|
5693
|
+
};
|
|
5694
|
+
}
|
|
5695
|
+
case "blockComment":
|
|
5696
|
+
return processBlockCommentMode(s, i, n);
|
|
5697
|
+
case "dollar":
|
|
5698
|
+
return state.dollarTag ? processDollarMode(s, i, state.dollarTag) : { consumed: 1, output: s[i], shouldExitMode: false };
|
|
5699
|
+
default:
|
|
5700
|
+
return { consumed: 1, output: s[i], shouldExitMode: false };
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
function updateStateAfterProcessing(currentState, result) {
|
|
5704
|
+
if (result.newState) {
|
|
5705
|
+
return result.newState;
|
|
5706
|
+
}
|
|
5707
|
+
if (result.shouldExitMode) {
|
|
5708
|
+
return { mode: "normal", dollarTag: null };
|
|
5709
|
+
}
|
|
5710
|
+
return currentState;
|
|
5169
5711
|
}
|
|
5170
5712
|
function replacePgPlaceholders(sql, replace) {
|
|
5171
5713
|
const s = String(sql);
|
|
5172
5714
|
const n = s.length;
|
|
5173
5715
|
let i = 0;
|
|
5174
|
-
let
|
|
5175
|
-
let dollarTag = null;
|
|
5716
|
+
let state = { mode: "normal", dollarTag: null };
|
|
5176
5717
|
let out = "";
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
const c = s.charCodeAt(j);
|
|
5183
|
-
if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
|
|
5184
|
-
j++;
|
|
5185
|
-
continue;
|
|
5186
|
-
}
|
|
5187
|
-
break;
|
|
5188
|
-
}
|
|
5189
|
-
if (j < n && s.charCodeAt(j) === 36 && j > pos) {
|
|
5190
|
-
return s.slice(pos, j + 1);
|
|
5191
|
-
}
|
|
5192
|
-
if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
|
|
5193
|
-
return "$$";
|
|
5194
|
-
}
|
|
5195
|
-
return null;
|
|
5196
|
-
};
|
|
5197
|
-
while (i < n) {
|
|
5198
|
-
const ch = s.charCodeAt(i);
|
|
5199
|
-
if (mode === "normal") {
|
|
5200
|
-
if (ch === 39) {
|
|
5201
|
-
out += s[i];
|
|
5202
|
-
mode = "single";
|
|
5203
|
-
i++;
|
|
5204
|
-
continue;
|
|
5205
|
-
}
|
|
5206
|
-
if (ch === 34) {
|
|
5207
|
-
out += s[i];
|
|
5208
|
-
mode = "double";
|
|
5209
|
-
i++;
|
|
5210
|
-
continue;
|
|
5211
|
-
}
|
|
5212
|
-
if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
|
|
5213
|
-
out += "--";
|
|
5214
|
-
mode = "lineComment";
|
|
5215
|
-
i += 2;
|
|
5216
|
-
continue;
|
|
5217
|
-
}
|
|
5218
|
-
if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
|
|
5219
|
-
out += "/*";
|
|
5220
|
-
mode = "blockComment";
|
|
5221
|
-
i += 2;
|
|
5222
|
-
continue;
|
|
5223
|
-
}
|
|
5224
|
-
if (ch === 36) {
|
|
5225
|
-
const tag = readDollarTag(i);
|
|
5226
|
-
if (tag) {
|
|
5227
|
-
out += tag;
|
|
5228
|
-
mode = "dollar";
|
|
5229
|
-
dollarTag = tag;
|
|
5230
|
-
i += tag.length;
|
|
5231
|
-
continue;
|
|
5232
|
-
}
|
|
5233
|
-
let j = i + 1;
|
|
5234
|
-
if (j < n) {
|
|
5235
|
-
const c1 = s.charCodeAt(j);
|
|
5236
|
-
if (c1 >= 48 && c1 <= 57) {
|
|
5237
|
-
while (j < n) {
|
|
5238
|
-
const cj = s.charCodeAt(j);
|
|
5239
|
-
if (cj >= 48 && cj <= 57) {
|
|
5240
|
-
j++;
|
|
5241
|
-
continue;
|
|
5242
|
-
}
|
|
5243
|
-
break;
|
|
5244
|
-
}
|
|
5245
|
-
const numStr = s.slice(i + 1, j);
|
|
5246
|
-
const oldIndex = Number(numStr);
|
|
5247
|
-
if (!Number.isInteger(oldIndex) || oldIndex < 1) {
|
|
5248
|
-
throw new Error(`Invalid param placeholder: $${numStr}`);
|
|
5249
|
-
}
|
|
5250
|
-
out += replace(oldIndex);
|
|
5251
|
-
i = j;
|
|
5252
|
-
continue;
|
|
5253
|
-
}
|
|
5254
|
-
}
|
|
5255
|
-
}
|
|
5256
|
-
out += s[i];
|
|
5257
|
-
i++;
|
|
5258
|
-
continue;
|
|
5259
|
-
}
|
|
5260
|
-
if (mode === "single") {
|
|
5261
|
-
out += s[i];
|
|
5262
|
-
if (ch === 39) {
|
|
5263
|
-
if (i + 1 < n && s.charCodeAt(i + 1) === 39) {
|
|
5264
|
-
out += s[i + 1];
|
|
5265
|
-
i += 2;
|
|
5266
|
-
continue;
|
|
5267
|
-
}
|
|
5268
|
-
mode = "normal";
|
|
5269
|
-
i++;
|
|
5270
|
-
continue;
|
|
5271
|
-
}
|
|
5272
|
-
i++;
|
|
5273
|
-
continue;
|
|
5274
|
-
}
|
|
5275
|
-
if (mode === "double") {
|
|
5276
|
-
out += s[i];
|
|
5277
|
-
if (ch === 34) {
|
|
5278
|
-
if (i + 1 < n && s.charCodeAt(i + 1) === 34) {
|
|
5279
|
-
out += s[i + 1];
|
|
5280
|
-
i += 2;
|
|
5281
|
-
continue;
|
|
5282
|
-
}
|
|
5283
|
-
mode = "normal";
|
|
5284
|
-
i++;
|
|
5285
|
-
continue;
|
|
5286
|
-
}
|
|
5287
|
-
i++;
|
|
5288
|
-
continue;
|
|
5289
|
-
}
|
|
5290
|
-
if (mode === "lineComment") {
|
|
5291
|
-
out += s[i];
|
|
5292
|
-
if (ch === 10) {
|
|
5293
|
-
mode = "normal";
|
|
5294
|
-
}
|
|
5295
|
-
i++;
|
|
5296
|
-
continue;
|
|
5297
|
-
}
|
|
5298
|
-
if (mode === "blockComment") {
|
|
5299
|
-
if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
|
|
5300
|
-
out += "*/";
|
|
5301
|
-
i += 2;
|
|
5302
|
-
mode = "normal";
|
|
5303
|
-
continue;
|
|
5304
|
-
}
|
|
5305
|
-
out += s[i];
|
|
5306
|
-
i++;
|
|
5307
|
-
continue;
|
|
5308
|
-
}
|
|
5309
|
-
if (mode === "dollar") {
|
|
5310
|
-
if (dollarTag && startsWith(i, dollarTag)) {
|
|
5311
|
-
out += dollarTag;
|
|
5312
|
-
i += dollarTag.length;
|
|
5313
|
-
mode = "normal";
|
|
5314
|
-
dollarTag = null;
|
|
5315
|
-
continue;
|
|
5316
|
-
}
|
|
5317
|
-
out += s[i];
|
|
5318
|
-
i++;
|
|
5319
|
-
continue;
|
|
5320
|
-
}
|
|
5321
|
-
out += s[i];
|
|
5322
|
-
i++;
|
|
5718
|
+
while (i < n) {
|
|
5719
|
+
const result = processCharacter(s, i, n, state, replace);
|
|
5720
|
+
out += result.output;
|
|
5721
|
+
i += result.consumed;
|
|
5722
|
+
state = updateStateAfterProcessing(state, result);
|
|
5323
5723
|
}
|
|
5324
5724
|
return out;
|
|
5325
5725
|
}
|
|
5726
|
+
function containsPgPlaceholder(sql) {
|
|
5727
|
+
let found = false;
|
|
5728
|
+
replacePgPlaceholders(sql, (oldIndex) => {
|
|
5729
|
+
found = true;
|
|
5730
|
+
return `$${oldIndex}`;
|
|
5731
|
+
});
|
|
5732
|
+
return found;
|
|
5733
|
+
}
|
|
5326
5734
|
function reindexParams(sql, params, offset) {
|
|
5327
5735
|
if (!Number.isInteger(offset) || offset < 0) {
|
|
5328
5736
|
throw new Error(`Invalid param offset: ${offset}`);
|
|
@@ -5346,7 +5754,7 @@ function reindexParams(sql, params, offset) {
|
|
|
5346
5754
|
return { sql: reindexed, params: newParams };
|
|
5347
5755
|
}
|
|
5348
5756
|
function wrapQueryForMethod(method, cteName, resultAlias) {
|
|
5349
|
-
const outKey =
|
|
5757
|
+
const outKey = quoteBatchIdent(resultAlias);
|
|
5350
5758
|
switch (method) {
|
|
5351
5759
|
case "findMany":
|
|
5352
5760
|
case "groupBy":
|
|
@@ -5364,24 +5772,70 @@ function wrapQueryForMethod(method, cteName, resultAlias) {
|
|
|
5364
5772
|
}
|
|
5365
5773
|
function isAllCountQueries(queries, keys) {
|
|
5366
5774
|
var _a;
|
|
5367
|
-
for (
|
|
5368
|
-
if (((_a = queries[
|
|
5775
|
+
for (const key of keys) {
|
|
5776
|
+
if (((_a = queries[key]) == null ? void 0 : _a.method) !== "count") return false;
|
|
5369
5777
|
}
|
|
5370
5778
|
return true;
|
|
5371
5779
|
}
|
|
5372
5780
|
function looksTooComplexForFilter(sql) {
|
|
5373
5781
|
const s = sql.toLowerCase();
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5782
|
+
const complexKeywords = [
|
|
5783
|
+
" group by ",
|
|
5784
|
+
" having ",
|
|
5785
|
+
" union ",
|
|
5786
|
+
" intersect ",
|
|
5787
|
+
" except ",
|
|
5788
|
+
" window ",
|
|
5789
|
+
" distinct "
|
|
5790
|
+
];
|
|
5791
|
+
return complexKeywords.some((keyword) => s.includes(keyword));
|
|
5382
5792
|
}
|
|
5383
|
-
function
|
|
5384
|
-
|
|
5793
|
+
function skipWhitespace(s, i) {
|
|
5794
|
+
while (i < s.length && /\s/.test(s[i])) {
|
|
5795
|
+
i++;
|
|
5796
|
+
}
|
|
5797
|
+
return i;
|
|
5798
|
+
}
|
|
5799
|
+
function matchKeyword(s, i, keyword) {
|
|
5800
|
+
const lower = s.slice(i).toLowerCase();
|
|
5801
|
+
if (!lower.startsWith(keyword)) return -1;
|
|
5802
|
+
const endPos = i + keyword.length;
|
|
5803
|
+
if (endPos < s.length && /[a-z0-9_]/i.test(s[endPos])) return -1;
|
|
5804
|
+
return endPos;
|
|
5805
|
+
}
|
|
5806
|
+
function parseQuotedIdentifier2(s, i) {
|
|
5807
|
+
let j = i + 1;
|
|
5808
|
+
while (j < s.length) {
|
|
5809
|
+
if (s[j] === '"') {
|
|
5810
|
+
if (j + 1 < s.length && s[j + 1] === '"') {
|
|
5811
|
+
j += 2;
|
|
5812
|
+
continue;
|
|
5813
|
+
}
|
|
5814
|
+
return { value: s.slice(i, j + 1), endPos: j + 1 };
|
|
5815
|
+
}
|
|
5816
|
+
j++;
|
|
5817
|
+
}
|
|
5818
|
+
return null;
|
|
5819
|
+
}
|
|
5820
|
+
function parseUnquotedIdentifier2(s, i) {
|
|
5821
|
+
if (!/[a-z_]/i.test(s[i])) return null;
|
|
5822
|
+
let j = i;
|
|
5823
|
+
while (j < s.length && /[a-z0-9_.]/i.test(s[j])) {
|
|
5824
|
+
j++;
|
|
5825
|
+
}
|
|
5826
|
+
return { value: s.slice(i, j), endPos: j };
|
|
5827
|
+
}
|
|
5828
|
+
function parseIdentifier(s, i) {
|
|
5829
|
+
if (i >= s.length) return null;
|
|
5830
|
+
if (s[i] === '"') {
|
|
5831
|
+
return parseQuotedIdentifier2(s, i);
|
|
5832
|
+
}
|
|
5833
|
+
return parseUnquotedIdentifier2(s, i);
|
|
5834
|
+
}
|
|
5835
|
+
function findFromClauseEnd(s, fromStart) {
|
|
5836
|
+
const lower = s.slice(fromStart).toLowerCase();
|
|
5837
|
+
const whereIdx = lower.indexOf(" where ");
|
|
5838
|
+
return whereIdx === -1 ? s.length : fromStart + whereIdx;
|
|
5385
5839
|
}
|
|
5386
5840
|
function parseSimpleCountSql(sql) {
|
|
5387
5841
|
const trimmed = sql.trim().replace(/;$/, "").trim();
|
|
@@ -5389,111 +5843,181 @@ function parseSimpleCountSql(sql) {
|
|
|
5389
5843
|
if (!lower.startsWith("select")) return null;
|
|
5390
5844
|
if (!lower.includes("count(*)")) return null;
|
|
5391
5845
|
if (looksTooComplexForFilter(trimmed)) return null;
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
);
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5846
|
+
let pos = matchKeyword(trimmed, 0, "select");
|
|
5847
|
+
if (pos === -1) return null;
|
|
5848
|
+
pos = skipWhitespace(trimmed, pos);
|
|
5849
|
+
const countMatch = trimmed.slice(pos).match(/^count\(\*\)/i);
|
|
5850
|
+
if (!countMatch) return null;
|
|
5851
|
+
pos += countMatch[0].length;
|
|
5852
|
+
pos = skipWhitespace(trimmed, pos);
|
|
5853
|
+
const castMatch = trimmed.slice(pos).match(/^::\s*\w+/);
|
|
5854
|
+
if (castMatch) {
|
|
5855
|
+
pos += castMatch[0].length;
|
|
5856
|
+
pos = skipWhitespace(trimmed, pos);
|
|
5857
|
+
}
|
|
5858
|
+
const asPos = matchKeyword(trimmed, pos, "as");
|
|
5859
|
+
if (asPos === -1) return null;
|
|
5860
|
+
pos = skipWhitespace(trimmed, asPos);
|
|
5861
|
+
const ident = parseIdentifier(trimmed, pos);
|
|
5862
|
+
if (!ident) return null;
|
|
5863
|
+
pos = skipWhitespace(trimmed, ident.endPos);
|
|
5864
|
+
const fromPos = matchKeyword(trimmed, pos, "from");
|
|
5865
|
+
if (fromPos === -1) return null;
|
|
5866
|
+
pos = skipWhitespace(trimmed, fromPos);
|
|
5867
|
+
const fromEnd = findFromClauseEnd(trimmed, pos);
|
|
5868
|
+
const fromSql = trimmed.slice(pos, fromEnd).trim();
|
|
5398
5869
|
if (!fromSql) return null;
|
|
5870
|
+
let whereSql = null;
|
|
5871
|
+
if (fromEnd < trimmed.length) {
|
|
5872
|
+
const adjustedPos = skipWhitespace(trimmed, fromEnd);
|
|
5873
|
+
const wherePos = matchKeyword(trimmed, adjustedPos, "where");
|
|
5874
|
+
if (wherePos !== -1) {
|
|
5875
|
+
whereSql = trimmed.slice(skipWhitespace(trimmed, wherePos)).trim();
|
|
5876
|
+
}
|
|
5877
|
+
}
|
|
5399
5878
|
return { fromSql, whereSql };
|
|
5400
5879
|
}
|
|
5401
|
-
function
|
|
5880
|
+
function processCountQuery(item, model, models, dialect, sharedFrom, localParams) {
|
|
5881
|
+
const built = buildSQLWithCache(model, models, "count", item.args, dialect);
|
|
5882
|
+
const parsed = parseSimpleCountSql(built.sql);
|
|
5883
|
+
if (!parsed) return null;
|
|
5884
|
+
if (containsPgPlaceholder(parsed.fromSql)) return null;
|
|
5885
|
+
const currentFrom = parsed.fromSql;
|
|
5886
|
+
if (sharedFrom !== null && sharedFrom !== currentFrom) return null;
|
|
5887
|
+
if (!parsed.whereSql) {
|
|
5888
|
+
if (built.params.length > 0) return null;
|
|
5889
|
+
return {
|
|
5890
|
+
expression: `count(*) AS ${quoteBatchIdent(item.alias)}`,
|
|
5891
|
+
reindexedParams: [],
|
|
5892
|
+
sharedFrom: currentFrom
|
|
5893
|
+
};
|
|
5894
|
+
}
|
|
5895
|
+
const re = reindexParams(parsed.whereSql, built.params, localParams.length);
|
|
5896
|
+
return {
|
|
5897
|
+
expression: `count(*) FILTER (WHERE ${re.sql}) AS ${quoteBatchIdent(item.alias)}`,
|
|
5898
|
+
reindexedParams: re.params,
|
|
5899
|
+
sharedFrom: currentFrom
|
|
5900
|
+
};
|
|
5901
|
+
}
|
|
5902
|
+
function buildCountSubqueriesForModel(items, model, models, dialect, aliasIndex) {
|
|
5903
|
+
let sharedFrom = null;
|
|
5904
|
+
const expressions = [];
|
|
5905
|
+
const localParams = [];
|
|
5906
|
+
const localKeys = [];
|
|
5907
|
+
const localAliases = [];
|
|
5908
|
+
for (const item of items) {
|
|
5909
|
+
const result = processCountQuery(
|
|
5910
|
+
item,
|
|
5911
|
+
model,
|
|
5912
|
+
models,
|
|
5913
|
+
dialect,
|
|
5914
|
+
sharedFrom,
|
|
5915
|
+
localParams
|
|
5916
|
+
);
|
|
5917
|
+
if (!result) return null;
|
|
5918
|
+
sharedFrom = result.sharedFrom;
|
|
5919
|
+
expressions.push(result.expression);
|
|
5920
|
+
for (const param of result.reindexedParams) {
|
|
5921
|
+
localParams.push(param);
|
|
5922
|
+
}
|
|
5923
|
+
localKeys.push(item.key);
|
|
5924
|
+
localAliases.push(item.alias);
|
|
5925
|
+
}
|
|
5926
|
+
if (!sharedFrom) return null;
|
|
5927
|
+
const alias = `m_${aliasIndex}`;
|
|
5928
|
+
const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
|
|
5929
|
+
return {
|
|
5930
|
+
alias,
|
|
5931
|
+
sql: subSql,
|
|
5932
|
+
params: localParams,
|
|
5933
|
+
keys: localKeys,
|
|
5934
|
+
aliases: localAliases
|
|
5935
|
+
};
|
|
5936
|
+
}
|
|
5937
|
+
function groupQueriesByModel(queries, keys, aliasesByKey) {
|
|
5402
5938
|
const modelGroups = /* @__PURE__ */ new Map();
|
|
5403
|
-
for (
|
|
5404
|
-
const key = keys[i];
|
|
5939
|
+
for (const key of keys) {
|
|
5405
5940
|
const q = queries[key];
|
|
5406
5941
|
const alias = aliasesByKey.get(key);
|
|
5407
5942
|
if (!alias) return null;
|
|
5408
|
-
if (!modelGroups.has(q.model))
|
|
5409
|
-
|
|
5943
|
+
if (!modelGroups.has(q.model)) {
|
|
5944
|
+
modelGroups.set(q.model, []);
|
|
5945
|
+
}
|
|
5946
|
+
const items = modelGroups.get(q.model);
|
|
5947
|
+
if (items) {
|
|
5948
|
+
items.push({
|
|
5949
|
+
key,
|
|
5950
|
+
alias,
|
|
5951
|
+
args: q.args || {}
|
|
5952
|
+
});
|
|
5953
|
+
}
|
|
5410
5954
|
}
|
|
5955
|
+
return modelGroups;
|
|
5956
|
+
}
|
|
5957
|
+
function buildSubqueriesFromGroups(modelGroups, modelMap, models, dialect) {
|
|
5411
5958
|
const subqueries = [];
|
|
5412
5959
|
let aliasIndex = 0;
|
|
5413
5960
|
for (const [modelName, items] of modelGroups) {
|
|
5414
5961
|
const model = modelMap.get(modelName);
|
|
5415
5962
|
if (!model) return null;
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
if (!parsed) return null;
|
|
5426
|
-
if (containsPgPlaceholder(parsed.fromSql)) {
|
|
5427
|
-
return null;
|
|
5428
|
-
}
|
|
5429
|
-
if (!parsed.whereSql) {
|
|
5430
|
-
if (built.params.length > 0) return null;
|
|
5431
|
-
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5432
|
-
if (sharedFrom !== parsed.fromSql) return null;
|
|
5433
|
-
expressions.push(`count(*) AS ${quoteIdent(alias2)}`);
|
|
5434
|
-
localKeys.push(key);
|
|
5435
|
-
localAliases.push(alias2);
|
|
5436
|
-
continue;
|
|
5437
|
-
}
|
|
5438
|
-
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5439
|
-
if (sharedFrom !== parsed.fromSql) return null;
|
|
5440
|
-
const re = reindexParams(
|
|
5441
|
-
parsed.whereSql,
|
|
5442
|
-
built.params,
|
|
5443
|
-
localParams.length
|
|
5444
|
-
);
|
|
5445
|
-
for (let p = 0; p < re.params.length; p++) localParams.push(re.params[p]);
|
|
5446
|
-
expressions.push(
|
|
5447
|
-
`count(*) FILTER (WHERE ${re.sql}) AS ${quoteIdent(alias2)}`
|
|
5448
|
-
);
|
|
5449
|
-
localKeys.push(key);
|
|
5450
|
-
localAliases.push(alias2);
|
|
5451
|
-
}
|
|
5452
|
-
if (!sharedFrom) return null;
|
|
5453
|
-
const alias = `m_${aliasIndex++}`;
|
|
5454
|
-
const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
|
|
5455
|
-
subqueries.push({
|
|
5456
|
-
alias,
|
|
5457
|
-
sql: subSql,
|
|
5458
|
-
params: localParams,
|
|
5459
|
-
keys: localKeys,
|
|
5460
|
-
aliases: localAliases
|
|
5461
|
-
});
|
|
5963
|
+
const subquery = buildCountSubqueriesForModel(
|
|
5964
|
+
items,
|
|
5965
|
+
model,
|
|
5966
|
+
models,
|
|
5967
|
+
dialect,
|
|
5968
|
+
aliasIndex++
|
|
5969
|
+
);
|
|
5970
|
+
if (!subquery) return null;
|
|
5971
|
+
subqueries.push(subquery);
|
|
5462
5972
|
}
|
|
5463
|
-
|
|
5973
|
+
return subqueries.length > 0 ? subqueries : null;
|
|
5974
|
+
}
|
|
5975
|
+
function reindexSubqueries(subqueries) {
|
|
5464
5976
|
let offset = 0;
|
|
5465
5977
|
const rewrittenSubs = [];
|
|
5466
5978
|
const finalParams = [];
|
|
5467
|
-
for (
|
|
5468
|
-
const sq = subqueries[i];
|
|
5979
|
+
for (const sq of subqueries) {
|
|
5469
5980
|
const re = reindexParams(sq.sql, sq.params, offset);
|
|
5470
5981
|
offset += re.params.length;
|
|
5471
5982
|
rewrittenSubs.push(re.sql);
|
|
5472
|
-
for (
|
|
5983
|
+
for (const p of re.params) {
|
|
5984
|
+
finalParams.push(p);
|
|
5985
|
+
}
|
|
5473
5986
|
}
|
|
5987
|
+
return { sql: rewrittenSubs, params: finalParams };
|
|
5988
|
+
}
|
|
5989
|
+
function buildSelectParts(subqueries) {
|
|
5474
5990
|
const selectParts = [];
|
|
5475
|
-
for (
|
|
5476
|
-
const sq
|
|
5477
|
-
for (let k = 0; k < sq.aliases.length; k++) {
|
|
5478
|
-
const outAlias = sq.aliases[k];
|
|
5991
|
+
for (const sq of subqueries) {
|
|
5992
|
+
for (const outAlias of sq.aliases) {
|
|
5479
5993
|
selectParts.push(
|
|
5480
|
-
`${sq.alias}.${
|
|
5994
|
+
`${sq.alias}.${quoteBatchIdent(outAlias)} AS ${quoteBatchIdent(outAlias)}`
|
|
5481
5995
|
);
|
|
5482
5996
|
}
|
|
5483
5997
|
}
|
|
5998
|
+
return selectParts;
|
|
5999
|
+
}
|
|
6000
|
+
function buildMergedCountBatchSql(queries, keys, aliasesByKey, modelMap, models, dialect) {
|
|
6001
|
+
const modelGroups = groupQueriesByModel(queries, keys, aliasesByKey);
|
|
6002
|
+
if (!modelGroups) return null;
|
|
6003
|
+
const subqueries = buildSubqueriesFromGroups(
|
|
6004
|
+
modelGroups,
|
|
6005
|
+
modelMap,
|
|
6006
|
+
models,
|
|
6007
|
+
dialect
|
|
6008
|
+
);
|
|
6009
|
+
if (!subqueries) return null;
|
|
6010
|
+
const { sql: rewrittenSubs, params: finalParams } = reindexSubqueries(subqueries);
|
|
6011
|
+
const selectParts = buildSelectParts(subqueries);
|
|
5484
6012
|
const fromSql = rewrittenSubs.join(" CROSS JOIN ");
|
|
5485
6013
|
const sql = `SELECT ${selectParts.join(", ")} FROM ${fromSql}`;
|
|
5486
|
-
const aliases = keys.map((k) =>
|
|
6014
|
+
const aliases = keys.map((k) => {
|
|
6015
|
+
var _a;
|
|
6016
|
+
return (_a = aliasesByKey.get(k)) != null ? _a : "";
|
|
6017
|
+
});
|
|
5487
6018
|
return { sql, params: finalParams, keys, aliases };
|
|
5488
6019
|
}
|
|
5489
|
-
function
|
|
5490
|
-
const keys = Object.keys(queries);
|
|
5491
|
-
if (keys.length === 0) {
|
|
5492
|
-
throw new Error("buildBatchSql requires at least one query");
|
|
5493
|
-
}
|
|
5494
|
-
if (dialect !== "postgres") {
|
|
5495
|
-
throw new Error("Batch queries are only supported for postgres dialect");
|
|
5496
|
-
}
|
|
6020
|
+
function buildAliasesForKeys(keys) {
|
|
5497
6021
|
const aliases = new Array(keys.length);
|
|
5498
6022
|
const aliasesByKey = /* @__PURE__ */ new Map();
|
|
5499
6023
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -5501,17 +6025,9 @@ function buildBatchSql(queries, modelMap, models, dialect) {
|
|
|
5501
6025
|
aliases[i] = a;
|
|
5502
6026
|
aliasesByKey.set(keys[i], a);
|
|
5503
6027
|
}
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
keys,
|
|
5508
|
-
aliasesByKey,
|
|
5509
|
-
modelMap,
|
|
5510
|
-
models,
|
|
5511
|
-
dialect
|
|
5512
|
-
);
|
|
5513
|
-
if (merged) return merged;
|
|
5514
|
-
}
|
|
6028
|
+
return { aliases, aliasesByKey };
|
|
6029
|
+
}
|
|
6030
|
+
function buildRegularBatchQueries(queries, keys, aliases, modelMap, models, dialect) {
|
|
5515
6031
|
const ctes = new Array(keys.length);
|
|
5516
6032
|
const selects = new Array(keys.length);
|
|
5517
6033
|
const allParams = [];
|
|
@@ -5536,541 +6052,51 @@ function buildBatchSql(queries, modelMap, models, dialect) {
|
|
|
5536
6052
|
queryParams,
|
|
5537
6053
|
allParams.length
|
|
5538
6054
|
);
|
|
5539
|
-
for (
|
|
5540
|
-
allParams.push(
|
|
6055
|
+
for (const p of reindexedParams) {
|
|
6056
|
+
allParams.push(p);
|
|
5541
6057
|
}
|
|
5542
6058
|
const cteName = `batch_${i}`;
|
|
5543
6059
|
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5544
6060
|
selects[i] = wrapQueryForMethod(query.method, cteName, aliases[i]);
|
|
5545
6061
|
}
|
|
5546
6062
|
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5547
|
-
return { sql, params: allParams
|
|
6063
|
+
return { sql, params: allParams };
|
|
5548
6064
|
}
|
|
5549
|
-
function
|
|
5550
|
-
|
|
5551
|
-
|
|
6065
|
+
function buildBatchSql(queries, modelMap, models, dialect) {
|
|
6066
|
+
const keys = Object.keys(queries);
|
|
6067
|
+
if (keys.length === 0) {
|
|
6068
|
+
throw new Error("buildBatchSql requires at least one query");
|
|
5552
6069
|
}
|
|
5553
6070
|
if (dialect !== "postgres") {
|
|
5554
|
-
throw new Error(
|
|
5555
|
-
"Batch count queries are only supported for postgres dialect"
|
|
5556
|
-
);
|
|
6071
|
+
throw new Error("Batch queries are only supported for postgres dialect");
|
|
5557
6072
|
}
|
|
5558
|
-
const
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
throw new Error(
|
|
5566
|
-
`Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5567
|
-
);
|
|
5568
|
-
}
|
|
5569
|
-
const { sql: querySql, params: queryParams } = buildSQLWithCache(
|
|
5570
|
-
model,
|
|
6073
|
+
const { aliases, aliasesByKey } = buildAliasesForKeys(keys);
|
|
6074
|
+
if (isAllCountQueries(queries, keys)) {
|
|
6075
|
+
const merged = buildMergedCountBatchSql(
|
|
6076
|
+
queries,
|
|
6077
|
+
keys,
|
|
6078
|
+
aliasesByKey,
|
|
6079
|
+
modelMap,
|
|
5571
6080
|
models,
|
|
5572
|
-
"count",
|
|
5573
|
-
query.args || {},
|
|
5574
6081
|
dialect
|
|
5575
6082
|
);
|
|
5576
|
-
|
|
5577
|
-
querySql,
|
|
5578
|
-
queryParams,
|
|
5579
|
-
allParams.length
|
|
5580
|
-
);
|
|
5581
|
-
for (let p = 0; p < reindexedParams.length; p++) {
|
|
5582
|
-
allParams.push(reindexedParams[p]);
|
|
5583
|
-
}
|
|
5584
|
-
const cteName = `count_${i}`;
|
|
5585
|
-
const resultKey = `count_${i}`;
|
|
5586
|
-
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5587
|
-
selects[i] = `(SELECT * FROM ${cteName}) AS ${quoteIdent(resultKey)}`;
|
|
5588
|
-
}
|
|
5589
|
-
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5590
|
-
return { sql, params: allParams };
|
|
5591
|
-
}
|
|
5592
|
-
function looksLikeJsonString(s) {
|
|
5593
|
-
const t = s.trim();
|
|
5594
|
-
if (t.length === 0) return false;
|
|
5595
|
-
const c0 = t.charCodeAt(0);
|
|
5596
|
-
const cN = t.charCodeAt(t.length - 1);
|
|
5597
|
-
if (c0 === 123 && cN === 125) return true;
|
|
5598
|
-
if (c0 === 91 && cN === 93) return true;
|
|
5599
|
-
if (t === "null" || t === "true" || t === "false") return true;
|
|
5600
|
-
return false;
|
|
5601
|
-
}
|
|
5602
|
-
function parseJsonValue(value) {
|
|
5603
|
-
if (typeof value !== "string") return value;
|
|
5604
|
-
if (!looksLikeJsonString(value)) return value;
|
|
5605
|
-
try {
|
|
5606
|
-
return JSON.parse(value);
|
|
5607
|
-
} catch (e) {
|
|
5608
|
-
return value;
|
|
5609
|
-
}
|
|
5610
|
-
}
|
|
5611
|
-
function parseCountValue(value) {
|
|
5612
|
-
if (value === null || value === void 0) return 0;
|
|
5613
|
-
if (typeof value === "number") return value;
|
|
5614
|
-
if (typeof value === "bigint") {
|
|
5615
|
-
const n = Number(value);
|
|
5616
|
-
return Number.isSafeInteger(n) ? n : 0;
|
|
5617
|
-
}
|
|
5618
|
-
if (typeof value === "string") {
|
|
5619
|
-
const n = Number.parseInt(value, 10);
|
|
5620
|
-
return Number.isFinite(n) ? n : 0;
|
|
5621
|
-
}
|
|
5622
|
-
if (typeof value === "object") {
|
|
5623
|
-
const obj = value;
|
|
5624
|
-
const countKey = Object.prototype.hasOwnProperty.call(obj, "count") ? "count" : Object.prototype.hasOwnProperty.call(obj, "_count") ? "_count" : Object.keys(obj).find((k) => k.endsWith("_count"));
|
|
5625
|
-
if (countKey !== void 0) {
|
|
5626
|
-
const v = obj[countKey];
|
|
5627
|
-
if (typeof v === "number") return v;
|
|
5628
|
-
if (typeof v === "bigint") {
|
|
5629
|
-
const n = Number(v);
|
|
5630
|
-
return Number.isSafeInteger(n) ? n : 0;
|
|
5631
|
-
}
|
|
5632
|
-
if (typeof v === "string") {
|
|
5633
|
-
const n = Number.parseInt(v, 10);
|
|
5634
|
-
return Number.isFinite(n) ? n : 0;
|
|
5635
|
-
}
|
|
5636
|
-
}
|
|
5637
|
-
}
|
|
5638
|
-
return 0;
|
|
5639
|
-
}
|
|
5640
|
-
function parseBatchCountResults(row, count) {
|
|
5641
|
-
const results = [];
|
|
5642
|
-
for (let i = 0; i < count; i++) {
|
|
5643
|
-
const key = `count_${i}`;
|
|
5644
|
-
const value = row[key];
|
|
5645
|
-
results.push(parseCountValue(value));
|
|
5646
|
-
}
|
|
5647
|
-
return results;
|
|
5648
|
-
}
|
|
5649
|
-
function parseBatchResults(row, keys, queries, aliases) {
|
|
5650
|
-
const results = {};
|
|
5651
|
-
for (let i = 0; i < keys.length; i++) {
|
|
5652
|
-
const key = keys[i];
|
|
5653
|
-
const columnKey = aliases && aliases[i] ? aliases[i] : key;
|
|
5654
|
-
const rawValue = row[columnKey];
|
|
5655
|
-
const query = queries[key];
|
|
5656
|
-
switch (query.method) {
|
|
5657
|
-
case "findMany": {
|
|
5658
|
-
const parsed = parseJsonValue(rawValue);
|
|
5659
|
-
results[key] = Array.isArray(parsed) ? parsed : [];
|
|
5660
|
-
break;
|
|
5661
|
-
}
|
|
5662
|
-
case "findFirst":
|
|
5663
|
-
case "findUnique": {
|
|
5664
|
-
const parsed = parseJsonValue(rawValue);
|
|
5665
|
-
results[key] = parsed != null ? parsed : null;
|
|
5666
|
-
break;
|
|
5667
|
-
}
|
|
5668
|
-
case "count": {
|
|
5669
|
-
results[key] = parseCountValue(rawValue);
|
|
5670
|
-
break;
|
|
5671
|
-
}
|
|
5672
|
-
case "aggregate": {
|
|
5673
|
-
const parsed = parseJsonValue(rawValue);
|
|
5674
|
-
const obj = parsed != null ? parsed : {};
|
|
5675
|
-
results[key] = transformQueryResults("aggregate", [obj]);
|
|
5676
|
-
break;
|
|
5677
|
-
}
|
|
5678
|
-
case "groupBy": {
|
|
5679
|
-
const parsed = parseJsonValue(rawValue);
|
|
5680
|
-
const arr = Array.isArray(parsed) ? parsed : [];
|
|
5681
|
-
results[key] = transformQueryResults("groupBy", arr);
|
|
5682
|
-
break;
|
|
5683
|
-
}
|
|
5684
|
-
default:
|
|
5685
|
-
results[key] = rawValue;
|
|
5686
|
-
}
|
|
6083
|
+
if (merged) return merged;
|
|
5687
6084
|
}
|
|
5688
|
-
|
|
6085
|
+
const result = buildRegularBatchQueries(
|
|
6086
|
+
queries,
|
|
6087
|
+
keys,
|
|
6088
|
+
aliases,
|
|
6089
|
+
modelMap,
|
|
6090
|
+
models,
|
|
6091
|
+
dialect
|
|
6092
|
+
);
|
|
6093
|
+
return __spreadProps(__spreadValues({}, result), { keys, aliases });
|
|
5689
6094
|
}
|
|
5690
6095
|
|
|
5691
|
-
// src/
|
|
5692
|
-
function isolationLevelToPostgresKeyword(level) {
|
|
5693
|
-
switch (level) {
|
|
5694
|
-
case "ReadCommitted":
|
|
5695
|
-
return "read committed";
|
|
5696
|
-
case "RepeatableRead":
|
|
5697
|
-
return "repeatable read";
|
|
5698
|
-
case "Serializable":
|
|
5699
|
-
return "serializable";
|
|
5700
|
-
default:
|
|
5701
|
-
return void 0;
|
|
5702
|
-
}
|
|
5703
|
-
}
|
|
5704
|
-
function createTransactionExecutor(deps) {
|
|
5705
|
-
const { modelMap, allModels, dialect, executeRaw, postgresClient } = deps;
|
|
5706
|
-
return {
|
|
5707
|
-
execute(queries, options) {
|
|
5708
|
-
return __async(this, null, function* () {
|
|
5709
|
-
if (queries.length === 0) return [];
|
|
5710
|
-
if (dialect !== "postgres") {
|
|
5711
|
-
throw new Error("$transaction is only supported for postgres dialect");
|
|
5712
|
-
}
|
|
5713
|
-
if (!postgresClient) {
|
|
5714
|
-
throw new Error("postgresClient is required for transactions");
|
|
5715
|
-
}
|
|
5716
|
-
const transactionCallback = (sql) => __async(null, null, function* () {
|
|
5717
|
-
const results = [];
|
|
5718
|
-
const isolationLevel = isolationLevelToPostgresKeyword(
|
|
5719
|
-
options == null ? void 0 : options.isolationLevel
|
|
5720
|
-
);
|
|
5721
|
-
if (isolationLevel) {
|
|
5722
|
-
yield sql.unsafe(
|
|
5723
|
-
`SET TRANSACTION ISOLATION LEVEL ${isolationLevel.toUpperCase()}`
|
|
5724
|
-
);
|
|
5725
|
-
}
|
|
5726
|
-
if (options == null ? void 0 : options.timeout) {
|
|
5727
|
-
yield sql.unsafe(
|
|
5728
|
-
`SET LOCAL statement_timeout = ${Math.floor(options.timeout)}`
|
|
5729
|
-
);
|
|
5730
|
-
}
|
|
5731
|
-
for (const q of queries) {
|
|
5732
|
-
const model = modelMap.get(q.model);
|
|
5733
|
-
if (!model) {
|
|
5734
|
-
throw new Error(
|
|
5735
|
-
`Model '${q.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5736
|
-
);
|
|
5737
|
-
}
|
|
5738
|
-
const { sql: sqlStr, params } = buildSQLWithCache(
|
|
5739
|
-
model,
|
|
5740
|
-
allModels,
|
|
5741
|
-
q.method,
|
|
5742
|
-
q.args || {},
|
|
5743
|
-
dialect
|
|
5744
|
-
);
|
|
5745
|
-
const rawResults = yield sql.unsafe(sqlStr, params);
|
|
5746
|
-
results.push(transformQueryResults(q.method, rawResults));
|
|
5747
|
-
}
|
|
5748
|
-
return results;
|
|
5749
|
-
});
|
|
5750
|
-
return yield postgresClient.begin(transactionCallback);
|
|
5751
|
-
});
|
|
5752
|
-
}
|
|
5753
|
-
};
|
|
5754
|
-
}
|
|
5755
|
-
var ACCELERATED_METHODS = /* @__PURE__ */ new Set([
|
|
5756
|
-
"findMany",
|
|
5757
|
-
"findFirst",
|
|
5758
|
-
"findUnique",
|
|
5759
|
-
"count",
|
|
5760
|
-
"aggregate",
|
|
5761
|
-
"groupBy"
|
|
5762
|
-
]);
|
|
6096
|
+
// src/index.ts
|
|
5763
6097
|
function buildSQL(model, models, method, args, dialect) {
|
|
5764
6098
|
return buildSQLWithCache(model, models, method, args, dialect);
|
|
5765
6099
|
}
|
|
5766
|
-
function executePostgres(client, sql, params) {
|
|
5767
|
-
return __async(this, null, function* () {
|
|
5768
|
-
return yield client.unsafe(sql, params);
|
|
5769
|
-
});
|
|
5770
|
-
}
|
|
5771
|
-
function shouldSqliteUseGet(method) {
|
|
5772
|
-
return method === "count" || method === "findFirst" || method === "findUnique" || method === "aggregate";
|
|
5773
|
-
}
|
|
5774
|
-
function executeSqlite(db, method, sql, params) {
|
|
5775
|
-
const stmt = db.prepare(sql);
|
|
5776
|
-
if (shouldSqliteUseGet(method)) {
|
|
5777
|
-
const row = stmt.get(...params);
|
|
5778
|
-
if (row === void 0) return [];
|
|
5779
|
-
return [row];
|
|
5780
|
-
}
|
|
5781
|
-
return stmt.all(...params);
|
|
5782
|
-
}
|
|
5783
|
-
function executeWithTiming(input) {
|
|
5784
|
-
return __async(this, null, function* () {
|
|
5785
|
-
var _a;
|
|
5786
|
-
const startTime = Date.now();
|
|
5787
|
-
const { sql, params } = buildSQL(
|
|
5788
|
-
input.model,
|
|
5789
|
-
input.allModels,
|
|
5790
|
-
input.method,
|
|
5791
|
-
input.args,
|
|
5792
|
-
input.dialect
|
|
5793
|
-
);
|
|
5794
|
-
if (input.debug) {
|
|
5795
|
-
console.log(`[${input.dialect}] ${input.modelName}.${input.method}`);
|
|
5796
|
-
console.log("SQL:", sql);
|
|
5797
|
-
console.log("Params:", params);
|
|
5798
|
-
}
|
|
5799
|
-
const results = yield input.executeQuery(input.method, sql, params);
|
|
5800
|
-
const duration = Date.now() - startTime;
|
|
5801
|
-
(_a = input.onQuery) == null ? void 0 : _a.call(input, {
|
|
5802
|
-
model: input.modelName,
|
|
5803
|
-
method: input.method,
|
|
5804
|
-
sql,
|
|
5805
|
-
params,
|
|
5806
|
-
duration
|
|
5807
|
-
});
|
|
5808
|
-
return results;
|
|
5809
|
-
});
|
|
5810
|
-
}
|
|
5811
|
-
function resolveModelName(ctx) {
|
|
5812
|
-
return (ctx == null ? void 0 : ctx.name) || (ctx == null ? void 0 : ctx.$name);
|
|
5813
|
-
}
|
|
5814
|
-
function isAllowedModel(allowedModels, modelName) {
|
|
5815
|
-
if (!allowedModels) return true;
|
|
5816
|
-
return allowedModels.includes(modelName);
|
|
5817
|
-
}
|
|
5818
|
-
function getModelOrNull(modelMap, modelName) {
|
|
5819
|
-
var _a;
|
|
5820
|
-
return (_a = modelMap.get(modelName)) != null ? _a : null;
|
|
5821
|
-
}
|
|
5822
|
-
function fallbackToPrisma(ctx, modelName, method, args) {
|
|
5823
|
-
return ctx.$parent[modelName][method](args);
|
|
5824
|
-
}
|
|
5825
|
-
function createExecuteQuery(client, dialect) {
|
|
5826
|
-
return (method, sql, params) => __async(null, null, function* () {
|
|
5827
|
-
if (dialect === "postgres") {
|
|
5828
|
-
return yield executePostgres(client, sql, params);
|
|
5829
|
-
}
|
|
5830
|
-
return executeSqlite(client, method, sql, params);
|
|
5831
|
-
});
|
|
5832
|
-
}
|
|
5833
|
-
function logAcceleratedError(debug, dialect, modelName, method, error) {
|
|
5834
|
-
if (!debug) return;
|
|
5835
|
-
console.error(`[${dialect}] ${modelName}.${method} failed:`, error);
|
|
5836
|
-
}
|
|
5837
|
-
function canAccelerate(deps, modelName, method) {
|
|
5838
|
-
if (!ACCELERATED_METHODS.has(method)) return false;
|
|
5839
|
-
if (!isAllowedModel(deps.allowedModels, modelName)) return false;
|
|
5840
|
-
return deps.modelMap.has(modelName);
|
|
5841
|
-
}
|
|
5842
|
-
function runAccelerated(deps, modelName, method, model, args) {
|
|
5843
|
-
return __async(this, null, function* () {
|
|
5844
|
-
const results = yield executeWithTiming({
|
|
5845
|
-
modelName,
|
|
5846
|
-
method,
|
|
5847
|
-
model,
|
|
5848
|
-
allModels: deps.allModels,
|
|
5849
|
-
args: args || {},
|
|
5850
|
-
dialect: deps.dialect,
|
|
5851
|
-
debug: deps.debug,
|
|
5852
|
-
executeQuery: deps.executeQuery,
|
|
5853
|
-
onQuery: deps.onQuery
|
|
5854
|
-
});
|
|
5855
|
-
return transformQueryResults(method, results);
|
|
5856
|
-
});
|
|
5857
|
-
}
|
|
5858
|
-
function handleMethodCall(ctx, method, args, deps) {
|
|
5859
|
-
return __async(this, null, function* () {
|
|
5860
|
-
const modelName = resolveModelName(ctx);
|
|
5861
|
-
if (!canAccelerate(deps, modelName, method)) {
|
|
5862
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
5863
|
-
}
|
|
5864
|
-
const model = getModelOrNull(deps.modelMap, modelName);
|
|
5865
|
-
if (!model) {
|
|
5866
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
5867
|
-
}
|
|
5868
|
-
try {
|
|
5869
|
-
return yield runAccelerated(deps, modelName, method, model, args);
|
|
5870
|
-
} catch (error) {
|
|
5871
|
-
logAcceleratedError(deps.debug, deps.dialect, modelName, method, error);
|
|
5872
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
5873
|
-
}
|
|
5874
|
-
});
|
|
5875
|
-
}
|
|
5876
|
-
var DeferredQuery = class {
|
|
5877
|
-
constructor(model, method, args) {
|
|
5878
|
-
this.model = model;
|
|
5879
|
-
this.method = method;
|
|
5880
|
-
this.args = args;
|
|
5881
|
-
}
|
|
5882
|
-
then(onfulfilled, onrejected) {
|
|
5883
|
-
throw new Error(
|
|
5884
|
-
"Cannot await a batch query. Batch queries must not be awaited inside the $batch callback."
|
|
5885
|
-
);
|
|
5886
|
-
}
|
|
5887
|
-
};
|
|
5888
|
-
function createBatchProxy(modelMap, allowedModels) {
|
|
5889
|
-
return new Proxy(
|
|
5890
|
-
{},
|
|
5891
|
-
{
|
|
5892
|
-
get(_target, modelName) {
|
|
5893
|
-
if (typeof modelName === "symbol") return void 0;
|
|
5894
|
-
const model = modelMap.get(modelName);
|
|
5895
|
-
if (!model) {
|
|
5896
|
-
throw new Error(
|
|
5897
|
-
`Model '${modelName}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5898
|
-
);
|
|
5899
|
-
}
|
|
5900
|
-
if (allowedModels && !allowedModels.includes(modelName)) {
|
|
5901
|
-
throw new Error(
|
|
5902
|
-
`Model '${modelName}' not allowed. Allowed: ${allowedModels.join(", ")}`
|
|
5903
|
-
);
|
|
5904
|
-
}
|
|
5905
|
-
return new Proxy(
|
|
5906
|
-
{},
|
|
5907
|
-
{
|
|
5908
|
-
get(_target2, method) {
|
|
5909
|
-
if (!ACCELERATED_METHODS.has(method)) {
|
|
5910
|
-
throw new Error(
|
|
5911
|
-
`Method '${method}' not supported in batch. Supported: ${[...ACCELERATED_METHODS].join(", ")}`
|
|
5912
|
-
);
|
|
5913
|
-
}
|
|
5914
|
-
return (args) => {
|
|
5915
|
-
return new DeferredQuery(
|
|
5916
|
-
modelName,
|
|
5917
|
-
method,
|
|
5918
|
-
args
|
|
5919
|
-
);
|
|
5920
|
-
};
|
|
5921
|
-
}
|
|
5922
|
-
}
|
|
5923
|
-
);
|
|
5924
|
-
}
|
|
5925
|
-
}
|
|
5926
|
-
);
|
|
5927
|
-
}
|
|
5928
|
-
function speedExtension(config) {
|
|
5929
|
-
const {
|
|
5930
|
-
postgres,
|
|
5931
|
-
sqlite,
|
|
5932
|
-
models: providedModels,
|
|
5933
|
-
dmmf,
|
|
5934
|
-
debug = false,
|
|
5935
|
-
allowedModels,
|
|
5936
|
-
onQuery
|
|
5937
|
-
} = config;
|
|
5938
|
-
if (!postgres && !sqlite) {
|
|
5939
|
-
throw new Error("speedExtension requires either postgres or sqlite client");
|
|
5940
|
-
}
|
|
5941
|
-
if (postgres && sqlite) {
|
|
5942
|
-
throw new Error(
|
|
5943
|
-
"speedExtension cannot use both postgres and sqlite clients"
|
|
5944
|
-
);
|
|
5945
|
-
}
|
|
5946
|
-
let models;
|
|
5947
|
-
if (providedModels) {
|
|
5948
|
-
models = providedModels;
|
|
5949
|
-
} else if (dmmf) {
|
|
5950
|
-
models = convertDMMFToModels(dmmf.datamodel);
|
|
5951
|
-
} else {
|
|
5952
|
-
throw new Error("speedExtension requires either models or dmmf parameter.");
|
|
5953
|
-
}
|
|
5954
|
-
if (!Array.isArray(models) || models.length === 0) {
|
|
5955
|
-
throw new Error("speedExtension: models array is empty or invalid");
|
|
5956
|
-
}
|
|
5957
|
-
const dialect = postgres ? "postgres" : "sqlite";
|
|
5958
|
-
const client = postgres || sqlite;
|
|
5959
|
-
setGlobalDialect(dialect);
|
|
5960
|
-
return (prisma) => {
|
|
5961
|
-
const modelMap = new Map(models.map((m) => [m.name, m]));
|
|
5962
|
-
const executeQuery = createExecuteQuery(client, dialect);
|
|
5963
|
-
const deps = {
|
|
5964
|
-
dialect,
|
|
5965
|
-
debug,
|
|
5966
|
-
onQuery,
|
|
5967
|
-
allowedModels,
|
|
5968
|
-
allModels: models,
|
|
5969
|
-
modelMap,
|
|
5970
|
-
executeQuery
|
|
5971
|
-
};
|
|
5972
|
-
const createMethodHandler = (method) => {
|
|
5973
|
-
return function(args) {
|
|
5974
|
-
return __async(this, null, function* () {
|
|
5975
|
-
return handleMethodCall(this, method, args, deps);
|
|
5976
|
-
});
|
|
5977
|
-
};
|
|
5978
|
-
};
|
|
5979
|
-
const methodHandlers = {};
|
|
5980
|
-
for (const method of ACCELERATED_METHODS) {
|
|
5981
|
-
methodHandlers[method] = createMethodHandler(method);
|
|
5982
|
-
}
|
|
5983
|
-
const executeRaw = (sql, params) => __async(null, null, function* () {
|
|
5984
|
-
if (dialect === "postgres") {
|
|
5985
|
-
return yield client.unsafe(sql, params);
|
|
5986
|
-
}
|
|
5987
|
-
throw new Error("Raw execution for sqlite not supported in transactions");
|
|
5988
|
-
});
|
|
5989
|
-
const txExecutor = createTransactionExecutor({
|
|
5990
|
-
modelMap,
|
|
5991
|
-
allModels: models,
|
|
5992
|
-
dialect,
|
|
5993
|
-
executeRaw,
|
|
5994
|
-
postgresClient: postgres
|
|
5995
|
-
});
|
|
5996
|
-
function batch(callback) {
|
|
5997
|
-
return __async(this, null, function* () {
|
|
5998
|
-
const batchProxy = createBatchProxy(modelMap, allowedModels);
|
|
5999
|
-
const queries = yield callback(batchProxy);
|
|
6000
|
-
const batchQueries = {};
|
|
6001
|
-
for (const [key, deferred] of Object.entries(queries)) {
|
|
6002
|
-
if (!(deferred instanceof DeferredQuery)) {
|
|
6003
|
-
throw new Error(
|
|
6004
|
-
`Batch query '${key}' must be a deferred query. Did you await it?`
|
|
6005
|
-
);
|
|
6006
|
-
}
|
|
6007
|
-
batchQueries[key] = {
|
|
6008
|
-
model: deferred.model,
|
|
6009
|
-
method: deferred.method,
|
|
6010
|
-
args: deferred.args || {}
|
|
6011
|
-
};
|
|
6012
|
-
}
|
|
6013
|
-
const startTime = Date.now();
|
|
6014
|
-
const { sql, params, keys, aliases } = buildBatchSql(
|
|
6015
|
-
batchQueries,
|
|
6016
|
-
modelMap,
|
|
6017
|
-
models,
|
|
6018
|
-
dialect
|
|
6019
|
-
);
|
|
6020
|
-
if (debug) {
|
|
6021
|
-
console.log(`[${dialect}] $batch (${keys.length} queries)`);
|
|
6022
|
-
console.log("SQL:", sql);
|
|
6023
|
-
console.log("Params:", params);
|
|
6024
|
-
}
|
|
6025
|
-
const rows = yield executeQuery("findMany", sql, params);
|
|
6026
|
-
const row = rows[0];
|
|
6027
|
-
const results = parseBatchResults(row, keys, batchQueries, aliases);
|
|
6028
|
-
const duration = Date.now() - startTime;
|
|
6029
|
-
onQuery == null ? void 0 : onQuery({
|
|
6030
|
-
model: "_batch",
|
|
6031
|
-
method: "batch",
|
|
6032
|
-
sql,
|
|
6033
|
-
params,
|
|
6034
|
-
duration
|
|
6035
|
-
});
|
|
6036
|
-
return results;
|
|
6037
|
-
});
|
|
6038
|
-
}
|
|
6039
|
-
function transaction(queries, options) {
|
|
6040
|
-
return __async(this, null, function* () {
|
|
6041
|
-
const startTime = Date.now();
|
|
6042
|
-
if (debug) {
|
|
6043
|
-
console.log(`[${dialect}] $transaction (${queries.length} queries)`);
|
|
6044
|
-
}
|
|
6045
|
-
const results = yield txExecutor.execute(queries, options);
|
|
6046
|
-
const duration = Date.now() - startTime;
|
|
6047
|
-
onQuery == null ? void 0 : onQuery({
|
|
6048
|
-
model: "_transaction",
|
|
6049
|
-
method: "transaction",
|
|
6050
|
-
sql: `TRANSACTION(${queries.length})`,
|
|
6051
|
-
params: [],
|
|
6052
|
-
duration
|
|
6053
|
-
});
|
|
6054
|
-
return results;
|
|
6055
|
-
});
|
|
6056
|
-
}
|
|
6057
|
-
return prisma.$extends({
|
|
6058
|
-
name: "prisma-sql-speed",
|
|
6059
|
-
client: {
|
|
6060
|
-
$original: prisma,
|
|
6061
|
-
$batch: batch,
|
|
6062
|
-
$transaction: transaction
|
|
6063
|
-
},
|
|
6064
|
-
model: {
|
|
6065
|
-
$allModels: methodHandlers
|
|
6066
|
-
}
|
|
6067
|
-
});
|
|
6068
|
-
};
|
|
6069
|
-
}
|
|
6070
|
-
function extendPrisma(prisma, config) {
|
|
6071
|
-
const extension = speedExtension(config);
|
|
6072
|
-
return extension(prisma);
|
|
6073
|
-
}
|
|
6074
6100
|
function createToSQLFunction(models, dialect) {
|
|
6075
6101
|
if (!models || !Array.isArray(models) || models.length === 0) {
|
|
6076
6102
|
throw new Error("createToSQL requires non-empty models array");
|
|
@@ -6158,6 +6184,6 @@ function generateSQLByModel(directives) {
|
|
|
6158
6184
|
return byModel;
|
|
6159
6185
|
}
|
|
6160
6186
|
|
|
6161
|
-
export {
|
|
6187
|
+
export { buildSQL, createPrismaSQL, createToSQL, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel };
|
|
6162
6188
|
//# sourceMappingURL=index.js.map
|
|
6163
6189
|
//# sourceMappingURL=index.js.map
|