prisma-sql 1.59.0 → 1.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generator.cjs +1163 -673
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1163 -673
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1469 -1682
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +28 -133
- package/dist/index.d.ts +28 -133
- package/dist/index.js +1470 -1670
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/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,155 +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
|
-
MAX_HAVING_DEPTH: 50
|
|
191
|
-
});
|
|
192
|
-
|
|
193
58
|
// src/utils/normalize-value.ts
|
|
194
59
|
var MAX_DEPTH = 20;
|
|
195
60
|
function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
@@ -197,45 +62,54 @@ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0)
|
|
|
197
62
|
throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
|
|
198
63
|
}
|
|
199
64
|
if (value instanceof Date) {
|
|
200
|
-
|
|
201
|
-
if (!Number.isFinite(t)) {
|
|
202
|
-
throw new Error("Invalid Date value in SQL params");
|
|
203
|
-
}
|
|
204
|
-
return value.toISOString();
|
|
65
|
+
return normalizeDateValue(value);
|
|
205
66
|
}
|
|
206
67
|
if (typeof value === "bigint") {
|
|
207
68
|
return value.toString();
|
|
208
69
|
}
|
|
209
70
|
if (Array.isArray(value)) {
|
|
210
|
-
|
|
211
|
-
if (seen.has(arrRef)) {
|
|
212
|
-
throw new Error("Circular reference in SQL params");
|
|
213
|
-
}
|
|
214
|
-
seen.add(arrRef);
|
|
215
|
-
const out = value.map((v) => normalizeValue(v, seen, depth + 1));
|
|
216
|
-
seen.delete(arrRef);
|
|
217
|
-
return out;
|
|
71
|
+
return normalizeArrayValue(value, seen, depth);
|
|
218
72
|
}
|
|
219
73
|
if (value && typeof value === "object") {
|
|
220
|
-
|
|
221
|
-
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
|
|
222
|
-
const proto = Object.getPrototypeOf(value);
|
|
223
|
-
const isPlain = proto === Object.prototype || proto === null;
|
|
224
|
-
if (!isPlain) return value;
|
|
225
|
-
const obj = value;
|
|
226
|
-
if (seen.has(obj)) {
|
|
227
|
-
throw new Error("Circular reference in SQL params");
|
|
228
|
-
}
|
|
229
|
-
seen.add(obj);
|
|
230
|
-
const out = {};
|
|
231
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
232
|
-
out[k] = normalizeValue(v, seen, depth + 1);
|
|
233
|
-
}
|
|
234
|
-
seen.delete(obj);
|
|
235
|
-
return out;
|
|
74
|
+
return normalizeObjectValue(value, seen, depth);
|
|
236
75
|
}
|
|
237
76
|
return value;
|
|
238
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
|
+
}
|
|
239
113
|
|
|
240
114
|
// src/sql-builder-dialect.ts
|
|
241
115
|
var globalDialect = "postgres";
|
|
@@ -250,15 +124,6 @@ function setGlobalDialect(dialect) {
|
|
|
250
124
|
function getGlobalDialect() {
|
|
251
125
|
return globalDialect;
|
|
252
126
|
}
|
|
253
|
-
function withDialect(dialect, fn) {
|
|
254
|
-
const prev = globalDialect;
|
|
255
|
-
globalDialect = dialect;
|
|
256
|
-
try {
|
|
257
|
-
return fn();
|
|
258
|
-
} finally {
|
|
259
|
-
globalDialect = prev;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
127
|
function assertNonEmpty(value, name) {
|
|
263
128
|
if (!value || value.trim().length === 0) {
|
|
264
129
|
throw new Error(`${name} is required and cannot be empty`);
|
|
@@ -424,94 +289,243 @@ function prepareArrayParam(value, dialect) {
|
|
|
424
289
|
if (dialect === "postgres") {
|
|
425
290
|
return value.map((v) => normalizeValue(v));
|
|
426
291
|
}
|
|
427
|
-
return JSON.stringify(value);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// src/builder/shared/validators/type-guards.ts
|
|
431
|
-
function isNotNullish(value) {
|
|
432
|
-
return value !== null && value !== void 0;
|
|
433
|
-
}
|
|
434
|
-
function isNonEmptyString(value) {
|
|
435
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
436
|
-
}
|
|
437
|
-
function isEmptyString(value) {
|
|
438
|
-
return typeof value === "string" && value.trim().length === 0;
|
|
439
|
-
}
|
|
440
|
-
function isNonEmptyArray(value) {
|
|
441
|
-
return Array.isArray(value) && value.length > 0;
|
|
442
|
-
}
|
|
443
|
-
function isEmptyArray(value) {
|
|
444
|
-
return Array.isArray(value) && value.length === 0;
|
|
445
|
-
}
|
|
446
|
-
function isPlainObject(val) {
|
|
447
|
-
if (!isNotNullish(val)) return false;
|
|
448
|
-
if (Array.isArray(val)) return false;
|
|
449
|
-
if (typeof val !== "object") return false;
|
|
450
|
-
return Object.prototype.toString.call(val) === "[object Object]";
|
|
451
|
-
}
|
|
452
|
-
function hasProperty(obj, key) {
|
|
453
|
-
return isPlainObject(obj) && key in obj;
|
|
454
|
-
}
|
|
455
|
-
function isArrayType(t) {
|
|
456
|
-
if (!isNotNullish(t)) return false;
|
|
457
|
-
const normalized = t.replace(/\?$/, "");
|
|
458
|
-
return normalized.endsWith("[]");
|
|
459
|
-
}
|
|
460
|
-
function isJsonType(t) {
|
|
461
|
-
return isNotNullish(t) && (t === "Json" || t === "Json?");
|
|
462
|
-
}
|
|
463
|
-
function hasValidContent(sql) {
|
|
464
|
-
return isNotNullish(sql) && sql.trim().length > 0;
|
|
465
|
-
}
|
|
466
|
-
function hasRequiredKeywords(sql) {
|
|
467
|
-
const upper = sql.toUpperCase();
|
|
468
|
-
const hasSelect = upper.includes("SELECT");
|
|
469
|
-
const hasFrom = upper.includes("FROM");
|
|
470
|
-
return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// src/builder/shared/errors.ts
|
|
474
|
-
var SqlBuilderError = class extends Error {
|
|
475
|
-
constructor(message, code, context) {
|
|
476
|
-
super(message);
|
|
477
|
-
this.name = "SqlBuilderError";
|
|
478
|
-
this.code = code;
|
|
479
|
-
this.context = context;
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
function createError(message, ctx, code = "VALIDATION_ERROR") {
|
|
483
|
-
const parts = [message];
|
|
484
|
-
if (isNonEmptyArray(ctx.path)) {
|
|
485
|
-
parts.push(`Path: ${ctx.path.join(".")}`);
|
|
486
|
-
}
|
|
487
|
-
if (isNotNullish(ctx.modelName)) {
|
|
488
|
-
parts.push(`Model: ${ctx.modelName}`);
|
|
489
|
-
}
|
|
490
|
-
if (isNonEmptyArray(ctx.availableFields)) {
|
|
491
|
-
parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
|
|
492
|
-
}
|
|
493
|
-
return new SqlBuilderError(parts.join("\n"), code, ctx);
|
|
292
|
+
return JSON.stringify(value.map((v) => normalizeValue(v)));
|
|
494
293
|
}
|
|
495
294
|
|
|
496
|
-
// src/builder/shared/
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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;
|
|
447
|
+
}
|
|
448
|
+
function isNonEmptyString(value) {
|
|
449
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
450
|
+
}
|
|
451
|
+
function isEmptyString(value) {
|
|
452
|
+
return typeof value === "string" && value.trim().length === 0;
|
|
453
|
+
}
|
|
454
|
+
function isNonEmptyArray(value) {
|
|
455
|
+
return Array.isArray(value) && value.length > 0;
|
|
456
|
+
}
|
|
457
|
+
function isEmptyArray(value) {
|
|
458
|
+
return Array.isArray(value) && value.length === 0;
|
|
459
|
+
}
|
|
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);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/builder/shared/validators/sql-validators.ts
|
|
511
|
+
function isValidWhereClause(clause) {
|
|
512
|
+
return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
|
|
513
|
+
}
|
|
514
|
+
function isEmptyWhere(where) {
|
|
515
|
+
if (!isNotNullish(where)) return true;
|
|
516
|
+
return Object.keys(where).length === 0;
|
|
517
|
+
}
|
|
518
|
+
function sqlPreview(sql) {
|
|
519
|
+
const s = String(sql);
|
|
520
|
+
if (s.length <= 160) return s;
|
|
521
|
+
return `${s.slice(0, 160)}...`;
|
|
522
|
+
}
|
|
523
|
+
function validateSelectQuery(sql) {
|
|
524
|
+
if (IS_PRODUCTION) return;
|
|
525
|
+
if (!hasValidContent(sql)) {
|
|
526
|
+
throw new Error("CRITICAL: Generated empty SQL query");
|
|
527
|
+
}
|
|
528
|
+
if (!hasRequiredKeywords(sql)) {
|
|
515
529
|
throw new Error(`CRITICAL: Invalid SQL structure. SQL: ${sqlPreview(sql)}`);
|
|
516
530
|
}
|
|
517
531
|
}
|
|
@@ -642,10 +656,13 @@ function quoteIdent(id) {
|
|
|
642
656
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
643
657
|
throw new Error("quoteIdent: identifier is required and cannot be empty");
|
|
644
658
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
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
|
+
}
|
|
649
666
|
}
|
|
650
667
|
if (needsQuoting(id)) {
|
|
651
668
|
return `"${id.replace(/"/g, '""')}"`;
|
|
@@ -710,7 +727,13 @@ function getQuotedColumn(model, fieldName) {
|
|
|
710
727
|
|
|
711
728
|
// src/builder/shared/sql-utils.ts
|
|
712
729
|
function containsControlChars(s) {
|
|
713
|
-
|
|
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;
|
|
714
737
|
}
|
|
715
738
|
function assertNoControlChars(label, s) {
|
|
716
739
|
if (containsControlChars(s)) {
|
|
@@ -785,20 +808,9 @@ function parseUnquotedPart(input, start) {
|
|
|
785
808
|
}
|
|
786
809
|
return i;
|
|
787
810
|
}
|
|
788
|
-
function
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
if (trimmed.length === 0) {
|
|
792
|
-
throw new Error("tableName/tableRef is required and cannot be empty");
|
|
793
|
-
}
|
|
794
|
-
if (raw !== trimmed) {
|
|
795
|
-
throw new Error(
|
|
796
|
-
`tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
|
|
797
|
-
);
|
|
798
|
-
}
|
|
799
|
-
assertNoControlChars("tableName/tableRef", trimmed);
|
|
800
|
-
for (let i2 = 0; i2 < trimmed.length; i2++) {
|
|
801
|
-
const c = trimmed.charCodeAt(i2);
|
|
811
|
+
function validateQualifiedNameFormat(trimmed) {
|
|
812
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
813
|
+
const c = trimmed.charCodeAt(i);
|
|
802
814
|
if (c === 9 || c === 11 || c === 12 || c === 32) {
|
|
803
815
|
throw new Error(
|
|
804
816
|
`tableName/tableRef must not contain whitespace: ${JSON.stringify(trimmed)}`
|
|
@@ -815,6 +827,8 @@ function assertSafeQualifiedName(input) {
|
|
|
815
827
|
);
|
|
816
828
|
}
|
|
817
829
|
}
|
|
830
|
+
}
|
|
831
|
+
function parseQualifiedNameParts(trimmed) {
|
|
818
832
|
let i = 0;
|
|
819
833
|
const n = trimmed.length;
|
|
820
834
|
let parts = 0;
|
|
@@ -850,6 +864,21 @@ function assertSafeQualifiedName(input) {
|
|
|
850
864
|
}
|
|
851
865
|
}
|
|
852
866
|
}
|
|
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
|
+
}
|
|
853
882
|
function quote(id) {
|
|
854
883
|
if (isEmptyString(id)) {
|
|
855
884
|
throw new Error("quote: identifier is required and cannot be empty");
|
|
@@ -939,7 +968,7 @@ function assertSafeAlias(alias) {
|
|
|
939
968
|
if (a !== alias) {
|
|
940
969
|
throw new Error("Invalid alias: leading/trailing whitespace");
|
|
941
970
|
}
|
|
942
|
-
if (
|
|
971
|
+
if (containsControlChars(a)) {
|
|
943
972
|
throw new Error(
|
|
944
973
|
"Invalid alias: contains unsafe characters (control characters)"
|
|
945
974
|
);
|
|
@@ -960,7 +989,7 @@ function assertSafeAlias(alias) {
|
|
|
960
989
|
"Invalid alias: must be a simple identifier without whitespace"
|
|
961
990
|
);
|
|
962
991
|
}
|
|
963
|
-
if (!/^[A-Za-z_]
|
|
992
|
+
if (!/^[A-Za-z_]\w*$/.test(a)) {
|
|
964
993
|
throw new Error(
|
|
965
994
|
`Invalid alias: must be a simple identifier (alphanumeric with underscores): "${alias}"`
|
|
966
995
|
);
|
|
@@ -1008,8 +1037,7 @@ function isValidRelationField(field) {
|
|
|
1008
1037
|
return false;
|
|
1009
1038
|
const fk = normalizeKeyList(field.foreignKey);
|
|
1010
1039
|
if (fk.length === 0) return false;
|
|
1011
|
-
const
|
|
1012
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1040
|
+
const refs = normalizeKeyList(field.references);
|
|
1013
1041
|
if (refs.length === 0) {
|
|
1014
1042
|
return fk.length === 1;
|
|
1015
1043
|
}
|
|
@@ -1017,8 +1045,7 @@ function isValidRelationField(field) {
|
|
|
1017
1045
|
return true;
|
|
1018
1046
|
}
|
|
1019
1047
|
function getReferenceFieldNames(field, foreignKeyCount) {
|
|
1020
|
-
const
|
|
1021
|
-
const refs = normalizeKeyList(refsRaw);
|
|
1048
|
+
const refs = normalizeKeyList(field.references);
|
|
1022
1049
|
if (refs.length === 0) {
|
|
1023
1050
|
if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
|
|
1024
1051
|
return [];
|
|
@@ -1179,29 +1206,35 @@ function normalizeOrderByInput(orderBy, parseValue) {
|
|
|
1179
1206
|
}
|
|
1180
1207
|
|
|
1181
1208
|
// src/builder/shared/order-by-determinism.ts
|
|
1182
|
-
function
|
|
1183
|
-
if (!model) return
|
|
1184
|
-
|
|
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;
|
|
1185
1217
|
}
|
|
1186
|
-
function
|
|
1218
|
+
function hasTiebreaker(orderBy, parse, field) {
|
|
1187
1219
|
if (!isNotNullish(orderBy)) return false;
|
|
1188
1220
|
const normalized = normalizeOrderByInput(orderBy, parse);
|
|
1189
1221
|
return normalized.some(
|
|
1190
|
-
(obj) => Object.prototype.hasOwnProperty.call(obj,
|
|
1222
|
+
(obj) => Object.prototype.hasOwnProperty.call(obj, field)
|
|
1191
1223
|
);
|
|
1192
1224
|
}
|
|
1193
|
-
function
|
|
1194
|
-
if (Array.isArray(orderBy)) return [...orderBy, {
|
|
1195
|
-
return [orderBy, {
|
|
1225
|
+
function addTiebreaker(orderBy, field) {
|
|
1226
|
+
if (Array.isArray(orderBy)) return [...orderBy, { [field]: "asc" }];
|
|
1227
|
+
return [orderBy, { [field]: "asc" }];
|
|
1196
1228
|
}
|
|
1197
1229
|
function ensureDeterministicOrderByInput(args) {
|
|
1198
1230
|
const { orderBy, model, parseValue } = args;
|
|
1199
|
-
|
|
1231
|
+
const tiebreaker = findTiebreakerField(model);
|
|
1232
|
+
if (!tiebreaker) return orderBy;
|
|
1200
1233
|
if (!isNotNullish(orderBy)) {
|
|
1201
|
-
return {
|
|
1234
|
+
return { [tiebreaker]: "asc" };
|
|
1202
1235
|
}
|
|
1203
|
-
if (
|
|
1204
|
-
return
|
|
1236
|
+
if (hasTiebreaker(orderBy, parseValue, tiebreaker)) return orderBy;
|
|
1237
|
+
return addTiebreaker(orderBy, tiebreaker);
|
|
1205
1238
|
}
|
|
1206
1239
|
|
|
1207
1240
|
// src/builder/shared/validators/field-assertions.ts
|
|
@@ -1348,11 +1381,9 @@ function defaultNullsFor(dialect, direction) {
|
|
|
1348
1381
|
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1349
1382
|
if (cursorEntries.length === 0) return orderEntries;
|
|
1350
1383
|
const existing = /* @__PURE__ */ new Set();
|
|
1351
|
-
for (
|
|
1352
|
-
existing.add(orderEntries[i].field);
|
|
1384
|
+
for (const entry of orderEntries) existing.add(entry.field);
|
|
1353
1385
|
let out = null;
|
|
1354
|
-
for (
|
|
1355
|
-
const field = cursorEntries[i][0];
|
|
1386
|
+
for (const [field] of cursorEntries) {
|
|
1356
1387
|
if (!existing.has(field)) {
|
|
1357
1388
|
if (!out) out = orderEntries.slice();
|
|
1358
1389
|
out.push({ field, direction: "asc" });
|
|
@@ -1592,7 +1623,44 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
|
1592
1623
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1593
1624
|
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1594
1625
|
}
|
|
1595
|
-
function
|
|
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 });
|
|
1634
|
+
}
|
|
1635
|
+
if ((op === Ops.IN || op === Ops.NOT_IN) && !isNotNullish(dialect)) {
|
|
1636
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1637
|
+
}
|
|
1638
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1639
|
+
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1640
|
+
operator: op
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
function routeOperatorHandler(expr, op, val, params, mode, dialect) {
|
|
1645
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1646
|
+
Ops.CONTAINS,
|
|
1647
|
+
Ops.STARTS_WITH,
|
|
1648
|
+
Ops.ENDS_WITH
|
|
1649
|
+
]);
|
|
1650
|
+
if (STRING_LIKE_OPS.has(op)) {
|
|
1651
|
+
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1652
|
+
}
|
|
1653
|
+
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1654
|
+
return handleInOperator(expr, op, val, params, dialect);
|
|
1655
|
+
}
|
|
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;
|
|
1596
1664
|
if (val === void 0) return "";
|
|
1597
1665
|
if (depth > MAX_NOT_DEPTH) {
|
|
1598
1666
|
throw new Error(
|
|
@@ -1609,33 +1677,17 @@ function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect, de
|
|
|
1609
1677
|
const placeholder = params.addAuto(val);
|
|
1610
1678
|
return `${expr} <> ${placeholder}`;
|
|
1611
1679
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
if (
|
|
1622
|
-
|
|
1623
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1624
|
-
operator: op
|
|
1625
|
-
});
|
|
1626
|
-
}
|
|
1627
|
-
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1628
|
-
}
|
|
1629
|
-
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1630
|
-
if (!isNotNullish(dialect)) {
|
|
1631
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1632
|
-
}
|
|
1633
|
-
return handleInOperator(expr, op, val, params, dialect);
|
|
1634
|
-
}
|
|
1635
|
-
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
|
|
1636
|
-
throw createError(`Insensitive equals requires a SQL dialect`, {
|
|
1637
|
-
operator: op
|
|
1638
|
-
});
|
|
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;
|
|
1639
1691
|
}
|
|
1640
1692
|
return handleComparisonOperator(expr, op, val, params);
|
|
1641
1693
|
}
|
|
@@ -1659,38 +1711,32 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, dep
|
|
|
1659
1711
|
if (!isNotNullish(dialect)) {
|
|
1660
1712
|
const clauses = [];
|
|
1661
1713
|
for (const [subOp, subVal] of entries) {
|
|
1662
|
-
const sub = buildScalarOperator(
|
|
1663
|
-
|
|
1664
|
-
subOp,
|
|
1665
|
-
subVal,
|
|
1666
|
-
params,
|
|
1667
|
-
effectiveMode,
|
|
1714
|
+
const sub = buildScalarOperator(expr, subOp, subVal, params, {
|
|
1715
|
+
mode: effectiveMode,
|
|
1668
1716
|
fieldType,
|
|
1669
|
-
void 0,
|
|
1670
|
-
depth + 1
|
|
1671
|
-
);
|
|
1717
|
+
dialect: void 0,
|
|
1718
|
+
depth: depth + 1
|
|
1719
|
+
});
|
|
1672
1720
|
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1673
1721
|
}
|
|
1674
1722
|
if (clauses.length === 0) return "";
|
|
1675
1723
|
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1676
|
-
|
|
1724
|
+
const separator2 = ` ${SQL_TEMPLATES.AND} `;
|
|
1725
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator2)})`;
|
|
1677
1726
|
}
|
|
1727
|
+
const separator = ` ${SQL_TEMPLATES.AND} `;
|
|
1678
1728
|
return buildNotComposite(
|
|
1679
1729
|
expr,
|
|
1680
1730
|
val,
|
|
1681
1731
|
params,
|
|
1682
1732
|
dialect,
|
|
1683
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(
|
|
1684
|
-
|
|
1685
|
-
subOp,
|
|
1686
|
-
subVal,
|
|
1687
|
-
p,
|
|
1688
|
-
effectiveMode,
|
|
1733
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, {
|
|
1734
|
+
mode: effectiveMode,
|
|
1689
1735
|
fieldType,
|
|
1690
|
-
d,
|
|
1691
|
-
depth + 1
|
|
1692
|
-
),
|
|
1693
|
-
|
|
1736
|
+
dialect: d,
|
|
1737
|
+
depth: depth + 1
|
|
1738
|
+
}),
|
|
1739
|
+
separator
|
|
1694
1740
|
);
|
|
1695
1741
|
}
|
|
1696
1742
|
function buildDynamicLikePattern(op, placeholder) {
|
|
@@ -1887,24 +1933,42 @@ function handleArrayIsEmpty(expr, val, dialect) {
|
|
|
1887
1933
|
// src/builder/where/operators-json.ts
|
|
1888
1934
|
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1889
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
|
+
}
|
|
1890
1949
|
function validateJsonPathSegments(segments) {
|
|
1891
|
-
|
|
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];
|
|
1892
1957
|
if (typeof segment !== "string") {
|
|
1893
|
-
throw createError(
|
|
1894
|
-
operator: Ops.PATH
|
|
1895
|
-
value: segment
|
|
1958
|
+
throw createError(`JSON path segment at index ${i} must be string`, {
|
|
1959
|
+
operator: Ops.PATH
|
|
1896
1960
|
});
|
|
1897
1961
|
}
|
|
1898
1962
|
if (segment.length > MAX_PATH_SEGMENT_LENGTH) {
|
|
1899
1963
|
throw createError(
|
|
1900
|
-
`JSON path segment too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
1901
|
-
{ operator: Ops.PATH
|
|
1964
|
+
`JSON path segment at index ${i} too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
|
|
1965
|
+
{ operator: Ops.PATH }
|
|
1902
1966
|
);
|
|
1903
1967
|
}
|
|
1904
1968
|
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1905
1969
|
throw createError(
|
|
1906
|
-
`Invalid JSON path segment: '${segment}'
|
|
1907
|
-
{ operator: Ops.PATH
|
|
1970
|
+
`Invalid JSON path segment at index ${i}: '${sanitizeForError(segment)}'`,
|
|
1971
|
+
{ operator: Ops.PATH }
|
|
1908
1972
|
);
|
|
1909
1973
|
}
|
|
1910
1974
|
}
|
|
@@ -2022,6 +2086,35 @@ function buildToOneNotExistsMatch(relTable, relAlias, join, sub) {
|
|
|
2022
2086
|
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2023
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})`;
|
|
2024
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
|
+
}
|
|
2025
2118
|
function buildListRelationFilters(args) {
|
|
2026
2119
|
const {
|
|
2027
2120
|
fieldName,
|
|
@@ -2042,21 +2135,16 @@ function buildListRelationFilters(args) {
|
|
|
2042
2135
|
isSubquery: true,
|
|
2043
2136
|
depth: ctx.depth + 1
|
|
2044
2137
|
}));
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
clause: whereClause,
|
|
2056
|
-
joins: freezeJoins([leftJoinSql])
|
|
2057
|
-
});
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2138
|
+
const optimized = tryOptimizeNoneFilter(
|
|
2139
|
+
noneValue,
|
|
2140
|
+
ctx,
|
|
2141
|
+
relModel,
|
|
2142
|
+
relTable,
|
|
2143
|
+
relAlias,
|
|
2144
|
+
join,
|
|
2145
|
+
sub
|
|
2146
|
+
);
|
|
2147
|
+
if (optimized) return optimized;
|
|
2060
2148
|
}
|
|
2061
2149
|
const filters = [
|
|
2062
2150
|
{
|
|
@@ -2077,17 +2165,8 @@ function buildListRelationFilters(args) {
|
|
|
2077
2165
|
];
|
|
2078
2166
|
const clauses = [];
|
|
2079
2167
|
for (const { key, wrap } of filters) {
|
|
2080
|
-
const
|
|
2081
|
-
if (
|
|
2082
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2083
|
-
alias: relAlias,
|
|
2084
|
-
model: relModel,
|
|
2085
|
-
path: [...ctx.path, fieldName, key],
|
|
2086
|
-
isSubquery: true,
|
|
2087
|
-
depth: ctx.depth + 1
|
|
2088
|
-
}));
|
|
2089
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2090
|
-
clauses.push(wrap(sub.clause, j));
|
|
2168
|
+
const clause = processRelationFilter(key, wrap, args);
|
|
2169
|
+
if (clause) clauses.push(clause);
|
|
2091
2170
|
}
|
|
2092
2171
|
if (clauses.length === 0) {
|
|
2093
2172
|
throw createError(
|
|
@@ -2489,15 +2568,11 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
|
2489
2568
|
if (fieldType && isJsonType(fieldType) && JSON_OPS.has(op)) {
|
|
2490
2569
|
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2491
2570
|
}
|
|
2492
|
-
return buildScalarOperator(
|
|
2493
|
-
expr,
|
|
2494
|
-
op,
|
|
2495
|
-
val,
|
|
2496
|
-
ctx.params,
|
|
2571
|
+
return buildScalarOperator(expr, op, val, ctx.params, {
|
|
2497
2572
|
mode,
|
|
2498
2573
|
fieldType,
|
|
2499
|
-
ctx.dialect
|
|
2500
|
-
);
|
|
2574
|
+
dialect: ctx.dialect
|
|
2575
|
+
});
|
|
2501
2576
|
}
|
|
2502
2577
|
|
|
2503
2578
|
// src/builder/shared/alias-generator.ts
|
|
@@ -2611,47 +2686,50 @@ function validateState(params, mappings, index) {
|
|
|
2611
2686
|
validateMappings(mappings);
|
|
2612
2687
|
assertNextIndexMatches(mappings.length, index);
|
|
2613
2688
|
}
|
|
2614
|
-
function
|
|
2615
|
-
let index = startIndex;
|
|
2616
|
-
const params = initialParams.length > 0 ? initialParams.slice() : [];
|
|
2617
|
-
const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
|
|
2689
|
+
function buildDynamicNameIndex(mappings) {
|
|
2618
2690
|
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2619
|
-
for (
|
|
2620
|
-
const m = mappings[i];
|
|
2691
|
+
for (const m of mappings) {
|
|
2621
2692
|
if (typeof m.dynamicName === "string") {
|
|
2622
2693
|
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2623
2694
|
}
|
|
2624
2695
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
function format(position) {
|
|
2635
|
-
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
|
+
);
|
|
2636
2703
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
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");
|
|
2643
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;
|
|
2644
2722
|
function addDynamic(dynamicName) {
|
|
2645
|
-
const dn =
|
|
2723
|
+
const dn = validateDynamicName(dynamicName);
|
|
2646
2724
|
const existing = dynamicNameToIndex.get(dn);
|
|
2647
|
-
if (existing !== void 0) return
|
|
2725
|
+
if (existing !== void 0) return formatPosition(existing);
|
|
2648
2726
|
const position = index;
|
|
2649
2727
|
dynamicNameToIndex.set(dn, position);
|
|
2650
2728
|
params.push(void 0);
|
|
2651
2729
|
mappings.push({ index: position, dynamicName: dn });
|
|
2652
2730
|
index++;
|
|
2653
2731
|
dirty = true;
|
|
2654
|
-
return
|
|
2732
|
+
return formatPosition(position);
|
|
2655
2733
|
}
|
|
2656
2734
|
function addStatic(value) {
|
|
2657
2735
|
const position = index;
|
|
@@ -2660,10 +2738,10 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
|
|
|
2660
2738
|
mappings.push({ index: position, value: normalizedValue });
|
|
2661
2739
|
index++;
|
|
2662
2740
|
dirty = true;
|
|
2663
|
-
return
|
|
2741
|
+
return formatPosition(position);
|
|
2664
2742
|
}
|
|
2665
2743
|
function add(value, dynamicName) {
|
|
2666
|
-
|
|
2744
|
+
assertCanAddParam(index);
|
|
2667
2745
|
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2668
2746
|
}
|
|
2669
2747
|
function addAuto(value) {
|
|
@@ -2884,6 +2962,17 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
|
|
|
2884
2962
|
var MAX_INCLUDE_DEPTH = 10;
|
|
2885
2963
|
var MAX_TOTAL_SUBQUERIES = 100;
|
|
2886
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
|
+
}
|
|
2887
2976
|
function getRelationTableReference(relModel, dialect) {
|
|
2888
2977
|
return buildTableReference(
|
|
2889
2978
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
@@ -3027,19 +3116,11 @@ function finalizeOrderByForInclude(args) {
|
|
|
3027
3116
|
}
|
|
3028
3117
|
function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
|
|
3029
3118
|
let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
|
|
3030
|
-
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
ctx.
|
|
3034
|
-
|
|
3035
|
-
relAlias,
|
|
3036
|
-
ctx.aliasGen,
|
|
3037
|
-
ctx.params,
|
|
3038
|
-
ctx.dialect,
|
|
3039
|
-
ctx.visitPath || [],
|
|
3040
|
-
(ctx.depth || 0) + 1,
|
|
3041
|
-
ctx.stats
|
|
3042
|
-
) : [];
|
|
3119
|
+
const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(relArgs, __spreadProps(__spreadValues({}, ctx), {
|
|
3120
|
+
model: relModel,
|
|
3121
|
+
parentAlias: relAlias,
|
|
3122
|
+
depth: (ctx.depth || 0) + 1
|
|
3123
|
+
})) : [];
|
|
3043
3124
|
if (isNonEmptyArray(nestedIncludes)) {
|
|
3044
3125
|
const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
|
|
3045
3126
|
const nestedSelects = nestedIncludes.map(
|
|
@@ -3094,6 +3175,7 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3094
3175
|
whereClause: args.whereClause
|
|
3095
3176
|
});
|
|
3096
3177
|
if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3178
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3097
3179
|
if (isNotNullish(args.takeVal)) {
|
|
3098
3180
|
return appendLimitOffset(
|
|
3099
3181
|
sql,
|
|
@@ -3101,15 +3183,10 @@ function buildOneToOneIncludeSql(args) {
|
|
|
3101
3183
|
args.ctx.params,
|
|
3102
3184
|
args.takeVal,
|
|
3103
3185
|
args.skipVal,
|
|
3104
|
-
|
|
3186
|
+
scopeBase
|
|
3105
3187
|
);
|
|
3106
3188
|
}
|
|
3107
|
-
return limitOneSql(
|
|
3108
|
-
sql,
|
|
3109
|
-
args.ctx.params,
|
|
3110
|
-
args.skipVal,
|
|
3111
|
-
`include.${args.relName}`
|
|
3112
|
-
);
|
|
3189
|
+
return limitOneSql(sql, args.ctx.params, args.skipVal, scopeBase);
|
|
3113
3190
|
}
|
|
3114
3191
|
function buildListIncludeSpec(args) {
|
|
3115
3192
|
const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
|
|
@@ -3137,13 +3214,14 @@ function buildListIncludeSpec(args) {
|
|
|
3137
3214
|
whereClause: args.whereClause
|
|
3138
3215
|
});
|
|
3139
3216
|
if (args.orderBySql) base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
|
|
3217
|
+
const scopeBase = buildIncludeScope(args.ctx.includePath);
|
|
3140
3218
|
base = appendLimitOffset(
|
|
3141
3219
|
base,
|
|
3142
3220
|
args.ctx.dialect,
|
|
3143
3221
|
args.ctx.params,
|
|
3144
3222
|
args.takeVal,
|
|
3145
3223
|
args.skipVal,
|
|
3146
|
-
|
|
3224
|
+
scopeBase
|
|
3147
3225
|
);
|
|
3148
3226
|
const selectExpr = jsonAgg("row", args.ctx.dialect);
|
|
3149
3227
|
const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
|
|
@@ -3192,7 +3270,6 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3192
3270
|
);
|
|
3193
3271
|
if (!isList) {
|
|
3194
3272
|
const sql = buildOneToOneIncludeSql({
|
|
3195
|
-
relName,
|
|
3196
3273
|
relTable,
|
|
3197
3274
|
relAlias,
|
|
3198
3275
|
joins: whereParts.joins,
|
|
@@ -3220,8 +3297,14 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
|
|
|
3220
3297
|
ctx
|
|
3221
3298
|
});
|
|
3222
3299
|
}
|
|
3223
|
-
function buildIncludeSqlInternal(args,
|
|
3224
|
-
|
|
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 || [];
|
|
3225
3308
|
if (depth > MAX_INCLUDE_DEPTH) {
|
|
3226
3309
|
throw new Error(
|
|
3227
3310
|
`Maximum include depth of ${MAX_INCLUDE_DEPTH} exceeded. Path: ${visitPath.join(" -> ")}. Deep includes cause exponential SQL complexity and performance issues.`
|
|
@@ -3229,7 +3312,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3229
3312
|
}
|
|
3230
3313
|
stats.maxDepth = Math.max(stats.maxDepth, depth);
|
|
3231
3314
|
const includes = [];
|
|
3232
|
-
const entries = relationEntriesFromArgs(args, model);
|
|
3315
|
+
const entries = relationEntriesFromArgs(args, ctx.model);
|
|
3233
3316
|
for (const [relName, relArgs] of entries) {
|
|
3234
3317
|
if (relArgs === false) continue;
|
|
3235
3318
|
stats.totalIncludes++;
|
|
@@ -3244,8 +3327,12 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3244
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.`
|
|
3245
3328
|
);
|
|
3246
3329
|
}
|
|
3247
|
-
const resolved = resolveRelationOrThrow(
|
|
3248
|
-
|
|
3330
|
+
const resolved = resolveRelationOrThrow(
|
|
3331
|
+
ctx.model,
|
|
3332
|
+
ctx.schemaByName,
|
|
3333
|
+
relName
|
|
3334
|
+
);
|
|
3335
|
+
const relationPath = `${ctx.model.name}.${relName}`;
|
|
3249
3336
|
const currentPath = [...visitPath, relationPath];
|
|
3250
3337
|
if (visitPath.includes(relationPath)) {
|
|
3251
3338
|
throw new Error(
|
|
@@ -3260,19 +3347,14 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
|
|
|
3260
3347
|
`Include too deeply nested: model '${resolved.relModel.name}' appears ${modelOccurrences} times in path: ${currentPath.join(" -> ")}`
|
|
3261
3348
|
);
|
|
3262
3349
|
}
|
|
3350
|
+
const nextIncludePath = [...ctx.includePath, relName];
|
|
3263
3351
|
includes.push(
|
|
3264
|
-
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, {
|
|
3265
|
-
|
|
3266
|
-
schemas,
|
|
3267
|
-
schemaByName,
|
|
3268
|
-
parentAlias,
|
|
3269
|
-
aliasGen,
|
|
3270
|
-
dialect,
|
|
3271
|
-
params,
|
|
3352
|
+
buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
|
|
3353
|
+
includePath: nextIncludePath,
|
|
3272
3354
|
visitPath: currentPath,
|
|
3273
3355
|
depth: depth + 1,
|
|
3274
3356
|
stats
|
|
3275
|
-
})
|
|
3357
|
+
}))
|
|
3276
3358
|
);
|
|
3277
3359
|
}
|
|
3278
3360
|
return includes;
|
|
@@ -3286,8 +3368,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3286
3368
|
};
|
|
3287
3369
|
const schemaByName = /* @__PURE__ */ new Map();
|
|
3288
3370
|
for (const m of schemas) schemaByName.set(m.name, m);
|
|
3289
|
-
return buildIncludeSqlInternal(
|
|
3290
|
-
args,
|
|
3371
|
+
return buildIncludeSqlInternal(args, {
|
|
3291
3372
|
model,
|
|
3292
3373
|
schemas,
|
|
3293
3374
|
schemaByName,
|
|
@@ -3295,10 +3376,11 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
|
|
|
3295
3376
|
aliasGen,
|
|
3296
3377
|
params,
|
|
3297
3378
|
dialect,
|
|
3298
|
-
[],
|
|
3299
|
-
|
|
3379
|
+
includePath: [],
|
|
3380
|
+
visitPath: [],
|
|
3381
|
+
depth: 0,
|
|
3300
3382
|
stats
|
|
3301
|
-
);
|
|
3383
|
+
});
|
|
3302
3384
|
}
|
|
3303
3385
|
function resolveCountRelationOrThrow(relName, model, schemaByName) {
|
|
3304
3386
|
const relationSet = getRelationFieldSet(model);
|
|
@@ -3343,8 +3425,7 @@ function resolveCountKeyPairs(field) {
|
|
|
3343
3425
|
if (fkFields.length === 0) {
|
|
3344
3426
|
throw new Error("Relation count requires foreignKey");
|
|
3345
3427
|
}
|
|
3346
|
-
const
|
|
3347
|
-
const refs = normalizeKeyList(refsRaw);
|
|
3428
|
+
const refs = normalizeKeyList(field.references);
|
|
3348
3429
|
const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
|
|
3349
3430
|
if (refFields.length !== fkFields.length) {
|
|
3350
3431
|
throw new Error(
|
|
@@ -3479,132 +3560,139 @@ function finalizeSql(sql, params, dialect) {
|
|
|
3479
3560
|
paramMappings: snapshot.mappings
|
|
3480
3561
|
};
|
|
3481
3562
|
}
|
|
3482
|
-
function
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
i2 += 2;
|
|
3504
|
-
continue;
|
|
3505
|
-
}
|
|
3506
|
-
if (!saw)
|
|
3507
|
-
throw new Error(
|
|
3508
|
-
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3509
|
-
);
|
|
3510
|
-
return { text: out, next: i2 + 1, quoted: true };
|
|
3511
|
-
}
|
|
3512
|
-
out += s[i2];
|
|
3513
|
-
saw = true;
|
|
3514
|
-
i2++;
|
|
3515
|
-
}
|
|
3516
|
-
throw new Error(
|
|
3517
|
-
`sqlite distinct emulation: unterminated quoted identifier in: ${s}`
|
|
3518
|
-
);
|
|
3519
|
-
}
|
|
3520
|
-
let i = start;
|
|
3521
|
-
while (i < n) {
|
|
3522
|
-
const c = s.charCodeAt(i);
|
|
3523
|
-
if (c === 32 || c === 9) break;
|
|
3524
|
-
if (c === 46) break;
|
|
3525
|
-
i++;
|
|
3526
|
-
}
|
|
3527
|
-
return { text: s.slice(start, i), next: i, quoted: false };
|
|
3528
|
-
};
|
|
3529
|
-
const skipSpaces = (s, i) => {
|
|
3530
|
-
while (i < s.length) {
|
|
3531
|
-
const c = s.charCodeAt(i);
|
|
3532
|
-
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;
|
|
3533
3584
|
i++;
|
|
3585
|
+
continue;
|
|
3534
3586
|
}
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
i = skipSpaces(p, i);
|
|
3542
|
-
const a = readIdentOrQuoted(p, i);
|
|
3543
|
-
const actualAlias = a.text.toLowerCase();
|
|
3544
|
-
if (!isIdent(a.text)) {
|
|
3545
|
-
throw new Error(
|
|
3546
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
|
|
3547
|
-
);
|
|
3548
|
-
}
|
|
3549
|
-
if (actualAlias !== fromLower) {
|
|
3550
|
-
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;
|
|
3551
3593
|
}
|
|
3552
|
-
|
|
3553
|
-
if (i >= p.length || p.charCodeAt(i) !== 46) {
|
|
3594
|
+
if (!saw) {
|
|
3554
3595
|
throw new Error(
|
|
3555
|
-
`sqlite distinct emulation
|
|
3596
|
+
`sqlite distinct emulation: empty quoted identifier in: ${s}`
|
|
3556
3597
|
);
|
|
3557
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;
|
|
3558
3611
|
i++;
|
|
3559
|
-
i = skipSpaces(p, i);
|
|
3560
|
-
const colPart = readIdentOrQuoted(p, i);
|
|
3561
|
-
const columnName = colPart.text.trim();
|
|
3562
|
-
if (columnName.length === 0) {
|
|
3563
|
-
throw new Error(`Failed to parse selected column name from: ${p}`);
|
|
3564
|
-
}
|
|
3565
|
-
i = colPart.next;
|
|
3566
|
-
i = skipSpaces(p, i);
|
|
3567
|
-
let outAlias = "";
|
|
3568
|
-
if (i < p.length) {
|
|
3569
|
-
const rest = p.slice(i).trim();
|
|
3570
|
-
if (rest.length > 0) {
|
|
3571
|
-
const m = rest.match(/^AS\s+/i);
|
|
3572
|
-
if (!m) {
|
|
3573
|
-
throw new Error(
|
|
3574
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3575
|
-
);
|
|
3576
|
-
}
|
|
3577
|
-
let j = i;
|
|
3578
|
-
j = skipSpaces(p, j);
|
|
3579
|
-
if (!/^AS\b/i.test(p.slice(j))) {
|
|
3580
|
-
throw new Error(`Failed to parse AS in: ${p}`);
|
|
3581
|
-
}
|
|
3582
|
-
j += 2;
|
|
3583
|
-
j = skipSpaces(p, j);
|
|
3584
|
-
const out = readIdentOrQuoted(p, j);
|
|
3585
|
-
outAlias = out.text.trim();
|
|
3586
|
-
if (outAlias.length === 0) {
|
|
3587
|
-
throw new Error(`Failed to parse output alias from: ${p}`);
|
|
3588
|
-
}
|
|
3589
|
-
j = skipSpaces(p, out.next);
|
|
3590
|
-
if (j !== p.length) {
|
|
3591
|
-
throw new Error(
|
|
3592
|
-
`sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
|
|
3593
|
-
);
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
const name = outAlias.length > 0 ? outAlias : columnName;
|
|
3598
|
-
names.push(name);
|
|
3599
3612
|
}
|
|
3600
|
-
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;
|
|
3601
3684
|
}
|
|
3602
|
-
function
|
|
3603
|
-
const
|
|
3604
|
-
if (
|
|
3605
|
-
const
|
|
3606
|
-
const
|
|
3607
|
-
|
|
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;
|
|
3608
3696
|
}
|
|
3609
3697
|
function buildDistinctColumns(distinct, fromAlias, model) {
|
|
3610
3698
|
return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
@@ -3628,14 +3716,60 @@ function buildWindowOrder(args) {
|
|
|
3628
3716
|
const idTiebreaker = idField ? ", " + col(fromAlias, "id", model) + " ASC" : "";
|
|
3629
3717
|
return baseOrder + idTiebreaker;
|
|
3630
3718
|
}
|
|
3631
|
-
function
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
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
|
+
}
|
|
3765
|
+
function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
3766
|
+
var _a, _b;
|
|
3767
|
+
const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
|
|
3768
|
+
if (!isNotNullish(distinct) || !isNonEmptyArray(distinct)) {
|
|
3769
|
+
throw new Error("buildSqliteDistinctQuery requires distinct fields");
|
|
3770
|
+
}
|
|
3771
|
+
const scalarNames = parseSimpleScalarSelect(spec.select, from.alias);
|
|
3772
|
+
const includeNames = includes.map((i) => i.name);
|
|
3639
3773
|
const hasCount = Boolean((_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count);
|
|
3640
3774
|
const outerSelectCols = buildOutputColumns(
|
|
3641
3775
|
scalarNames,
|
|
@@ -3654,7 +3788,8 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3654
3788
|
fromAlias: from.alias,
|
|
3655
3789
|
model
|
|
3656
3790
|
});
|
|
3657
|
-
const
|
|
3791
|
+
const outerEntries = extractDistinctOrderEntries(spec);
|
|
3792
|
+
const outerOrder = buildFieldNameOrderBy(outerEntries, '"__tp_distinct"');
|
|
3658
3793
|
const joins = buildJoinsSql(whereJoins, countJoins);
|
|
3659
3794
|
const conditions = [];
|
|
3660
3795
|
if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
|
|
@@ -3689,17 +3824,33 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3689
3824
|
}
|
|
3690
3825
|
return outerParts.join(" ");
|
|
3691
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
|
+
}
|
|
3692
3842
|
function buildIncludeColumns(spec) {
|
|
3693
3843
|
var _a, _b;
|
|
3694
3844
|
const { select, includes, dialect, model, schemas, from, params } = spec;
|
|
3695
3845
|
const baseSelect = (select != null ? select : "").trim();
|
|
3696
3846
|
let countCols = "";
|
|
3697
3847
|
let countJoins = [];
|
|
3698
|
-
const
|
|
3699
|
-
if (
|
|
3700
|
-
|
|
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) {
|
|
3701
3852
|
const countBuild = buildRelationCountSql(
|
|
3702
|
-
|
|
3853
|
+
resolvedCountSelect,
|
|
3703
3854
|
model,
|
|
3704
3855
|
schemas,
|
|
3705
3856
|
from.alias,
|
|
@@ -4524,32 +4675,32 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
|
|
|
4524
4675
|
paramMappings: allMappings
|
|
4525
4676
|
};
|
|
4526
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
|
+
}
|
|
4527
4700
|
function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
|
|
4528
4701
|
assertSafeAlias(alias);
|
|
4529
4702
|
assertSafeTableRef(tableName);
|
|
4530
|
-
|
|
4531
|
-
if (isDynamicParameter(skip)) {
|
|
4532
|
-
throw new Error(
|
|
4533
|
-
"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."
|
|
4534
|
-
);
|
|
4535
|
-
}
|
|
4536
|
-
if (typeof skip === "string") {
|
|
4537
|
-
const s = skip.trim();
|
|
4538
|
-
if (s.length > 0) {
|
|
4539
|
-
const n = Number(s);
|
|
4540
|
-
if (Number.isFinite(n) && Number.isInteger(n) && n > 0) {
|
|
4541
|
-
throw new Error(
|
|
4542
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4543
|
-
);
|
|
4544
|
-
}
|
|
4545
|
-
}
|
|
4546
|
-
}
|
|
4547
|
-
if (typeof skip === "number" && Number.isFinite(skip) && Number.isInteger(skip) && skip > 0) {
|
|
4548
|
-
throw new Error(
|
|
4549
|
-
"count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
|
|
4550
|
-
);
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4703
|
+
validateSkipParameter(skip);
|
|
4553
4704
|
const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
|
|
4554
4705
|
const parts = [
|
|
4555
4706
|
SQL_TEMPLATES.SELECT,
|
|
@@ -4774,62 +4925,6 @@ function generateSQL(directive) {
|
|
|
4774
4925
|
});
|
|
4775
4926
|
}
|
|
4776
4927
|
|
|
4777
|
-
// src/result-transformers.ts
|
|
4778
|
-
function parseAggregateValue(value) {
|
|
4779
|
-
if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
|
|
4780
|
-
return parseFloat(value);
|
|
4781
|
-
}
|
|
4782
|
-
return value;
|
|
4783
|
-
}
|
|
4784
|
-
function transformGroupByResults(results) {
|
|
4785
|
-
return results.map((row) => {
|
|
4786
|
-
const raw = row;
|
|
4787
|
-
const parsed = {};
|
|
4788
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
4789
|
-
const parts = key.split(".");
|
|
4790
|
-
if (parts.length === 2) {
|
|
4791
|
-
const [group, field] = parts;
|
|
4792
|
-
if (!parsed[group]) parsed[group] = {};
|
|
4793
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
4794
|
-
} else {
|
|
4795
|
-
parsed[key] = value;
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
return parsed;
|
|
4799
|
-
});
|
|
4800
|
-
}
|
|
4801
|
-
function transformCountResults(results) {
|
|
4802
|
-
const result = results[0];
|
|
4803
|
-
const count = (result == null ? void 0 : result["_count._all"]) || (result == null ? void 0 : result.count) || 0;
|
|
4804
|
-
return typeof count === "string" ? parseInt(count, 10) : count;
|
|
4805
|
-
}
|
|
4806
|
-
function transformAggregateResults(results) {
|
|
4807
|
-
const raw = results[0] || {};
|
|
4808
|
-
const parsed = {};
|
|
4809
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
4810
|
-
const parts = key.split(".");
|
|
4811
|
-
if (parts.length === 2) {
|
|
4812
|
-
const [group, field] = parts;
|
|
4813
|
-
if (!parsed[group]) parsed[group] = {};
|
|
4814
|
-
parsed[group][field] = parseAggregateValue(value);
|
|
4815
|
-
} else {
|
|
4816
|
-
parsed[key] = value;
|
|
4817
|
-
}
|
|
4818
|
-
}
|
|
4819
|
-
return parsed;
|
|
4820
|
-
}
|
|
4821
|
-
var RESULT_TRANSFORMERS = {
|
|
4822
|
-
findFirst: (results) => results[0] || null,
|
|
4823
|
-
findUnique: (results) => results[0] || null,
|
|
4824
|
-
count: transformCountResults,
|
|
4825
|
-
aggregate: transformAggregateResults,
|
|
4826
|
-
groupBy: transformGroupByResults
|
|
4827
|
-
};
|
|
4828
|
-
function transformQueryResults(method, results) {
|
|
4829
|
-
const transformer = RESULT_TRANSFORMERS[method];
|
|
4830
|
-
return transformer ? transformer(results) : results;
|
|
4831
|
-
}
|
|
4832
|
-
|
|
4833
4928
|
// src/utils/s3-fifo.ts
|
|
4834
4929
|
function withDispose(it) {
|
|
4835
4930
|
const anyIt = it;
|
|
@@ -5049,89 +5144,309 @@ function createBoundedCache(maxSize) {
|
|
|
5049
5144
|
return new BoundedCache(maxSize);
|
|
5050
5145
|
}
|
|
5051
5146
|
|
|
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
|
+
};
|
|
5155
|
+
}
|
|
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)}`);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
return cols.join(", ");
|
|
5169
|
+
}
|
|
5170
|
+
function getTableAndAlias(model, dialect) {
|
|
5171
|
+
const tableName = buildTableReference(
|
|
5172
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5173
|
+
model.tableName,
|
|
5174
|
+
dialect
|
|
5175
|
+
);
|
|
5176
|
+
const alias = makeAlias(model.tableName);
|
|
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"
|
|
5206
|
+
);
|
|
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
|
+
|
|
5052
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();
|
|
5053
5335
|
var queryCache = createBoundedCache(1e3);
|
|
5336
|
+
var queryCacheStats = new QueryCacheStats();
|
|
5054
5337
|
function makeAlias(name) {
|
|
5055
5338
|
const base = name.toLowerCase().replace(/[^a-z0-9_]/g, "_").slice(0, 50);
|
|
5056
5339
|
const safe = /^[a-z_]/.test(base) ? base : `_${base}`;
|
|
5057
5340
|
return SQL_RESERVED_WORDS.has(safe) ? `${safe}_t` : safe;
|
|
5058
5341
|
}
|
|
5059
|
-
function
|
|
5060
|
-
const reorderedParams = [];
|
|
5342
|
+
function handleSingleQuote(sql, state) {
|
|
5061
5343
|
const n = sql.length;
|
|
5062
|
-
let i =
|
|
5063
|
-
let out =
|
|
5064
|
-
|
|
5344
|
+
let i = state.position;
|
|
5345
|
+
let out = state.output + sql[i];
|
|
5346
|
+
i++;
|
|
5065
5347
|
while (i < n) {
|
|
5066
|
-
|
|
5067
|
-
if (
|
|
5068
|
-
if (
|
|
5069
|
-
out += sql[i];
|
|
5070
|
-
|
|
5071
|
-
i++;
|
|
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;
|
|
5072
5353
|
continue;
|
|
5073
5354
|
}
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
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;
|
|
5078
5372
|
continue;
|
|
5079
5373
|
}
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
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);
|
|
5103
5426
|
continue;
|
|
5104
5427
|
}
|
|
5105
|
-
if (mode === "
|
|
5106
|
-
|
|
5107
|
-
if (ch === 39) {
|
|
5108
|
-
if (i + 1 < n && sql.charCodeAt(i + 1) === 39) {
|
|
5109
|
-
out += sql[i + 1];
|
|
5110
|
-
i += 2;
|
|
5111
|
-
continue;
|
|
5112
|
-
}
|
|
5113
|
-
mode = "normal";
|
|
5114
|
-
}
|
|
5115
|
-
i++;
|
|
5428
|
+
if (state.mode === "double") {
|
|
5429
|
+
state = handleDoubleQuote(sql, state);
|
|
5116
5430
|
continue;
|
|
5117
5431
|
}
|
|
5118
|
-
if (
|
|
5119
|
-
|
|
5120
|
-
if (ch === 34) {
|
|
5121
|
-
if (i + 1 < n && sql.charCodeAt(i + 1) === 34) {
|
|
5122
|
-
out += sql[i + 1];
|
|
5123
|
-
i += 2;
|
|
5124
|
-
continue;
|
|
5125
|
-
}
|
|
5126
|
-
mode = "normal";
|
|
5127
|
-
}
|
|
5128
|
-
i++;
|
|
5432
|
+
if (ch === 39) {
|
|
5433
|
+
state = __spreadProps(__spreadValues({}, state), { mode: "single" });
|
|
5129
5434
|
continue;
|
|
5130
5435
|
}
|
|
5131
|
-
|
|
5132
|
-
|
|
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
|
+
});
|
|
5133
5448
|
}
|
|
5134
|
-
return { sql:
|
|
5449
|
+
return { sql: state.output, params: state.reorderedParams };
|
|
5135
5450
|
}
|
|
5136
5451
|
function canonicalizeReplacer(key, value) {
|
|
5137
5452
|
if (typeof value === "bigint") return `__bigint__${value.toString()}`;
|
|
@@ -5139,11 +5454,7 @@ function canonicalizeReplacer(key, value) {
|
|
|
5139
5454
|
const obj = value;
|
|
5140
5455
|
const sorted = {};
|
|
5141
5456
|
for (const k of Object.keys(obj).sort()) {
|
|
5142
|
-
|
|
5143
|
-
if (v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0) {
|
|
5144
|
-
continue;
|
|
5145
|
-
}
|
|
5146
|
-
sorted[k] = v;
|
|
5457
|
+
sorted[k] = obj[k];
|
|
5147
5458
|
}
|
|
5148
5459
|
return sorted;
|
|
5149
5460
|
}
|
|
@@ -5217,11 +5528,20 @@ function buildSQLWithCache(model, models, method, args, dialect) {
|
|
|
5217
5528
|
const cacheKey = canonicalizeQuery(model.name, method, args, dialect);
|
|
5218
5529
|
const cached = queryCache.get(cacheKey);
|
|
5219
5530
|
if (cached) {
|
|
5531
|
+
queryCacheStats.hit();
|
|
5220
5532
|
return { sql: cached.sql, params: [...cached.params] };
|
|
5221
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;
|
|
5542
|
+
}
|
|
5222
5543
|
const result = buildSQLFull(model, models, method, args, dialect);
|
|
5223
|
-
queryCache.set(cacheKey, result);
|
|
5224
|
-
queryCache.size;
|
|
5544
|
+
queryCache.set(cacheKey, { sql: result.sql, params: [...result.params] });
|
|
5225
5545
|
return result;
|
|
5226
5546
|
}
|
|
5227
5547
|
|
|
@@ -5234,159 +5554,172 @@ function quoteBatchIdent(id) {
|
|
|
5234
5554
|
function makeBatchAlias(i) {
|
|
5235
5555
|
return `k${i}`;
|
|
5236
5556
|
}
|
|
5237
|
-
function
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
continue;
|
|
5334
|
-
}
|
|
5335
|
-
mode = "normal";
|
|
5336
|
-
i++;
|
|
5337
|
-
continue;
|
|
5338
|
-
}
|
|
5339
|
-
i++;
|
|
5340
|
-
continue;
|
|
5341
|
-
}
|
|
5342
|
-
if (mode === "double") {
|
|
5343
|
-
out += s[i];
|
|
5344
|
-
if (ch === 34) {
|
|
5345
|
-
if (i + 1 < n && s.charCodeAt(i + 1) === 34) {
|
|
5346
|
-
out += s[i + 1];
|
|
5347
|
-
i += 2;
|
|
5348
|
-
continue;
|
|
5349
|
-
}
|
|
5350
|
-
mode = "normal";
|
|
5351
|
-
i++;
|
|
5352
|
-
continue;
|
|
5353
|
-
}
|
|
5354
|
-
i++;
|
|
5355
|
-
continue;
|
|
5356
|
-
}
|
|
5357
|
-
if (mode === "lineComment") {
|
|
5358
|
-
out += s[i];
|
|
5359
|
-
if (ch === 10) {
|
|
5360
|
-
mode = "normal";
|
|
5361
|
-
}
|
|
5362
|
-
i++;
|
|
5363
|
-
continue;
|
|
5364
|
-
}
|
|
5365
|
-
if (mode === "blockComment") {
|
|
5366
|
-
if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
|
|
5367
|
-
out += "*/";
|
|
5368
|
-
i += 2;
|
|
5369
|
-
mode = "normal";
|
|
5370
|
-
continue;
|
|
5371
|
-
}
|
|
5372
|
-
out += s[i];
|
|
5373
|
-
i++;
|
|
5374
|
-
continue;
|
|
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 "$$";
|
|
5574
|
+
}
|
|
5575
|
+
return null;
|
|
5576
|
+
}
|
|
5577
|
+
function parseParamPlaceholder(s, i, n, replace) {
|
|
5578
|
+
let j = i + 1;
|
|
5579
|
+
if (j >= n) {
|
|
5580
|
+
return { consumed: 1, output: s[i] };
|
|
5581
|
+
}
|
|
5582
|
+
const c1 = s.charCodeAt(j);
|
|
5583
|
+
if (!isDigit(c1)) {
|
|
5584
|
+
return { consumed: 1, output: s[i] };
|
|
5585
|
+
}
|
|
5586
|
+
while (j < n && isDigit(s.charCodeAt(j))) {
|
|
5587
|
+
j++;
|
|
5588
|
+
}
|
|
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}`);
|
|
5593
|
+
}
|
|
5594
|
+
return { consumed: j - i, output: replace(oldIndex) };
|
|
5595
|
+
}
|
|
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 });
|
|
5607
|
+
}
|
|
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 };
|
|
5375
5653
|
}
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
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
|
+
};
|
|
5387
5694
|
}
|
|
5388
|
-
|
|
5389
|
-
|
|
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;
|
|
5711
|
+
}
|
|
5712
|
+
function replacePgPlaceholders(sql, replace) {
|
|
5713
|
+
const s = String(sql);
|
|
5714
|
+
const n = s.length;
|
|
5715
|
+
let i = 0;
|
|
5716
|
+
let state = { mode: "normal", dollarTag: null };
|
|
5717
|
+
let out = "";
|
|
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);
|
|
5390
5723
|
}
|
|
5391
5724
|
return out;
|
|
5392
5725
|
}
|
|
@@ -5439,21 +5772,70 @@ function wrapQueryForMethod(method, cteName, resultAlias) {
|
|
|
5439
5772
|
}
|
|
5440
5773
|
function isAllCountQueries(queries, keys) {
|
|
5441
5774
|
var _a;
|
|
5442
|
-
for (
|
|
5443
|
-
if (((_a = queries[
|
|
5775
|
+
for (const key of keys) {
|
|
5776
|
+
if (((_a = queries[key]) == null ? void 0 : _a.method) !== "count") return false;
|
|
5444
5777
|
}
|
|
5445
5778
|
return true;
|
|
5446
5779
|
}
|
|
5447
5780
|
function looksTooComplexForFilter(sql) {
|
|
5448
5781
|
const s = sql.toLowerCase();
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
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));
|
|
5792
|
+
}
|
|
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;
|
|
5457
5839
|
}
|
|
5458
5840
|
function parseSimpleCountSql(sql) {
|
|
5459
5841
|
const trimmed = sql.trim().replace(/;$/, "").trim();
|
|
@@ -5461,111 +5843,181 @@ function parseSimpleCountSql(sql) {
|
|
|
5461
5843
|
if (!lower.startsWith("select")) return null;
|
|
5462
5844
|
if (!lower.includes("count(*)")) return null;
|
|
5463
5845
|
if (looksTooComplexForFilter(trimmed)) return null;
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
);
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
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();
|
|
5470
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
|
+
}
|
|
5471
5878
|
return { fromSql, whereSql };
|
|
5472
5879
|
}
|
|
5473
|
-
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) {
|
|
5474
5938
|
const modelGroups = /* @__PURE__ */ new Map();
|
|
5475
|
-
for (
|
|
5476
|
-
const key = keys[i];
|
|
5939
|
+
for (const key of keys) {
|
|
5477
5940
|
const q = queries[key];
|
|
5478
5941
|
const alias = aliasesByKey.get(key);
|
|
5479
5942
|
if (!alias) return null;
|
|
5480
|
-
if (!modelGroups.has(q.model))
|
|
5481
|
-
|
|
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
|
+
}
|
|
5482
5954
|
}
|
|
5955
|
+
return modelGroups;
|
|
5956
|
+
}
|
|
5957
|
+
function buildSubqueriesFromGroups(modelGroups, modelMap, models, dialect) {
|
|
5483
5958
|
const subqueries = [];
|
|
5484
5959
|
let aliasIndex = 0;
|
|
5485
5960
|
for (const [modelName, items] of modelGroups) {
|
|
5486
5961
|
const model = modelMap.get(modelName);
|
|
5487
5962
|
if (!model) return null;
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
if (!parsed) return null;
|
|
5498
|
-
if (containsPgPlaceholder(parsed.fromSql)) {
|
|
5499
|
-
return null;
|
|
5500
|
-
}
|
|
5501
|
-
if (!parsed.whereSql) {
|
|
5502
|
-
if (built.params.length > 0) return null;
|
|
5503
|
-
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5504
|
-
if (sharedFrom !== parsed.fromSql) return null;
|
|
5505
|
-
expressions.push(`count(*) AS ${quoteBatchIdent(alias2)}`);
|
|
5506
|
-
localKeys.push(key);
|
|
5507
|
-
localAliases.push(alias2);
|
|
5508
|
-
continue;
|
|
5509
|
-
}
|
|
5510
|
-
if (sharedFrom === null) sharedFrom = parsed.fromSql;
|
|
5511
|
-
if (sharedFrom !== parsed.fromSql) return null;
|
|
5512
|
-
const re = reindexParams(
|
|
5513
|
-
parsed.whereSql,
|
|
5514
|
-
built.params,
|
|
5515
|
-
localParams.length
|
|
5516
|
-
);
|
|
5517
|
-
for (let p = 0; p < re.params.length; p++) localParams.push(re.params[p]);
|
|
5518
|
-
expressions.push(
|
|
5519
|
-
`count(*) FILTER (WHERE ${re.sql}) AS ${quoteBatchIdent(alias2)}`
|
|
5520
|
-
);
|
|
5521
|
-
localKeys.push(key);
|
|
5522
|
-
localAliases.push(alias2);
|
|
5523
|
-
}
|
|
5524
|
-
if (!sharedFrom) return null;
|
|
5525
|
-
const alias = `m_${aliasIndex++}`;
|
|
5526
|
-
const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
|
|
5527
|
-
subqueries.push({
|
|
5528
|
-
alias,
|
|
5529
|
-
sql: subSql,
|
|
5530
|
-
params: localParams,
|
|
5531
|
-
keys: localKeys,
|
|
5532
|
-
aliases: localAliases
|
|
5533
|
-
});
|
|
5963
|
+
const subquery = buildCountSubqueriesForModel(
|
|
5964
|
+
items,
|
|
5965
|
+
model,
|
|
5966
|
+
models,
|
|
5967
|
+
dialect,
|
|
5968
|
+
aliasIndex++
|
|
5969
|
+
);
|
|
5970
|
+
if (!subquery) return null;
|
|
5971
|
+
subqueries.push(subquery);
|
|
5534
5972
|
}
|
|
5535
|
-
|
|
5973
|
+
return subqueries.length > 0 ? subqueries : null;
|
|
5974
|
+
}
|
|
5975
|
+
function reindexSubqueries(subqueries) {
|
|
5536
5976
|
let offset = 0;
|
|
5537
5977
|
const rewrittenSubs = [];
|
|
5538
5978
|
const finalParams = [];
|
|
5539
|
-
for (
|
|
5540
|
-
const sq = subqueries[i];
|
|
5979
|
+
for (const sq of subqueries) {
|
|
5541
5980
|
const re = reindexParams(sq.sql, sq.params, offset);
|
|
5542
5981
|
offset += re.params.length;
|
|
5543
5982
|
rewrittenSubs.push(re.sql);
|
|
5544
|
-
for (
|
|
5983
|
+
for (const p of re.params) {
|
|
5984
|
+
finalParams.push(p);
|
|
5985
|
+
}
|
|
5545
5986
|
}
|
|
5987
|
+
return { sql: rewrittenSubs, params: finalParams };
|
|
5988
|
+
}
|
|
5989
|
+
function buildSelectParts(subqueries) {
|
|
5546
5990
|
const selectParts = [];
|
|
5547
|
-
for (
|
|
5548
|
-
const sq
|
|
5549
|
-
for (let k = 0; k < sq.aliases.length; k++) {
|
|
5550
|
-
const outAlias = sq.aliases[k];
|
|
5991
|
+
for (const sq of subqueries) {
|
|
5992
|
+
for (const outAlias of sq.aliases) {
|
|
5551
5993
|
selectParts.push(
|
|
5552
5994
|
`${sq.alias}.${quoteBatchIdent(outAlias)} AS ${quoteBatchIdent(outAlias)}`
|
|
5553
5995
|
);
|
|
5554
5996
|
}
|
|
5555
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);
|
|
5556
6012
|
const fromSql = rewrittenSubs.join(" CROSS JOIN ");
|
|
5557
6013
|
const sql = `SELECT ${selectParts.join(", ")} FROM ${fromSql}`;
|
|
5558
|
-
const aliases = keys.map((k) =>
|
|
6014
|
+
const aliases = keys.map((k) => {
|
|
6015
|
+
var _a;
|
|
6016
|
+
return (_a = aliasesByKey.get(k)) != null ? _a : "";
|
|
6017
|
+
});
|
|
5559
6018
|
return { sql, params: finalParams, keys, aliases };
|
|
5560
6019
|
}
|
|
5561
|
-
function
|
|
5562
|
-
const keys = Object.keys(queries);
|
|
5563
|
-
if (keys.length === 0) {
|
|
5564
|
-
throw new Error("buildBatchSql requires at least one query");
|
|
5565
|
-
}
|
|
5566
|
-
if (dialect !== "postgres") {
|
|
5567
|
-
throw new Error("Batch queries are only supported for postgres dialect");
|
|
5568
|
-
}
|
|
6020
|
+
function buildAliasesForKeys(keys) {
|
|
5569
6021
|
const aliases = new Array(keys.length);
|
|
5570
6022
|
const aliasesByKey = /* @__PURE__ */ new Map();
|
|
5571
6023
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -5573,17 +6025,9 @@ function buildBatchSql(queries, modelMap, models, dialect) {
|
|
|
5573
6025
|
aliases[i] = a;
|
|
5574
6026
|
aliasesByKey.set(keys[i], a);
|
|
5575
6027
|
}
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
keys,
|
|
5580
|
-
aliasesByKey,
|
|
5581
|
-
modelMap,
|
|
5582
|
-
models,
|
|
5583
|
-
dialect
|
|
5584
|
-
);
|
|
5585
|
-
if (merged) return merged;
|
|
5586
|
-
}
|
|
6028
|
+
return { aliases, aliasesByKey };
|
|
6029
|
+
}
|
|
6030
|
+
function buildRegularBatchQueries(queries, keys, aliases, modelMap, models, dialect) {
|
|
5587
6031
|
const ctes = new Array(keys.length);
|
|
5588
6032
|
const selects = new Array(keys.length);
|
|
5589
6033
|
const allParams = [];
|
|
@@ -5608,695 +6052,51 @@ function buildBatchSql(queries, modelMap, models, dialect) {
|
|
|
5608
6052
|
queryParams,
|
|
5609
6053
|
allParams.length
|
|
5610
6054
|
);
|
|
5611
|
-
for (
|
|
5612
|
-
allParams.push(
|
|
6055
|
+
for (const p of reindexedParams) {
|
|
6056
|
+
allParams.push(p);
|
|
5613
6057
|
}
|
|
5614
6058
|
const cteName = `batch_${i}`;
|
|
5615
6059
|
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5616
6060
|
selects[i] = wrapQueryForMethod(query.method, cteName, aliases[i]);
|
|
5617
6061
|
}
|
|
5618
6062
|
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5619
|
-
return { sql, params: allParams
|
|
6063
|
+
return { sql, params: allParams };
|
|
5620
6064
|
}
|
|
5621
|
-
function
|
|
5622
|
-
|
|
5623
|
-
|
|
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");
|
|
5624
6069
|
}
|
|
5625
6070
|
if (dialect !== "postgres") {
|
|
5626
|
-
throw new Error(
|
|
5627
|
-
"Batch count queries are only supported for postgres dialect"
|
|
5628
|
-
);
|
|
6071
|
+
throw new Error("Batch queries are only supported for postgres dialect");
|
|
5629
6072
|
}
|
|
5630
|
-
const
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
throw new Error(
|
|
5638
|
-
`Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5639
|
-
);
|
|
5640
|
-
}
|
|
5641
|
-
const { sql: querySql, params: queryParams } = buildSQLWithCache(
|
|
5642
|
-
model,
|
|
6073
|
+
const { aliases, aliasesByKey } = buildAliasesForKeys(keys);
|
|
6074
|
+
if (isAllCountQueries(queries, keys)) {
|
|
6075
|
+
const merged = buildMergedCountBatchSql(
|
|
6076
|
+
queries,
|
|
6077
|
+
keys,
|
|
6078
|
+
aliasesByKey,
|
|
6079
|
+
modelMap,
|
|
5643
6080
|
models,
|
|
5644
|
-
"count",
|
|
5645
|
-
query.args || {},
|
|
5646
6081
|
dialect
|
|
5647
6082
|
);
|
|
5648
|
-
|
|
5649
|
-
querySql,
|
|
5650
|
-
queryParams,
|
|
5651
|
-
allParams.length
|
|
5652
|
-
);
|
|
5653
|
-
for (let p = 0; p < reindexedParams.length; p++) {
|
|
5654
|
-
allParams.push(reindexedParams[p]);
|
|
5655
|
-
}
|
|
5656
|
-
const cteName = `count_${i}`;
|
|
5657
|
-
const resultKey = `count_${i}`;
|
|
5658
|
-
ctes[i] = `${cteName} AS (${reindexedSql})`;
|
|
5659
|
-
selects[i] = `(SELECT * FROM ${cteName}) AS ${quoteBatchIdent(resultKey)}`;
|
|
5660
|
-
}
|
|
5661
|
-
const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
|
|
5662
|
-
return { sql, params: allParams };
|
|
5663
|
-
}
|
|
5664
|
-
function looksLikeJsonString(s) {
|
|
5665
|
-
const t = s.trim();
|
|
5666
|
-
if (t.length === 0) return false;
|
|
5667
|
-
const c0 = t.charCodeAt(0);
|
|
5668
|
-
const cN = t.charCodeAt(t.length - 1);
|
|
5669
|
-
if (c0 === 123 && cN === 125) return true;
|
|
5670
|
-
if (c0 === 91 && cN === 93) return true;
|
|
5671
|
-
if (t === "null" || t === "true" || t === "false") return true;
|
|
5672
|
-
return false;
|
|
5673
|
-
}
|
|
5674
|
-
function parseJsonValue(value) {
|
|
5675
|
-
if (typeof value !== "string") return value;
|
|
5676
|
-
if (!looksLikeJsonString(value)) return value;
|
|
5677
|
-
try {
|
|
5678
|
-
return JSON.parse(value);
|
|
5679
|
-
} catch (e) {
|
|
5680
|
-
return value;
|
|
5681
|
-
}
|
|
5682
|
-
}
|
|
5683
|
-
function parseCountValue(value) {
|
|
5684
|
-
if (value === null || value === void 0) return 0;
|
|
5685
|
-
if (typeof value === "number") return value;
|
|
5686
|
-
if (typeof value === "bigint") {
|
|
5687
|
-
const n = Number(value);
|
|
5688
|
-
return Number.isSafeInteger(n) ? n : 0;
|
|
5689
|
-
}
|
|
5690
|
-
if (typeof value === "string") {
|
|
5691
|
-
const n = Number.parseInt(value, 10);
|
|
5692
|
-
return Number.isFinite(n) ? n : 0;
|
|
5693
|
-
}
|
|
5694
|
-
if (typeof value === "object") {
|
|
5695
|
-
const obj = value;
|
|
5696
|
-
const countKey = Object.prototype.hasOwnProperty.call(obj, "count") ? "count" : Object.prototype.hasOwnProperty.call(obj, "_count") ? "_count" : Object.keys(obj).find((k) => k.endsWith("_count"));
|
|
5697
|
-
if (countKey !== void 0) {
|
|
5698
|
-
const v = obj[countKey];
|
|
5699
|
-
if (typeof v === "number") return v;
|
|
5700
|
-
if (typeof v === "bigint") {
|
|
5701
|
-
const n = Number(v);
|
|
5702
|
-
return Number.isSafeInteger(n) ? n : 0;
|
|
5703
|
-
}
|
|
5704
|
-
if (typeof v === "string") {
|
|
5705
|
-
const n = Number.parseInt(v, 10);
|
|
5706
|
-
return Number.isFinite(n) ? n : 0;
|
|
5707
|
-
}
|
|
5708
|
-
}
|
|
5709
|
-
}
|
|
5710
|
-
return 0;
|
|
5711
|
-
}
|
|
5712
|
-
function parseBatchCountResults(row, count) {
|
|
5713
|
-
const results = [];
|
|
5714
|
-
for (let i = 0; i < count; i++) {
|
|
5715
|
-
const key = `count_${i}`;
|
|
5716
|
-
const value = row[key];
|
|
5717
|
-
results.push(parseCountValue(value));
|
|
5718
|
-
}
|
|
5719
|
-
return results;
|
|
5720
|
-
}
|
|
5721
|
-
function parseBatchResults(row, keys, queries, aliases) {
|
|
5722
|
-
const results = {};
|
|
5723
|
-
for (let i = 0; i < keys.length; i++) {
|
|
5724
|
-
const key = keys[i];
|
|
5725
|
-
const columnKey = aliases && aliases[i] ? aliases[i] : key;
|
|
5726
|
-
const rawValue = row[columnKey];
|
|
5727
|
-
const query = queries[key];
|
|
5728
|
-
switch (query.method) {
|
|
5729
|
-
case "findMany": {
|
|
5730
|
-
const parsed = parseJsonValue(rawValue);
|
|
5731
|
-
results[key] = Array.isArray(parsed) ? parsed : [];
|
|
5732
|
-
break;
|
|
5733
|
-
}
|
|
5734
|
-
case "findFirst":
|
|
5735
|
-
case "findUnique": {
|
|
5736
|
-
const parsed = parseJsonValue(rawValue);
|
|
5737
|
-
results[key] = parsed != null ? parsed : null;
|
|
5738
|
-
break;
|
|
5739
|
-
}
|
|
5740
|
-
case "count": {
|
|
5741
|
-
results[key] = parseCountValue(rawValue);
|
|
5742
|
-
break;
|
|
5743
|
-
}
|
|
5744
|
-
case "aggregate": {
|
|
5745
|
-
const parsed = parseJsonValue(rawValue);
|
|
5746
|
-
const obj = parsed != null ? parsed : {};
|
|
5747
|
-
results[key] = transformQueryResults("aggregate", [obj]);
|
|
5748
|
-
break;
|
|
5749
|
-
}
|
|
5750
|
-
case "groupBy": {
|
|
5751
|
-
const parsed = parseJsonValue(rawValue);
|
|
5752
|
-
const arr = Array.isArray(parsed) ? parsed : [];
|
|
5753
|
-
results[key] = transformQueryResults("groupBy", arr);
|
|
5754
|
-
break;
|
|
5755
|
-
}
|
|
5756
|
-
default:
|
|
5757
|
-
results[key] = rawValue;
|
|
5758
|
-
}
|
|
5759
|
-
}
|
|
5760
|
-
return results;
|
|
5761
|
-
}
|
|
5762
|
-
|
|
5763
|
-
// src/transaction.ts
|
|
5764
|
-
function isolationLevelToPostgresKeyword(level) {
|
|
5765
|
-
switch (level) {
|
|
5766
|
-
case "ReadCommitted":
|
|
5767
|
-
return "read committed";
|
|
5768
|
-
case "RepeatableRead":
|
|
5769
|
-
return "repeatable read";
|
|
5770
|
-
case "Serializable":
|
|
5771
|
-
return "serializable";
|
|
5772
|
-
default:
|
|
5773
|
-
return void 0;
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5776
|
-
function validateTimeout(timeout) {
|
|
5777
|
-
if (typeof timeout !== "number") {
|
|
5778
|
-
throw new Error(
|
|
5779
|
-
`Transaction timeout must be a number, got ${typeof timeout}`
|
|
5780
|
-
);
|
|
5781
|
-
}
|
|
5782
|
-
if (!Number.isFinite(timeout)) {
|
|
5783
|
-
throw new Error(`Transaction timeout must be finite, got ${timeout}`);
|
|
5784
|
-
}
|
|
5785
|
-
if (timeout < 0) {
|
|
5786
|
-
throw new Error(`Transaction timeout must be non-negative, got ${timeout}`);
|
|
5787
|
-
}
|
|
5788
|
-
return Math.floor(timeout);
|
|
5789
|
-
}
|
|
5790
|
-
function createTransactionExecutor(deps) {
|
|
5791
|
-
const { modelMap, allModels, dialect, executeRaw, postgresClient } = deps;
|
|
5792
|
-
return {
|
|
5793
|
-
execute(queries, options) {
|
|
5794
|
-
return __async(this, null, function* () {
|
|
5795
|
-
if (queries.length === 0) return [];
|
|
5796
|
-
if (dialect !== "postgres") {
|
|
5797
|
-
throw new Error("$transaction is only supported for postgres dialect");
|
|
5798
|
-
}
|
|
5799
|
-
if (!postgresClient) {
|
|
5800
|
-
throw new Error("postgresClient is required for transactions");
|
|
5801
|
-
}
|
|
5802
|
-
const transactionCallback = (sql) => __async(null, null, function* () {
|
|
5803
|
-
const results = [];
|
|
5804
|
-
const isolationLevel = isolationLevelToPostgresKeyword(
|
|
5805
|
-
options == null ? void 0 : options.isolationLevel
|
|
5806
|
-
);
|
|
5807
|
-
if (isolationLevel) {
|
|
5808
|
-
yield sql.unsafe(
|
|
5809
|
-
`SET TRANSACTION ISOLATION LEVEL ${isolationLevel.toUpperCase()}`
|
|
5810
|
-
);
|
|
5811
|
-
}
|
|
5812
|
-
if ((options == null ? void 0 : options.timeout) !== void 0 && options.timeout !== null) {
|
|
5813
|
-
const validatedTimeout = validateTimeout(options.timeout);
|
|
5814
|
-
yield sql.unsafe(`SET LOCAL statement_timeout = $1`, [
|
|
5815
|
-
validatedTimeout
|
|
5816
|
-
]);
|
|
5817
|
-
}
|
|
5818
|
-
for (const q of queries) {
|
|
5819
|
-
const model = modelMap.get(q.model);
|
|
5820
|
-
if (!model) {
|
|
5821
|
-
throw new Error(
|
|
5822
|
-
`Model '${q.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
5823
|
-
);
|
|
5824
|
-
}
|
|
5825
|
-
const { sql: sqlStr, params } = buildSQLWithCache(
|
|
5826
|
-
model,
|
|
5827
|
-
allModels,
|
|
5828
|
-
q.method,
|
|
5829
|
-
q.args || {},
|
|
5830
|
-
dialect
|
|
5831
|
-
);
|
|
5832
|
-
const rawResults = yield sql.unsafe(sqlStr, params);
|
|
5833
|
-
results.push(transformQueryResults(q.method, rawResults));
|
|
5834
|
-
}
|
|
5835
|
-
return results;
|
|
5836
|
-
});
|
|
5837
|
-
return yield postgresClient.begin(transactionCallback);
|
|
5838
|
-
});
|
|
5839
|
-
}
|
|
5840
|
-
};
|
|
5841
|
-
}
|
|
5842
|
-
|
|
5843
|
-
// src/fast-path.ts
|
|
5844
|
-
function getIdField(model) {
|
|
5845
|
-
const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
|
|
5846
|
-
if (!idField) return null;
|
|
5847
|
-
return {
|
|
5848
|
-
name: "id",
|
|
5849
|
-
dbName: idField.dbName || "id"
|
|
5850
|
-
};
|
|
5851
|
-
}
|
|
5852
|
-
function buildColumnList(model) {
|
|
5853
|
-
const cols = [];
|
|
5854
|
-
for (const f of model.fields) {
|
|
5855
|
-
if (f.isRelation) continue;
|
|
5856
|
-
if (f.name.startsWith("@") || f.name.startsWith("//")) continue;
|
|
5857
|
-
const dbName = f.dbName || f.name;
|
|
5858
|
-
if (dbName !== f.name) {
|
|
5859
|
-
cols.push(`${quote(dbName)} AS ${quote(f.name)}`);
|
|
5860
|
-
} else {
|
|
5861
|
-
cols.push(quote(dbName));
|
|
5862
|
-
}
|
|
6083
|
+
if (merged) return merged;
|
|
5863
6084
|
}
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
6085
|
+
const result = buildRegularBatchQueries(
|
|
6086
|
+
queries,
|
|
6087
|
+
keys,
|
|
6088
|
+
aliases,
|
|
6089
|
+
modelMap,
|
|
6090
|
+
models,
|
|
5870
6091
|
dialect
|
|
5871
6092
|
);
|
|
5872
|
-
|
|
5873
|
-
const sql = `SELECT ${columns} FROM ${tableName} ${where}${suffix}`;
|
|
5874
|
-
return { sql, params };
|
|
5875
|
-
}
|
|
5876
|
-
function norm(value) {
|
|
5877
|
-
return normalizeValue(value);
|
|
5878
|
-
}
|
|
5879
|
-
function tryFastPath(model, method, args, dialect) {
|
|
5880
|
-
const where = args.where;
|
|
5881
|
-
if (method === "findUnique" && isPlainObject(where) && Object.keys(where).length === 1 && "id" in where && !args.select && !args.include) {
|
|
5882
|
-
const idField = getIdField(model);
|
|
5883
|
-
if (!idField) return null;
|
|
5884
|
-
const whereClause = dialect === "sqlite" ? `WHERE ${quote(idField.dbName)} = ?` : `WHERE ${quote(idField.dbName)} = $1`;
|
|
5885
|
-
return buildSimpleQuery(
|
|
5886
|
-
model,
|
|
5887
|
-
dialect,
|
|
5888
|
-
whereClause,
|
|
5889
|
-
[norm(where.id)],
|
|
5890
|
-
" LIMIT 1"
|
|
5891
|
-
);
|
|
5892
|
-
}
|
|
5893
|
-
if (method === "findMany" && isPlainObject(where) && Object.keys(where).length === 1 && "id" in where && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
|
|
5894
|
-
const idField = getIdField(model);
|
|
5895
|
-
if (!idField) return null;
|
|
5896
|
-
const whereClause = dialect === "sqlite" ? `WHERE ${quote(idField.dbName)} = ?` : `WHERE ${quote(idField.dbName)} = $1`;
|
|
5897
|
-
return buildSimpleQuery(model, dialect, whereClause, [norm(where.id)]);
|
|
5898
|
-
}
|
|
5899
|
-
if (method === "count" && (!where || Object.keys(where).length === 0) && !args.select && !args.skip) {
|
|
5900
|
-
const tableName = buildTableReference(
|
|
5901
|
-
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5902
|
-
model.tableName,
|
|
5903
|
-
dialect
|
|
5904
|
-
);
|
|
5905
|
-
const sql = `SELECT COUNT(*) AS ${quote("_count._all")} FROM ${tableName}`;
|
|
5906
|
-
return { sql, params: [] };
|
|
5907
|
-
}
|
|
5908
|
-
if (method === "findMany" && (!where || Object.keys(where).length === 0) && !args.select && !args.include && !args.orderBy && !args.skip && !args.distinct && !args.cursor && typeof args.take === "number") {
|
|
5909
|
-
const tableName = buildTableReference(
|
|
5910
|
-
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5911
|
-
model.tableName,
|
|
5912
|
-
dialect
|
|
5913
|
-
);
|
|
5914
|
-
const columns = buildColumnList(model);
|
|
5915
|
-
const sql = dialect === "sqlite" ? `SELECT ${columns} FROM ${tableName} LIMIT ?` : `SELECT ${columns} FROM ${tableName} LIMIT $1`;
|
|
5916
|
-
return { sql, params: [norm(args.take)] };
|
|
5917
|
-
}
|
|
5918
|
-
if (method === "findFirst" && isPlainObject(where) && Object.keys(where).length === 1 && !args.select && !args.include && !args.orderBy && !args.skip) {
|
|
5919
|
-
const field = Object.keys(where)[0];
|
|
5920
|
-
const value = where[field];
|
|
5921
|
-
if (value !== null && typeof value !== "object") {
|
|
5922
|
-
const fieldDef = model.fields.find((f) => f.name === field);
|
|
5923
|
-
if (fieldDef && !fieldDef.isRelation) {
|
|
5924
|
-
const columnName = fieldDef.dbName || field;
|
|
5925
|
-
const whereClause = dialect === "sqlite" ? `WHERE ${quote(columnName)} = ?` : `WHERE ${quote(columnName)} = $1`;
|
|
5926
|
-
return buildSimpleQuery(
|
|
5927
|
-
model,
|
|
5928
|
-
dialect,
|
|
5929
|
-
whereClause,
|
|
5930
|
-
[norm(value)],
|
|
5931
|
-
" LIMIT 1"
|
|
5932
|
-
);
|
|
5933
|
-
}
|
|
5934
|
-
}
|
|
5935
|
-
}
|
|
5936
|
-
if (method === "findMany" && isPlainObject(where) && Object.keys(where).length === 1 && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
|
|
5937
|
-
const field = Object.keys(where)[0];
|
|
5938
|
-
const value = where[field];
|
|
5939
|
-
if (value !== null && typeof value !== "object") {
|
|
5940
|
-
const fieldDef = model.fields.find((f) => f.name === field);
|
|
5941
|
-
if (fieldDef && !fieldDef.isRelation) {
|
|
5942
|
-
const columnName = fieldDef.dbName || field;
|
|
5943
|
-
const whereClause = dialect === "sqlite" ? `WHERE ${quote(columnName)} = ?` : `WHERE ${quote(columnName)} = $1`;
|
|
5944
|
-
return buildSimpleQuery(model, dialect, whereClause, [norm(value)]);
|
|
5945
|
-
}
|
|
5946
|
-
}
|
|
5947
|
-
}
|
|
5948
|
-
if (method === "findMany" && (!where || Object.keys(where).length === 0) && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
|
|
5949
|
-
const tableName = buildTableReference(
|
|
5950
|
-
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
5951
|
-
model.tableName,
|
|
5952
|
-
dialect
|
|
5953
|
-
);
|
|
5954
|
-
const columns = buildColumnList(model);
|
|
5955
|
-
const sql = `SELECT ${columns} FROM ${tableName}`;
|
|
5956
|
-
return { sql, params: [] };
|
|
5957
|
-
}
|
|
5958
|
-
return null;
|
|
6093
|
+
return __spreadProps(__spreadValues({}, result), { keys, aliases });
|
|
5959
6094
|
}
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
"findFirst",
|
|
5963
|
-
"findUnique",
|
|
5964
|
-
"count",
|
|
5965
|
-
"aggregate",
|
|
5966
|
-
"groupBy"
|
|
5967
|
-
]);
|
|
6095
|
+
|
|
6096
|
+
// src/index.ts
|
|
5968
6097
|
function buildSQL(model, models, method, args, dialect) {
|
|
5969
6098
|
return buildSQLWithCache(model, models, method, args, dialect);
|
|
5970
6099
|
}
|
|
5971
|
-
function executePostgres(client, sql, params) {
|
|
5972
|
-
return __async(this, null, function* () {
|
|
5973
|
-
return yield client.unsafe(sql, params);
|
|
5974
|
-
});
|
|
5975
|
-
}
|
|
5976
|
-
function shouldSqliteUseGet(method) {
|
|
5977
|
-
return method === "count" || method === "findFirst" || method === "findUnique" || method === "aggregate";
|
|
5978
|
-
}
|
|
5979
|
-
function executeSqlite(db, method, sql, params) {
|
|
5980
|
-
const stmt = db.prepare(sql);
|
|
5981
|
-
if (shouldSqliteUseGet(method)) {
|
|
5982
|
-
const row = stmt.get(...params);
|
|
5983
|
-
if (row === void 0) return [];
|
|
5984
|
-
return [row];
|
|
5985
|
-
}
|
|
5986
|
-
return stmt.all(...params);
|
|
5987
|
-
}
|
|
5988
|
-
function executeWithTiming(input) {
|
|
5989
|
-
return __async(this, null, function* () {
|
|
5990
|
-
var _a;
|
|
5991
|
-
const startTime = Date.now();
|
|
5992
|
-
const { sql, params } = buildSQL(
|
|
5993
|
-
input.model,
|
|
5994
|
-
input.allModels,
|
|
5995
|
-
input.method,
|
|
5996
|
-
input.args,
|
|
5997
|
-
input.dialect
|
|
5998
|
-
);
|
|
5999
|
-
if (input.debug) {
|
|
6000
|
-
console.log(`[${input.dialect}] ${input.modelName}.${input.method}`);
|
|
6001
|
-
console.log("SQL:", sql);
|
|
6002
|
-
console.log("Params:", params);
|
|
6003
|
-
}
|
|
6004
|
-
const results = yield input.executeQuery(input.method, sql, params);
|
|
6005
|
-
const duration = Date.now() - startTime;
|
|
6006
|
-
(_a = input.onQuery) == null ? void 0 : _a.call(input, {
|
|
6007
|
-
model: input.modelName,
|
|
6008
|
-
method: input.method,
|
|
6009
|
-
sql,
|
|
6010
|
-
params,
|
|
6011
|
-
duration
|
|
6012
|
-
});
|
|
6013
|
-
return results;
|
|
6014
|
-
});
|
|
6015
|
-
}
|
|
6016
|
-
function resolveModelName(ctx) {
|
|
6017
|
-
return (ctx == null ? void 0 : ctx.name) || (ctx == null ? void 0 : ctx.$name);
|
|
6018
|
-
}
|
|
6019
|
-
function isAllowedModel(allowedModels, modelName) {
|
|
6020
|
-
if (!allowedModels) return true;
|
|
6021
|
-
return allowedModels.includes(modelName);
|
|
6022
|
-
}
|
|
6023
|
-
function getModelOrNull(modelMap, modelName) {
|
|
6024
|
-
var _a;
|
|
6025
|
-
return (_a = modelMap.get(modelName)) != null ? _a : null;
|
|
6026
|
-
}
|
|
6027
|
-
function fallbackToPrisma(ctx, modelName, method, args) {
|
|
6028
|
-
return ctx.$parent[modelName][method](args);
|
|
6029
|
-
}
|
|
6030
|
-
function createExecuteQuery(client, dialect) {
|
|
6031
|
-
return (method, sql, params) => __async(null, null, function* () {
|
|
6032
|
-
if (dialect === "postgres") {
|
|
6033
|
-
return yield executePostgres(client, sql, params);
|
|
6034
|
-
}
|
|
6035
|
-
return executeSqlite(client, method, sql, params);
|
|
6036
|
-
});
|
|
6037
|
-
}
|
|
6038
|
-
function logAcceleratedError(debug, dialect, modelName, method, error) {
|
|
6039
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
6040
|
-
console.warn(
|
|
6041
|
-
`[prisma-sql] ${modelName}.${method} acceleration failed, falling back to Prisma: ${msg}`
|
|
6042
|
-
);
|
|
6043
|
-
if (debug && error instanceof Error && error.stack) {
|
|
6044
|
-
console.warn(error.stack);
|
|
6045
|
-
}
|
|
6046
|
-
}
|
|
6047
|
-
function canAccelerate(deps, modelName, method) {
|
|
6048
|
-
if (!ACCELERATED_METHODS.has(method)) return false;
|
|
6049
|
-
if (!isAllowedModel(deps.allowedModels, modelName)) return false;
|
|
6050
|
-
return deps.modelMap.has(modelName);
|
|
6051
|
-
}
|
|
6052
|
-
function runAccelerated(deps, modelName, method, model, args) {
|
|
6053
|
-
return __async(this, null, function* () {
|
|
6054
|
-
return withDialect(deps.dialect, () => __async(null, null, function* () {
|
|
6055
|
-
const fastPath = tryFastPath(model, method, args || {}, deps.dialect);
|
|
6056
|
-
if (fastPath) {
|
|
6057
|
-
if (deps.debug) {
|
|
6058
|
-
console.log(`[${deps.dialect}] ${modelName}.${method} \u26A1 FAST PATH`);
|
|
6059
|
-
console.log("SQL:", fastPath.sql);
|
|
6060
|
-
console.log("Params:", fastPath.params);
|
|
6061
|
-
}
|
|
6062
|
-
const results2 = yield deps.executeQuery(
|
|
6063
|
-
method,
|
|
6064
|
-
fastPath.sql,
|
|
6065
|
-
fastPath.params
|
|
6066
|
-
);
|
|
6067
|
-
return transformQueryResults(method, results2);
|
|
6068
|
-
}
|
|
6069
|
-
const results = yield executeWithTiming({
|
|
6070
|
-
modelName,
|
|
6071
|
-
method,
|
|
6072
|
-
model,
|
|
6073
|
-
allModels: deps.allModels,
|
|
6074
|
-
args: args || {},
|
|
6075
|
-
dialect: deps.dialect,
|
|
6076
|
-
debug: deps.debug,
|
|
6077
|
-
executeQuery: deps.executeQuery,
|
|
6078
|
-
onQuery: deps.onQuery
|
|
6079
|
-
});
|
|
6080
|
-
return transformQueryResults(method, results);
|
|
6081
|
-
}));
|
|
6082
|
-
});
|
|
6083
|
-
}
|
|
6084
|
-
function handleMethodCall(ctx, method, args, deps) {
|
|
6085
|
-
return __async(this, null, function* () {
|
|
6086
|
-
const modelName = resolveModelName(ctx);
|
|
6087
|
-
if (!canAccelerate(deps, modelName, method)) {
|
|
6088
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
6089
|
-
}
|
|
6090
|
-
const model = getModelOrNull(deps.modelMap, modelName);
|
|
6091
|
-
if (!model) {
|
|
6092
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
6093
|
-
}
|
|
6094
|
-
try {
|
|
6095
|
-
return yield runAccelerated(deps, modelName, method, model, args);
|
|
6096
|
-
} catch (error) {
|
|
6097
|
-
logAcceleratedError(deps.debug, deps.dialect, modelName, method, error);
|
|
6098
|
-
return fallbackToPrisma(ctx, modelName, method, args);
|
|
6099
|
-
}
|
|
6100
|
-
});
|
|
6101
|
-
}
|
|
6102
|
-
var DeferredQuery = class {
|
|
6103
|
-
constructor(model, method, args) {
|
|
6104
|
-
this.model = model;
|
|
6105
|
-
this.method = method;
|
|
6106
|
-
this.args = args;
|
|
6107
|
-
}
|
|
6108
|
-
then(onfulfilled, onrejected) {
|
|
6109
|
-
throw new Error(
|
|
6110
|
-
"Cannot await a batch query. Batch queries must not be awaited inside the $batch callback."
|
|
6111
|
-
);
|
|
6112
|
-
}
|
|
6113
|
-
};
|
|
6114
|
-
function createBatchProxy(modelMap, allowedModels) {
|
|
6115
|
-
return new Proxy(
|
|
6116
|
-
{},
|
|
6117
|
-
{
|
|
6118
|
-
get(_target, modelName) {
|
|
6119
|
-
if (typeof modelName === "symbol") return void 0;
|
|
6120
|
-
const model = modelMap.get(modelName);
|
|
6121
|
-
if (!model) {
|
|
6122
|
-
throw new Error(
|
|
6123
|
-
`Model '${modelName}' not found. Available: ${[...modelMap.keys()].join(", ")}`
|
|
6124
|
-
);
|
|
6125
|
-
}
|
|
6126
|
-
if (allowedModels && !allowedModels.includes(modelName)) {
|
|
6127
|
-
throw new Error(
|
|
6128
|
-
`Model '${modelName}' not allowed. Allowed: ${allowedModels.join(", ")}`
|
|
6129
|
-
);
|
|
6130
|
-
}
|
|
6131
|
-
return new Proxy(
|
|
6132
|
-
{},
|
|
6133
|
-
{
|
|
6134
|
-
get(_target2, method) {
|
|
6135
|
-
if (!ACCELERATED_METHODS.has(method)) {
|
|
6136
|
-
throw new Error(
|
|
6137
|
-
`Method '${method}' not supported in batch. Supported: ${[...ACCELERATED_METHODS].join(", ")}`
|
|
6138
|
-
);
|
|
6139
|
-
}
|
|
6140
|
-
return (args) => {
|
|
6141
|
-
return new DeferredQuery(
|
|
6142
|
-
modelName,
|
|
6143
|
-
method,
|
|
6144
|
-
args
|
|
6145
|
-
);
|
|
6146
|
-
};
|
|
6147
|
-
}
|
|
6148
|
-
}
|
|
6149
|
-
);
|
|
6150
|
-
}
|
|
6151
|
-
}
|
|
6152
|
-
);
|
|
6153
|
-
}
|
|
6154
|
-
function speedExtension(config) {
|
|
6155
|
-
const {
|
|
6156
|
-
postgres,
|
|
6157
|
-
sqlite,
|
|
6158
|
-
models: providedModels,
|
|
6159
|
-
dmmf,
|
|
6160
|
-
debug = false,
|
|
6161
|
-
allowedModels,
|
|
6162
|
-
onQuery
|
|
6163
|
-
} = config;
|
|
6164
|
-
if (!postgres && !sqlite) {
|
|
6165
|
-
throw new Error("speedExtension requires either postgres or sqlite client");
|
|
6166
|
-
}
|
|
6167
|
-
if (postgres && sqlite) {
|
|
6168
|
-
throw new Error(
|
|
6169
|
-
"speedExtension cannot use both postgres and sqlite clients"
|
|
6170
|
-
);
|
|
6171
|
-
}
|
|
6172
|
-
let models;
|
|
6173
|
-
if (providedModels) {
|
|
6174
|
-
models = providedModels;
|
|
6175
|
-
} else if (dmmf) {
|
|
6176
|
-
models = convertDMMFToModels(dmmf.datamodel);
|
|
6177
|
-
} else {
|
|
6178
|
-
throw new Error("speedExtension requires either models or dmmf parameter.");
|
|
6179
|
-
}
|
|
6180
|
-
if (!Array.isArray(models) || models.length === 0) {
|
|
6181
|
-
throw new Error("speedExtension: models array is empty or invalid");
|
|
6182
|
-
}
|
|
6183
|
-
const dialect = postgres ? "postgres" : "sqlite";
|
|
6184
|
-
const client = postgres || sqlite;
|
|
6185
|
-
setGlobalDialect(dialect);
|
|
6186
|
-
return (prisma) => {
|
|
6187
|
-
const modelMap = new Map(models.map((m) => [m.name, m]));
|
|
6188
|
-
const executeQuery = createExecuteQuery(client, dialect);
|
|
6189
|
-
const deps = {
|
|
6190
|
-
dialect,
|
|
6191
|
-
debug,
|
|
6192
|
-
onQuery,
|
|
6193
|
-
allowedModels,
|
|
6194
|
-
allModels: models,
|
|
6195
|
-
modelMap,
|
|
6196
|
-
executeQuery
|
|
6197
|
-
};
|
|
6198
|
-
const createMethodHandler = (method) => {
|
|
6199
|
-
return function(args) {
|
|
6200
|
-
return __async(this, null, function* () {
|
|
6201
|
-
return handleMethodCall(this, method, args, deps);
|
|
6202
|
-
});
|
|
6203
|
-
};
|
|
6204
|
-
};
|
|
6205
|
-
const methodHandlers = {};
|
|
6206
|
-
for (const method of ACCELERATED_METHODS) {
|
|
6207
|
-
methodHandlers[method] = createMethodHandler(method);
|
|
6208
|
-
}
|
|
6209
|
-
const executeRaw = (sql, params) => __async(null, null, function* () {
|
|
6210
|
-
if (dialect === "postgres") {
|
|
6211
|
-
return yield client.unsafe(sql, params);
|
|
6212
|
-
}
|
|
6213
|
-
throw new Error("Raw execution for sqlite not supported in transactions");
|
|
6214
|
-
});
|
|
6215
|
-
const txExecutor = createTransactionExecutor({
|
|
6216
|
-
modelMap,
|
|
6217
|
-
allModels: models,
|
|
6218
|
-
dialect,
|
|
6219
|
-
executeRaw,
|
|
6220
|
-
postgresClient: postgres
|
|
6221
|
-
});
|
|
6222
|
-
function batch(callback) {
|
|
6223
|
-
return __async(this, null, function* () {
|
|
6224
|
-
const batchProxy = createBatchProxy(modelMap, allowedModels);
|
|
6225
|
-
const queries = yield callback(batchProxy);
|
|
6226
|
-
const batchQueries = {};
|
|
6227
|
-
for (const [key, deferred] of Object.entries(queries)) {
|
|
6228
|
-
if (!(deferred instanceof DeferredQuery)) {
|
|
6229
|
-
throw new Error(
|
|
6230
|
-
`Batch query '${key}' must be a deferred query. Did you await it?`
|
|
6231
|
-
);
|
|
6232
|
-
}
|
|
6233
|
-
batchQueries[key] = {
|
|
6234
|
-
model: deferred.model,
|
|
6235
|
-
method: deferred.method,
|
|
6236
|
-
args: deferred.args || {}
|
|
6237
|
-
};
|
|
6238
|
-
}
|
|
6239
|
-
const startTime = Date.now();
|
|
6240
|
-
const { sql, params, keys, aliases } = buildBatchSql(
|
|
6241
|
-
batchQueries,
|
|
6242
|
-
modelMap,
|
|
6243
|
-
models,
|
|
6244
|
-
dialect
|
|
6245
|
-
);
|
|
6246
|
-
if (debug) {
|
|
6247
|
-
console.log(`[${dialect}] $batch (${keys.length} queries)`);
|
|
6248
|
-
console.log("SQL:", sql);
|
|
6249
|
-
console.log("Params:", params);
|
|
6250
|
-
}
|
|
6251
|
-
const rows = yield executeQuery("findMany", sql, params);
|
|
6252
|
-
const row = rows[0];
|
|
6253
|
-
const results = parseBatchResults(row, keys, batchQueries, aliases);
|
|
6254
|
-
const duration = Date.now() - startTime;
|
|
6255
|
-
onQuery == null ? void 0 : onQuery({
|
|
6256
|
-
model: "_batch",
|
|
6257
|
-
method: "batch",
|
|
6258
|
-
sql,
|
|
6259
|
-
params,
|
|
6260
|
-
duration
|
|
6261
|
-
});
|
|
6262
|
-
return results;
|
|
6263
|
-
});
|
|
6264
|
-
}
|
|
6265
|
-
function transaction(queries, options) {
|
|
6266
|
-
return __async(this, null, function* () {
|
|
6267
|
-
const startTime = Date.now();
|
|
6268
|
-
if (debug) {
|
|
6269
|
-
console.log(`[${dialect}] $transaction (${queries.length} queries)`);
|
|
6270
|
-
}
|
|
6271
|
-
const results = yield txExecutor.execute(queries, options);
|
|
6272
|
-
const duration = Date.now() - startTime;
|
|
6273
|
-
onQuery == null ? void 0 : onQuery({
|
|
6274
|
-
model: "_transaction",
|
|
6275
|
-
method: "transaction",
|
|
6276
|
-
sql: `TRANSACTION(${queries.length})`,
|
|
6277
|
-
params: [],
|
|
6278
|
-
duration
|
|
6279
|
-
});
|
|
6280
|
-
return results;
|
|
6281
|
-
});
|
|
6282
|
-
}
|
|
6283
|
-
return prisma.$extends({
|
|
6284
|
-
name: "prisma-sql-speed",
|
|
6285
|
-
client: {
|
|
6286
|
-
$original: prisma,
|
|
6287
|
-
$batch: batch,
|
|
6288
|
-
$transaction: transaction
|
|
6289
|
-
},
|
|
6290
|
-
model: {
|
|
6291
|
-
$allModels: methodHandlers
|
|
6292
|
-
}
|
|
6293
|
-
});
|
|
6294
|
-
};
|
|
6295
|
-
}
|
|
6296
|
-
function extendPrisma(prisma, config) {
|
|
6297
|
-
const extension = speedExtension(config);
|
|
6298
|
-
return extension(prisma);
|
|
6299
|
-
}
|
|
6300
6100
|
function createToSQLFunction(models, dialect) {
|
|
6301
6101
|
if (!models || !Array.isArray(models) || models.length === 0) {
|
|
6302
6102
|
throw new Error("createToSQL requires non-empty models array");
|
|
@@ -6384,6 +6184,6 @@ function generateSQLByModel(directives) {
|
|
|
6384
6184
|
return byModel;
|
|
6385
6185
|
}
|
|
6386
6186
|
|
|
6387
|
-
export {
|
|
6187
|
+
export { buildSQL, createPrismaSQL, createToSQL, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel };
|
|
6388
6188
|
//# sourceMappingURL=index.js.map
|
|
6389
6189
|
//# sourceMappingURL=index.js.map
|