prisma-sql 1.37.0 → 1.39.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 +1670 -1566
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1670 -1566
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1882 -1607
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1882 -1607
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/readme.md +1 -1
package/dist/generator.cjs
CHANGED
|
@@ -56,7 +56,7 @@ var require_package = __commonJS({
|
|
|
56
56
|
"package.json"(exports$1, module) {
|
|
57
57
|
module.exports = {
|
|
58
58
|
name: "prisma-sql",
|
|
59
|
-
version: "1.
|
|
59
|
+
version: "1.39.0",
|
|
60
60
|
description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
|
|
61
61
|
main: "dist/index.cjs",
|
|
62
62
|
module: "dist/index.js",
|
|
@@ -129,7 +129,6 @@ var require_package = __commonJS({
|
|
|
129
129
|
"@faker-js/faker": "^10.2.0",
|
|
130
130
|
"@prisma/adapter-better-sqlite3": "^7.2.0",
|
|
131
131
|
"@prisma/adapter-pg": "^7.2.0",
|
|
132
|
-
"@prisma/client": "7.2.0",
|
|
133
132
|
"@types/better-sqlite3": "^7.6.13",
|
|
134
133
|
"@types/node": "^25.0.10",
|
|
135
134
|
"@vitest/coverage-v8": "4.0.18",
|
|
@@ -137,11 +136,12 @@ var require_package = __commonJS({
|
|
|
137
136
|
"drizzle-kit": "^0.31.8",
|
|
138
137
|
"drizzle-orm": "^0.45.1",
|
|
139
138
|
postgres: "^3.4.8",
|
|
140
|
-
prisma: "7.2.0",
|
|
141
139
|
tsup: "^8.5.1",
|
|
142
140
|
tsx: "^4.21.0",
|
|
143
141
|
typescript: "^5.9.3",
|
|
144
|
-
vitest: "^4.0.18"
|
|
142
|
+
vitest: "^4.0.18",
|
|
143
|
+
"@prisma/client": "7.2.0",
|
|
144
|
+
prisma: "7.2.0"
|
|
145
145
|
},
|
|
146
146
|
engines: {
|
|
147
147
|
node: ">=16.0.0"
|
|
@@ -150,6 +150,148 @@ var require_package = __commonJS({
|
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
+
// src/builder/shared/constants.ts
|
|
154
|
+
var SQL_SEPARATORS = Object.freeze({
|
|
155
|
+
FIELD_LIST: ", ",
|
|
156
|
+
CONDITION_AND: " AND ",
|
|
157
|
+
CONDITION_OR: " OR ",
|
|
158
|
+
ORDER_BY: ", "
|
|
159
|
+
});
|
|
160
|
+
var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
161
|
+
"select",
|
|
162
|
+
"from",
|
|
163
|
+
"where",
|
|
164
|
+
"and",
|
|
165
|
+
"or",
|
|
166
|
+
"not",
|
|
167
|
+
"in",
|
|
168
|
+
"like",
|
|
169
|
+
"between",
|
|
170
|
+
"order",
|
|
171
|
+
"by",
|
|
172
|
+
"group",
|
|
173
|
+
"having",
|
|
174
|
+
"limit",
|
|
175
|
+
"offset",
|
|
176
|
+
"join",
|
|
177
|
+
"inner",
|
|
178
|
+
"left",
|
|
179
|
+
"right",
|
|
180
|
+
"outer",
|
|
181
|
+
"on",
|
|
182
|
+
"as",
|
|
183
|
+
"table",
|
|
184
|
+
"column",
|
|
185
|
+
"index",
|
|
186
|
+
"user",
|
|
187
|
+
"users",
|
|
188
|
+
"values",
|
|
189
|
+
"update",
|
|
190
|
+
"insert",
|
|
191
|
+
"delete",
|
|
192
|
+
"create",
|
|
193
|
+
"drop",
|
|
194
|
+
"alter",
|
|
195
|
+
"truncate",
|
|
196
|
+
"grant",
|
|
197
|
+
"revoke",
|
|
198
|
+
"exec",
|
|
199
|
+
"execute",
|
|
200
|
+
"union",
|
|
201
|
+
"intersect",
|
|
202
|
+
"except",
|
|
203
|
+
"case",
|
|
204
|
+
"when",
|
|
205
|
+
"then",
|
|
206
|
+
"else",
|
|
207
|
+
"end",
|
|
208
|
+
"null",
|
|
209
|
+
"true",
|
|
210
|
+
"false",
|
|
211
|
+
"is",
|
|
212
|
+
"exists",
|
|
213
|
+
"all",
|
|
214
|
+
"any",
|
|
215
|
+
"some"
|
|
216
|
+
]);
|
|
217
|
+
var SQL_KEYWORDS = SQL_RESERVED_WORDS;
|
|
218
|
+
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
219
|
+
var SPECIAL_FIELDS = Object.freeze({
|
|
220
|
+
ID: "id"
|
|
221
|
+
});
|
|
222
|
+
var SQL_TEMPLATES = Object.freeze({
|
|
223
|
+
PUBLIC_SCHEMA: "public",
|
|
224
|
+
WHERE: "WHERE",
|
|
225
|
+
SELECT: "SELECT",
|
|
226
|
+
FROM: "FROM",
|
|
227
|
+
ORDER_BY: "ORDER BY",
|
|
228
|
+
GROUP_BY: "GROUP BY",
|
|
229
|
+
HAVING: "HAVING",
|
|
230
|
+
LIMIT: "LIMIT",
|
|
231
|
+
OFFSET: "OFFSET",
|
|
232
|
+
COUNT_ALL: "COUNT(*)",
|
|
233
|
+
AS: "AS",
|
|
234
|
+
DISTINCT_ON: "DISTINCT ON",
|
|
235
|
+
IS_NULL: "IS NULL",
|
|
236
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
237
|
+
LIKE: "LIKE",
|
|
238
|
+
AND: "AND",
|
|
239
|
+
OR: "OR",
|
|
240
|
+
NOT: "NOT"
|
|
241
|
+
});
|
|
242
|
+
var SCHEMA_PREFIXES = Object.freeze({
|
|
243
|
+
INTERNAL: "@",
|
|
244
|
+
COMMENT: "//"
|
|
245
|
+
});
|
|
246
|
+
var Ops = Object.freeze({
|
|
247
|
+
EQUALS: "equals",
|
|
248
|
+
NOT: "not",
|
|
249
|
+
GT: "gt",
|
|
250
|
+
GTE: "gte",
|
|
251
|
+
LT: "lt",
|
|
252
|
+
LTE: "lte",
|
|
253
|
+
IN: "in",
|
|
254
|
+
NOT_IN: "notIn",
|
|
255
|
+
CONTAINS: "contains",
|
|
256
|
+
STARTS_WITH: "startsWith",
|
|
257
|
+
ENDS_WITH: "endsWith",
|
|
258
|
+
HAS: "has",
|
|
259
|
+
HAS_SOME: "hasSome",
|
|
260
|
+
HAS_EVERY: "hasEvery",
|
|
261
|
+
IS_EMPTY: "isEmpty",
|
|
262
|
+
PATH: "path",
|
|
263
|
+
STRING_CONTAINS: "string_contains",
|
|
264
|
+
STRING_STARTS_WITH: "string_starts_with",
|
|
265
|
+
STRING_ENDS_WITH: "string_ends_with"
|
|
266
|
+
});
|
|
267
|
+
var LogicalOps = Object.freeze({
|
|
268
|
+
AND: "AND",
|
|
269
|
+
OR: "OR",
|
|
270
|
+
NOT: "NOT"
|
|
271
|
+
});
|
|
272
|
+
var RelationFilters = Object.freeze({
|
|
273
|
+
SOME: "some",
|
|
274
|
+
EVERY: "every",
|
|
275
|
+
NONE: "none"
|
|
276
|
+
});
|
|
277
|
+
var Modes = Object.freeze({
|
|
278
|
+
INSENSITIVE: "insensitive",
|
|
279
|
+
DEFAULT: "default"
|
|
280
|
+
});
|
|
281
|
+
var Wildcards = Object.freeze({
|
|
282
|
+
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
283
|
+
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
284
|
+
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
285
|
+
});
|
|
286
|
+
var REGEX_CACHE = {
|
|
287
|
+
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
288
|
+
};
|
|
289
|
+
var LIMITS = Object.freeze({
|
|
290
|
+
MAX_QUERY_DEPTH: 50,
|
|
291
|
+
MAX_ARRAY_SIZE: 1e4,
|
|
292
|
+
MAX_STRING_LENGTH: 1e4
|
|
293
|
+
});
|
|
294
|
+
|
|
153
295
|
// src/sql-builder-dialect.ts
|
|
154
296
|
var globalDialect = "postgres";
|
|
155
297
|
function setGlobalDialect(dialect) {
|
|
@@ -304,7 +446,7 @@ function getArrayType(prismaType, dialect) {
|
|
|
304
446
|
case "DateTime":
|
|
305
447
|
return "timestamptz[]";
|
|
306
448
|
default:
|
|
307
|
-
return "
|
|
449
|
+
return `"${baseType}"[]`;
|
|
308
450
|
}
|
|
309
451
|
}
|
|
310
452
|
function jsonAgg(content, dialect) {
|
|
@@ -331,148 +473,6 @@ function prepareArrayParam(value, dialect) {
|
|
|
331
473
|
return JSON.stringify(value);
|
|
332
474
|
}
|
|
333
475
|
|
|
334
|
-
// src/builder/shared/constants.ts
|
|
335
|
-
var SQL_SEPARATORS = Object.freeze({
|
|
336
|
-
FIELD_LIST: ", ",
|
|
337
|
-
CONDITION_AND: " AND ",
|
|
338
|
-
CONDITION_OR: " OR ",
|
|
339
|
-
ORDER_BY: ", "
|
|
340
|
-
});
|
|
341
|
-
var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
342
|
-
"select",
|
|
343
|
-
"from",
|
|
344
|
-
"where",
|
|
345
|
-
"and",
|
|
346
|
-
"or",
|
|
347
|
-
"not",
|
|
348
|
-
"in",
|
|
349
|
-
"like",
|
|
350
|
-
"between",
|
|
351
|
-
"order",
|
|
352
|
-
"by",
|
|
353
|
-
"group",
|
|
354
|
-
"having",
|
|
355
|
-
"limit",
|
|
356
|
-
"offset",
|
|
357
|
-
"join",
|
|
358
|
-
"inner",
|
|
359
|
-
"left",
|
|
360
|
-
"right",
|
|
361
|
-
"outer",
|
|
362
|
-
"on",
|
|
363
|
-
"as",
|
|
364
|
-
"table",
|
|
365
|
-
"column",
|
|
366
|
-
"index",
|
|
367
|
-
"user",
|
|
368
|
-
"users",
|
|
369
|
-
"values",
|
|
370
|
-
"update",
|
|
371
|
-
"insert",
|
|
372
|
-
"delete",
|
|
373
|
-
"create",
|
|
374
|
-
"drop",
|
|
375
|
-
"alter",
|
|
376
|
-
"truncate",
|
|
377
|
-
"grant",
|
|
378
|
-
"revoke",
|
|
379
|
-
"exec",
|
|
380
|
-
"execute",
|
|
381
|
-
"union",
|
|
382
|
-
"intersect",
|
|
383
|
-
"except",
|
|
384
|
-
"case",
|
|
385
|
-
"when",
|
|
386
|
-
"then",
|
|
387
|
-
"else",
|
|
388
|
-
"end",
|
|
389
|
-
"null",
|
|
390
|
-
"true",
|
|
391
|
-
"false",
|
|
392
|
-
"is",
|
|
393
|
-
"exists",
|
|
394
|
-
"all",
|
|
395
|
-
"any",
|
|
396
|
-
"some"
|
|
397
|
-
]);
|
|
398
|
-
var SQL_KEYWORDS = SQL_RESERVED_WORDS;
|
|
399
|
-
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
400
|
-
var SPECIAL_FIELDS = Object.freeze({
|
|
401
|
-
ID: "id"
|
|
402
|
-
});
|
|
403
|
-
var SQL_TEMPLATES = Object.freeze({
|
|
404
|
-
PUBLIC_SCHEMA: "public",
|
|
405
|
-
WHERE: "WHERE",
|
|
406
|
-
SELECT: "SELECT",
|
|
407
|
-
FROM: "FROM",
|
|
408
|
-
ORDER_BY: "ORDER BY",
|
|
409
|
-
GROUP_BY: "GROUP BY",
|
|
410
|
-
HAVING: "HAVING",
|
|
411
|
-
LIMIT: "LIMIT",
|
|
412
|
-
OFFSET: "OFFSET",
|
|
413
|
-
COUNT_ALL: "COUNT(*)",
|
|
414
|
-
AS: "AS",
|
|
415
|
-
DISTINCT_ON: "DISTINCT ON",
|
|
416
|
-
IS_NULL: "IS NULL",
|
|
417
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
418
|
-
LIKE: "LIKE",
|
|
419
|
-
AND: "AND",
|
|
420
|
-
OR: "OR",
|
|
421
|
-
NOT: "NOT"
|
|
422
|
-
});
|
|
423
|
-
var SCHEMA_PREFIXES = Object.freeze({
|
|
424
|
-
INTERNAL: "@",
|
|
425
|
-
COMMENT: "//"
|
|
426
|
-
});
|
|
427
|
-
var Ops = Object.freeze({
|
|
428
|
-
EQUALS: "equals",
|
|
429
|
-
NOT: "not",
|
|
430
|
-
GT: "gt",
|
|
431
|
-
GTE: "gte",
|
|
432
|
-
LT: "lt",
|
|
433
|
-
LTE: "lte",
|
|
434
|
-
IN: "in",
|
|
435
|
-
NOT_IN: "notIn",
|
|
436
|
-
CONTAINS: "contains",
|
|
437
|
-
STARTS_WITH: "startsWith",
|
|
438
|
-
ENDS_WITH: "endsWith",
|
|
439
|
-
HAS: "has",
|
|
440
|
-
HAS_SOME: "hasSome",
|
|
441
|
-
HAS_EVERY: "hasEvery",
|
|
442
|
-
IS_EMPTY: "isEmpty",
|
|
443
|
-
PATH: "path",
|
|
444
|
-
STRING_CONTAINS: "string_contains",
|
|
445
|
-
STRING_STARTS_WITH: "string_starts_with",
|
|
446
|
-
STRING_ENDS_WITH: "string_ends_with"
|
|
447
|
-
});
|
|
448
|
-
var LogicalOps = Object.freeze({
|
|
449
|
-
AND: "AND",
|
|
450
|
-
OR: "OR",
|
|
451
|
-
NOT: "NOT"
|
|
452
|
-
});
|
|
453
|
-
var RelationFilters = Object.freeze({
|
|
454
|
-
SOME: "some",
|
|
455
|
-
EVERY: "every",
|
|
456
|
-
NONE: "none"
|
|
457
|
-
});
|
|
458
|
-
var Modes = Object.freeze({
|
|
459
|
-
INSENSITIVE: "insensitive",
|
|
460
|
-
DEFAULT: "default"
|
|
461
|
-
});
|
|
462
|
-
var Wildcards = Object.freeze({
|
|
463
|
-
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
464
|
-
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
465
|
-
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
466
|
-
});
|
|
467
|
-
var REGEX_CACHE = {
|
|
468
|
-
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
469
|
-
};
|
|
470
|
-
var LIMITS = Object.freeze({
|
|
471
|
-
MAX_QUERY_DEPTH: 50,
|
|
472
|
-
MAX_ARRAY_SIZE: 1e4,
|
|
473
|
-
MAX_STRING_LENGTH: 1e4
|
|
474
|
-
});
|
|
475
|
-
|
|
476
476
|
// src/builder/shared/validators/type-guards.ts
|
|
477
477
|
function isNotNullish(value) {
|
|
478
478
|
return value !== null && value !== void 0;
|
|
@@ -558,6 +558,19 @@ function getRelationFieldSet(model) {
|
|
|
558
558
|
RELATION_SET_CACHE.set(model, s);
|
|
559
559
|
return s;
|
|
560
560
|
}
|
|
561
|
+
var COLUMN_MAP_CACHE = /* @__PURE__ */ new WeakMap();
|
|
562
|
+
function getColumnMap(model) {
|
|
563
|
+
const cached = COLUMN_MAP_CACHE.get(model);
|
|
564
|
+
if (cached) return cached;
|
|
565
|
+
const map = /* @__PURE__ */ new Map();
|
|
566
|
+
for (const f of model.fields) {
|
|
567
|
+
if (!f.isRelation) {
|
|
568
|
+
map.set(f.name, f.dbName || f.name);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
COLUMN_MAP_CACHE.set(model, map);
|
|
572
|
+
return map;
|
|
573
|
+
}
|
|
561
574
|
|
|
562
575
|
// src/builder/shared/validators/sql-validators.ts
|
|
563
576
|
function isValidWhereClause(clause) {
|
|
@@ -945,27 +958,10 @@ function quote(id) {
|
|
|
945
958
|
}
|
|
946
959
|
return id;
|
|
947
960
|
}
|
|
948
|
-
function pickDbFieldName(field) {
|
|
949
|
-
const candidates = [
|
|
950
|
-
field == null ? void 0 : field.dbName,
|
|
951
|
-
field == null ? void 0 : field.columnName,
|
|
952
|
-
field == null ? void 0 : field.databaseName,
|
|
953
|
-
field == null ? void 0 : field.mappedName
|
|
954
|
-
];
|
|
955
|
-
for (const c of candidates) {
|
|
956
|
-
if (typeof c === "string") {
|
|
957
|
-
const s = c.trim();
|
|
958
|
-
if (s.length > 0) return s;
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
return void 0;
|
|
962
|
-
}
|
|
963
961
|
function resolveColumnName(model, fieldName) {
|
|
964
|
-
var _a;
|
|
965
962
|
if (!model) return fieldName;
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
return (_a = pickDbFieldName(f)) != null ? _a : fieldName;
|
|
963
|
+
const columnMap = getColumnMap(model);
|
|
964
|
+
return columnMap.get(fieldName) || fieldName;
|
|
969
965
|
}
|
|
970
966
|
function quoteColumn(model, fieldName) {
|
|
971
967
|
return quote(resolveColumnName(model, fieldName));
|
|
@@ -1123,1570 +1119,1570 @@ function joinCondition(field, parentModel, childModel, parentAlias, childAlias)
|
|
|
1123
1119
|
function getModelByName(schemas, name) {
|
|
1124
1120
|
return schemas.find((m) => m.name === name);
|
|
1125
1121
|
}
|
|
1126
|
-
function
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
);
|
|
1130
|
-
if (
|
|
1131
|
-
|
|
1132
|
-
for (const [subOp, subVal] of entries) {
|
|
1133
|
-
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1134
|
-
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1135
|
-
}
|
|
1136
|
-
if (clauses.length === 0) return "";
|
|
1137
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1138
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1139
|
-
}
|
|
1140
|
-
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1141
|
-
if (val === void 0) return "";
|
|
1142
|
-
if (val === null) {
|
|
1143
|
-
return handleNullValue(expr, op);
|
|
1144
|
-
}
|
|
1145
|
-
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1146
|
-
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1147
|
-
}
|
|
1148
|
-
if (op === Ops.NOT) {
|
|
1149
|
-
const placeholder = params.addAuto(val);
|
|
1150
|
-
return `${expr} <> ${placeholder}`;
|
|
1122
|
+
function normalizeIntLike(name, v, opts = {}) {
|
|
1123
|
+
var _a, _b;
|
|
1124
|
+
if (!isNotNullish(v)) return void 0;
|
|
1125
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1126
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1127
|
+
throw new Error(`${name} must be an integer`);
|
|
1151
1128
|
}
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1129
|
+
const min = (_a = opts.min) != null ? _a : 0;
|
|
1130
|
+
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
1131
|
+
if (!allowZero && v === 0) {
|
|
1132
|
+
throw new Error(`${name} must be > 0`);
|
|
1155
1133
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
Ops.STARTS_WITH,
|
|
1159
|
-
Ops.ENDS_WITH
|
|
1160
|
-
]);
|
|
1161
|
-
if (STRING_LIKE_OPS.has(op)) {
|
|
1162
|
-
if (!isNotNullish(dialect)) {
|
|
1163
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1164
|
-
operator: op
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1134
|
+
if (v < min) {
|
|
1135
|
+
throw new Error(`${name} must be >= ${min}`);
|
|
1168
1136
|
}
|
|
1169
|
-
if (
|
|
1170
|
-
|
|
1171
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1172
|
-
}
|
|
1173
|
-
return handleInOperator(expr, op, val, params, dialect);
|
|
1137
|
+
if (typeof opts.max === "number" && v > opts.max) {
|
|
1138
|
+
throw new Error(`${name} must be <= ${opts.max}`);
|
|
1174
1139
|
}
|
|
1175
|
-
return
|
|
1176
|
-
}
|
|
1177
|
-
function handleNullValue(expr, op) {
|
|
1178
|
-
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1179
|
-
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1180
|
-
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1181
|
-
}
|
|
1182
|
-
function normalizeMode(v) {
|
|
1183
|
-
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1184
|
-
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1185
|
-
return void 0;
|
|
1140
|
+
return v;
|
|
1186
1141
|
}
|
|
1187
|
-
function
|
|
1188
|
-
const
|
|
1189
|
-
const
|
|
1190
|
-
return
|
|
1191
|
-
|
|
1192
|
-
val,
|
|
1193
|
-
params,
|
|
1194
|
-
dialect,
|
|
1195
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1196
|
-
` ${SQL_TEMPLATES.AND} `
|
|
1197
|
-
);
|
|
1142
|
+
function scopeName(scope, dynamicName) {
|
|
1143
|
+
const s = String(scope).trim();
|
|
1144
|
+
const dn = String(dynamicName).trim();
|
|
1145
|
+
if (s.length === 0) return dn;
|
|
1146
|
+
return `${s}:${dn}`;
|
|
1198
1147
|
}
|
|
1199
|
-
function
|
|
1200
|
-
if (
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1204
|
-
case Ops.STARTS_WITH:
|
|
1205
|
-
return `(${placeholder} || '%')`;
|
|
1206
|
-
case Ops.ENDS_WITH:
|
|
1207
|
-
return `('%' || ${placeholder})`;
|
|
1208
|
-
default:
|
|
1209
|
-
return placeholder;
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
switch (op) {
|
|
1213
|
-
case Ops.CONTAINS:
|
|
1214
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1215
|
-
case Ops.STARTS_WITH:
|
|
1216
|
-
return `(${placeholder} || '%')`;
|
|
1217
|
-
case Ops.ENDS_WITH:
|
|
1218
|
-
return `('%' || ${placeholder})`;
|
|
1219
|
-
default:
|
|
1220
|
-
return placeholder;
|
|
1148
|
+
function addAutoScoped(params, value, scope) {
|
|
1149
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
1150
|
+
const dn = schemaParser.extractDynamicName(value);
|
|
1151
|
+
return params.add(void 0, scopeName(scope, dn));
|
|
1221
1152
|
}
|
|
1153
|
+
return params.add(value);
|
|
1222
1154
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1155
|
+
|
|
1156
|
+
// src/builder/shared/order-by-utils.ts
|
|
1157
|
+
var flipNulls = (v) => {
|
|
1158
|
+
const s = String(v).toLowerCase();
|
|
1159
|
+
if (s === "first") return "last";
|
|
1160
|
+
if (s === "last") return "first";
|
|
1161
|
+
return v;
|
|
1162
|
+
};
|
|
1163
|
+
var flipSortString = (v) => {
|
|
1164
|
+
if (typeof v !== "string") return v;
|
|
1165
|
+
const s = v.toLowerCase();
|
|
1166
|
+
if (s === "asc") return "desc";
|
|
1167
|
+
if (s === "desc") return "asc";
|
|
1168
|
+
return v;
|
|
1169
|
+
};
|
|
1170
|
+
var getNextSort = (sortRaw) => {
|
|
1171
|
+
if (typeof sortRaw !== "string") return sortRaw;
|
|
1172
|
+
const s = sortRaw.toLowerCase();
|
|
1173
|
+
if (s === "asc") return "desc";
|
|
1174
|
+
if (s === "desc") return "asc";
|
|
1175
|
+
return sortRaw;
|
|
1176
|
+
};
|
|
1177
|
+
var flipObjectSort = (obj) => {
|
|
1178
|
+
const sortRaw = obj.sort;
|
|
1179
|
+
const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
|
|
1180
|
+
const nullsRaw = obj.nulls;
|
|
1181
|
+
if (typeof nullsRaw === "string") {
|
|
1182
|
+
out.nulls = flipNulls(nullsRaw);
|
|
1232
1183
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1184
|
+
return out;
|
|
1185
|
+
};
|
|
1186
|
+
var flipValue = (v) => {
|
|
1187
|
+
if (typeof v === "string") return flipSortString(v);
|
|
1188
|
+
if (isPlainObject(v)) return flipObjectSort(v);
|
|
1189
|
+
return v;
|
|
1190
|
+
};
|
|
1191
|
+
var assertSingleFieldObject = (item) => {
|
|
1192
|
+
if (!isPlainObject(item)) {
|
|
1193
|
+
throw new Error("orderBy array entries must be objects");
|
|
1236
1194
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
if (val === void 0) return "";
|
|
1241
|
-
if (schemaParser.isDynamicParameter(val)) {
|
|
1242
|
-
const placeholder2 = params.addAuto(val);
|
|
1243
|
-
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1195
|
+
const entries = Object.entries(item);
|
|
1196
|
+
if (entries.length !== 1) {
|
|
1197
|
+
throw new Error("orderBy array entries must have exactly one field");
|
|
1244
1198
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1199
|
+
return entries[0];
|
|
1200
|
+
};
|
|
1201
|
+
var flipOrderByArray = (orderBy) => {
|
|
1202
|
+
return orderBy.map((item) => {
|
|
1203
|
+
const [k, v] = assertSingleFieldObject(item);
|
|
1204
|
+
return { [k]: flipValue(v) };
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
var flipOrderByObject = (orderBy) => {
|
|
1208
|
+
const out = {};
|
|
1209
|
+
for (const [k, v] of Object.entries(orderBy)) {
|
|
1210
|
+
out[k] = flipValue(v);
|
|
1250
1211
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1212
|
+
return out;
|
|
1213
|
+
};
|
|
1214
|
+
function reverseOrderByInput(orderBy) {
|
|
1215
|
+
if (!isNotNullish(orderBy)) return orderBy;
|
|
1216
|
+
if (Array.isArray(orderBy)) {
|
|
1217
|
+
return flipOrderByArray(orderBy);
|
|
1253
1218
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1219
|
+
if (isPlainObject(orderBy)) {
|
|
1220
|
+
return flipOrderByObject(orderBy);
|
|
1221
|
+
}
|
|
1222
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1257
1223
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
const placeholder = params.addAuto(val);
|
|
1272
|
-
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1273
|
-
}
|
|
1274
|
-
function buildArrayParam(val, params, dialect) {
|
|
1275
|
-
if (schemaParser.isDynamicParameter(val)) {
|
|
1276
|
-
return params.addAuto(val);
|
|
1224
|
+
var normalizePairs = (pairs, parseValue) => {
|
|
1225
|
+
return pairs.map(([field, rawValue]) => {
|
|
1226
|
+
const parsed = parseValue(rawValue, field);
|
|
1227
|
+
return {
|
|
1228
|
+
[field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
1229
|
+
};
|
|
1230
|
+
});
|
|
1231
|
+
};
|
|
1232
|
+
function normalizeOrderByInput(orderBy, parseValue) {
|
|
1233
|
+
if (!isNotNullish(orderBy)) return [];
|
|
1234
|
+
if (Array.isArray(orderBy)) {
|
|
1235
|
+
const pairs = orderBy.map(assertSingleFieldObject);
|
|
1236
|
+
return normalizePairs(pairs, parseValue);
|
|
1277
1237
|
}
|
|
1278
|
-
if (
|
|
1279
|
-
|
|
1238
|
+
if (isPlainObject(orderBy)) {
|
|
1239
|
+
return normalizePairs(Object.entries(orderBy), parseValue);
|
|
1280
1240
|
}
|
|
1281
|
-
|
|
1282
|
-
return params.add(paramValue);
|
|
1241
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1283
1242
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
if (op === Ops.EQUALS) {
|
|
1292
|
-
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1293
|
-
}
|
|
1294
|
-
if (op === Ops.NOT) {
|
|
1295
|
-
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1296
|
-
}
|
|
1297
|
-
switch (op) {
|
|
1298
|
-
case Ops.HAS:
|
|
1299
|
-
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1300
|
-
case Ops.HAS_SOME:
|
|
1301
|
-
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1302
|
-
case Ops.HAS_EVERY:
|
|
1303
|
-
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1304
|
-
case Ops.IS_EMPTY:
|
|
1305
|
-
return handleArrayIsEmpty(expr, val, dialect);
|
|
1306
|
-
default:
|
|
1307
|
-
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
1308
|
-
}
|
|
1243
|
+
|
|
1244
|
+
// src/builder/pagination.ts
|
|
1245
|
+
var MAX_LIMIT_OFFSET = 2147483647;
|
|
1246
|
+
function parseDirectionRaw(raw, errorLabel) {
|
|
1247
|
+
const s = String(raw).toLowerCase();
|
|
1248
|
+
if (s === "asc" || s === "desc") return s;
|
|
1249
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1309
1250
|
}
|
|
1310
|
-
function
|
|
1311
|
-
if (
|
|
1312
|
-
|
|
1313
|
-
|
|
1251
|
+
function parseNullsRaw(raw, errorLabel) {
|
|
1252
|
+
if (!isNotNullish(raw)) return void 0;
|
|
1253
|
+
const s = String(raw).toLowerCase();
|
|
1254
|
+
if (s === "first" || s === "last") return s;
|
|
1255
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1256
|
+
}
|
|
1257
|
+
function requireOrderByObject(v, errorPrefix) {
|
|
1258
|
+
if (!isPlainObject(v) || !("sort" in v)) {
|
|
1259
|
+
throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
|
|
1314
1260
|
}
|
|
1315
|
-
|
|
1316
|
-
return arrayEquals(expr, placeholder, cast, dialect);
|
|
1261
|
+
return v;
|
|
1317
1262
|
}
|
|
1318
|
-
function
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
} else {
|
|
1326
|
-
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1327
|
-
operator: Ops.NOT,
|
|
1328
|
-
value: val
|
|
1329
|
-
});
|
|
1263
|
+
function assertAllowedOrderByKeys(obj, fieldName) {
|
|
1264
|
+
const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
|
|
1265
|
+
for (const k of Object.keys(obj)) {
|
|
1266
|
+
if (!allowed.has(k)) {
|
|
1267
|
+
throw new Error(
|
|
1268
|
+
fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
|
|
1269
|
+
);
|
|
1330
1270
|
}
|
|
1331
1271
|
}
|
|
1332
|
-
if (target === null) {
|
|
1333
|
-
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1334
|
-
}
|
|
1335
|
-
if (isEmptyArray(target)) {
|
|
1336
|
-
return arrayIsNotEmpty(expr, dialect);
|
|
1337
|
-
}
|
|
1338
|
-
const placeholder = buildArrayParam(target, params, dialect);
|
|
1339
|
-
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
1340
1272
|
}
|
|
1341
|
-
function
|
|
1342
|
-
|
|
1343
|
-
if (
|
|
1344
|
-
|
|
1345
|
-
operator: Ops.HAS,
|
|
1346
|
-
value: val
|
|
1347
|
-
});
|
|
1348
|
-
}
|
|
1349
|
-
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1350
|
-
throw createError(`has requires scalar value (single element), not array`, {
|
|
1351
|
-
operator: Ops.HAS,
|
|
1352
|
-
value: val
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
if (isPlainObject(val)) {
|
|
1356
|
-
throw createError(`has requires scalar value`, {
|
|
1357
|
-
operator: Ops.HAS,
|
|
1358
|
-
value: val
|
|
1359
|
-
});
|
|
1273
|
+
function parseOrderByValue(v, fieldName) {
|
|
1274
|
+
const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
|
|
1275
|
+
if (typeof v === "string") {
|
|
1276
|
+
return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
|
|
1360
1277
|
}
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1278
|
+
const obj = requireOrderByObject(v, errorPrefix);
|
|
1279
|
+
const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
|
|
1280
|
+
const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
|
|
1281
|
+
assertAllowedOrderByKeys(obj, fieldName);
|
|
1282
|
+
return { direction, nulls };
|
|
1363
1283
|
}
|
|
1364
|
-
function
|
|
1365
|
-
if (
|
|
1366
|
-
|
|
1367
|
-
const placeholder2 = params.addAuto(val);
|
|
1368
|
-
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
1284
|
+
function normalizeFiniteInteger(name, v) {
|
|
1285
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1286
|
+
throw new Error(`${name} must be an integer`);
|
|
1369
1287
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1288
|
+
return v;
|
|
1289
|
+
}
|
|
1290
|
+
function normalizeNonNegativeInt(name, v) {
|
|
1291
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1292
|
+
const n = normalizeFiniteInteger(name, v);
|
|
1293
|
+
if (n < 0) {
|
|
1294
|
+
throw new Error(`${name} must be >= 0`);
|
|
1375
1295
|
}
|
|
1376
|
-
if (
|
|
1377
|
-
throw
|
|
1378
|
-
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1379
|
-
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1380
|
-
);
|
|
1296
|
+
if (n > MAX_LIMIT_OFFSET) {
|
|
1297
|
+
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
1381
1298
|
}
|
|
1382
|
-
|
|
1383
|
-
const paramValue = prepareArrayParam(val, dialect);
|
|
1384
|
-
const placeholder = params.add(paramValue);
|
|
1385
|
-
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
1299
|
+
return n;
|
|
1386
1300
|
}
|
|
1387
|
-
function
|
|
1388
|
-
|
|
1389
|
-
const placeholder = buildArrayParam(val, params, dialect);
|
|
1390
|
-
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
1301
|
+
function hasNonNullishProp(v, key) {
|
|
1302
|
+
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
1391
1303
|
}
|
|
1392
|
-
function
|
|
1393
|
-
if (
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1304
|
+
function normalizeIntegerOrDynamic(name, v) {
|
|
1305
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1306
|
+
return normalizeFiniteInteger(name, v);
|
|
1307
|
+
}
|
|
1308
|
+
function readSkipTake(relArgs) {
|
|
1309
|
+
const hasSkip = hasNonNullishProp(relArgs, "skip");
|
|
1310
|
+
const hasTake = hasNonNullishProp(relArgs, "take");
|
|
1311
|
+
if (!hasSkip && !hasTake) {
|
|
1312
|
+
return {
|
|
1313
|
+
hasSkip: false,
|
|
1314
|
+
hasTake: false,
|
|
1315
|
+
skipVal: void 0,
|
|
1316
|
+
takeVal: void 0
|
|
1317
|
+
};
|
|
1398
1318
|
}
|
|
1399
|
-
|
|
1319
|
+
const obj = relArgs;
|
|
1320
|
+
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
1321
|
+
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
1322
|
+
return { hasSkip, hasTake, skipVal, takeVal };
|
|
1400
1323
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1324
|
+
function buildOrderByFragment(entries, alias, dialect, model) {
|
|
1325
|
+
if (entries.length === 0) return "";
|
|
1326
|
+
const out = [];
|
|
1327
|
+
for (const e of entries) {
|
|
1328
|
+
const dir = e.direction.toUpperCase();
|
|
1329
|
+
const c = col(alias, e.field, model);
|
|
1330
|
+
if (dialect === "postgres") {
|
|
1331
|
+
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
1332
|
+
out.push(`${c} ${dir}${nulls}`);
|
|
1333
|
+
continue;
|
|
1411
1334
|
}
|
|
1412
|
-
if (
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
);
|
|
1335
|
+
if (isNotNullish(e.nulls)) {
|
|
1336
|
+
const isNullExpr = `(${c} IS NULL)`;
|
|
1337
|
+
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
1338
|
+
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
1339
|
+
out.push(`${c} ${dir}`);
|
|
1340
|
+
continue;
|
|
1417
1341
|
}
|
|
1342
|
+
out.push(`${c} ${dir}`);
|
|
1418
1343
|
}
|
|
1344
|
+
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
1419
1345
|
}
|
|
1420
|
-
function
|
|
1421
|
-
if (
|
|
1422
|
-
|
|
1423
|
-
return handleJsonPath(expr, val, params, dialect);
|
|
1424
|
-
}
|
|
1425
|
-
const jsonWildcards = {
|
|
1426
|
-
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1427
|
-
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1428
|
-
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1429
|
-
};
|
|
1430
|
-
if (op in jsonWildcards) {
|
|
1431
|
-
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
1346
|
+
function defaultNullsFor(dialect, direction) {
|
|
1347
|
+
if (dialect === "postgres") {
|
|
1348
|
+
return direction === "asc" ? "last" : "first";
|
|
1432
1349
|
}
|
|
1433
|
-
|
|
1350
|
+
return direction === "asc" ? "first" : "last";
|
|
1434
1351
|
}
|
|
1435
|
-
function
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1352
|
+
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1353
|
+
const existing = /* @__PURE__ */ new Map();
|
|
1354
|
+
for (const e of orderEntries) existing.set(e.field, e);
|
|
1355
|
+
const out = [...orderEntries];
|
|
1356
|
+
for (const [field] of cursorEntries) {
|
|
1357
|
+
if (!existing.has(field)) {
|
|
1358
|
+
out.push({ field, direction: "asc" });
|
|
1359
|
+
existing.set(field, out[out.length - 1]);
|
|
1360
|
+
}
|
|
1442
1361
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
["<", v.lt],
|
|
1450
|
-
["<=", v.lte]
|
|
1451
|
-
];
|
|
1452
|
-
const ops = rawOps.filter(
|
|
1453
|
-
([, value]) => value !== void 0
|
|
1454
|
-
);
|
|
1455
|
-
if (ops.length === 0) {
|
|
1456
|
-
throw createError("JSON path query missing comparison operator", {
|
|
1457
|
-
operator: Ops.PATH
|
|
1458
|
-
});
|
|
1362
|
+
return out;
|
|
1363
|
+
}
|
|
1364
|
+
function buildCursorFilterParts(cursor, cursorAlias, params, model) {
|
|
1365
|
+
const entries = Object.entries(cursor);
|
|
1366
|
+
if (entries.length === 0) {
|
|
1367
|
+
throw new Error("cursor must have at least one field");
|
|
1459
1368
|
}
|
|
1369
|
+
const placeholdersByField = /* @__PURE__ */ new Map();
|
|
1460
1370
|
const parts = [];
|
|
1461
|
-
for (const [
|
|
1371
|
+
for (const [field, value] of entries) {
|
|
1372
|
+
const c = `${cursorAlias}.${quote(field)}`;
|
|
1462
1373
|
if (value === null) {
|
|
1463
|
-
|
|
1464
|
-
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1374
|
+
parts.push(`${c} IS NULL`);
|
|
1465
1375
|
continue;
|
|
1466
1376
|
}
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
parts.push(`${
|
|
1470
|
-
}
|
|
1471
|
-
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1472
|
-
}
|
|
1473
|
-
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1474
|
-
if (!isNotNullish(val)) {
|
|
1475
|
-
throw createError(`JSON string operator requires non-null value`, {
|
|
1476
|
-
operator: op,
|
|
1477
|
-
value: val
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1481
|
-
throw createError(`JSON string operator requires scalar value`, {
|
|
1482
|
-
operator: op,
|
|
1483
|
-
value: val
|
|
1484
|
-
});
|
|
1485
|
-
}
|
|
1486
|
-
const strVal = String(val);
|
|
1487
|
-
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1488
|
-
throw createError(
|
|
1489
|
-
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1490
|
-
{ operator: op }
|
|
1491
|
-
);
|
|
1377
|
+
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
1378
|
+
placeholdersByField.set(field, ph);
|
|
1379
|
+
parts.push(`${c} = ${ph}`);
|
|
1492
1380
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1381
|
+
return {
|
|
1382
|
+
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
1383
|
+
placeholdersByField
|
|
1384
|
+
};
|
|
1496
1385
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
function isListRelation(fieldType) {
|
|
1501
|
-
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1386
|
+
function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
|
|
1387
|
+
const colName = quote(field);
|
|
1388
|
+
return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1502
1389
|
}
|
|
1503
|
-
function
|
|
1504
|
-
|
|
1505
|
-
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1506
|
-
if (isLocal) {
|
|
1507
|
-
if (fkFields.length === 0) {
|
|
1508
|
-
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1509
|
-
field: field.name
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
const parts = fkFields.map((fk) => {
|
|
1513
|
-
const safe = fk.replace(/"/g, '""');
|
|
1514
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
1515
|
-
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1516
|
-
});
|
|
1517
|
-
if (parts.length === 1) return parts[0];
|
|
1518
|
-
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
1519
|
-
}
|
|
1520
|
-
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
|
|
1521
|
-
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
1390
|
+
function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
|
|
1391
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1522
1392
|
}
|
|
1523
|
-
function
|
|
1524
|
-
|
|
1525
|
-
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1393
|
+
function buildCursorEqualityExpr(columnExpr, valueExpr) {
|
|
1394
|
+
return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
|
|
1526
1395
|
}
|
|
1527
|
-
function
|
|
1528
|
-
const
|
|
1529
|
-
|
|
1396
|
+
function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
|
|
1397
|
+
const op = direction === "asc" ? ">" : "<";
|
|
1398
|
+
if (nulls === "first") {
|
|
1399
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
1400
|
+
}
|
|
1401
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
|
|
1530
1402
|
}
|
|
1531
|
-
function
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
if (noneValue !== void 0 && noneValue !== null) {
|
|
1544
|
-
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1545
|
-
alias: relAlias,
|
|
1546
|
-
model: relModel,
|
|
1547
|
-
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1548
|
-
isSubquery: true,
|
|
1549
|
-
depth: ctx.depth + 1
|
|
1550
|
-
}));
|
|
1551
|
-
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1552
|
-
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1553
|
-
if (canOptimize) {
|
|
1554
|
-
const checkField = relModel.fields.find(
|
|
1555
|
-
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1556
|
-
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1557
|
-
if (checkField) {
|
|
1558
|
-
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
1559
|
-
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1560
|
-
return Object.freeze({
|
|
1561
|
-
clause: whereClause,
|
|
1562
|
-
joins: [leftJoinSql]
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1403
|
+
function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params, model) {
|
|
1404
|
+
const parts = [];
|
|
1405
|
+
for (const [field, value] of Object.entries(cursor)) {
|
|
1406
|
+
const c = col(outerAlias, field, model);
|
|
1407
|
+
if (value === null) {
|
|
1408
|
+
parts.push(`${c} IS NULL`);
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
const existing = placeholdersByField.get(field);
|
|
1412
|
+
if (typeof existing === "string" && existing.length > 0) {
|
|
1413
|
+
parts.push(`${c} = ${existing}`);
|
|
1414
|
+
continue;
|
|
1565
1415
|
}
|
|
1416
|
+
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
1417
|
+
parts.push(`${c} = ${ph}`);
|
|
1566
1418
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1419
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1420
|
+
}
|
|
1421
|
+
function buildOrderEntries(orderBy) {
|
|
1422
|
+
const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
|
|
1423
|
+
const entries = [];
|
|
1424
|
+
for (const item of normalized) {
|
|
1425
|
+
for (const [field, value] of Object.entries(item)) {
|
|
1426
|
+
if (typeof value === "string") {
|
|
1427
|
+
entries.push({ field, direction: value });
|
|
1428
|
+
} else {
|
|
1429
|
+
entries.push({
|
|
1430
|
+
field,
|
|
1431
|
+
direction: value.sort,
|
|
1432
|
+
nulls: value.nulls
|
|
1433
|
+
});
|
|
1581
1434
|
}
|
|
1582
1435
|
}
|
|
1583
|
-
];
|
|
1584
|
-
const clauses = [];
|
|
1585
|
-
for (const { key, wrap } of filters) {
|
|
1586
|
-
const raw = value[key];
|
|
1587
|
-
if (raw === void 0 || raw === null) continue;
|
|
1588
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
1589
|
-
alias: relAlias,
|
|
1590
|
-
model: relModel,
|
|
1591
|
-
path: [...ctx.path, fieldName, key],
|
|
1592
|
-
isSubquery: true,
|
|
1593
|
-
depth: ctx.depth + 1
|
|
1594
|
-
}));
|
|
1595
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1596
|
-
clauses.push(wrap(sub.clause, j));
|
|
1597
|
-
}
|
|
1598
|
-
if (clauses.length === 0) {
|
|
1599
|
-
throw createError(
|
|
1600
|
-
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
1601
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1602
|
-
);
|
|
1603
1436
|
}
|
|
1604
|
-
return
|
|
1605
|
-
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
1606
|
-
joins: NO_JOINS
|
|
1607
|
-
});
|
|
1437
|
+
return entries;
|
|
1608
1438
|
}
|
|
1609
|
-
function
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
field,
|
|
1616
|
-
relModel,
|
|
1617
|
-
relTable,
|
|
1618
|
-
relAlias,
|
|
1619
|
-
join: join3
|
|
1620
|
-
} = args;
|
|
1621
|
-
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
1622
|
-
if (hasSomeEveryNone) {
|
|
1623
|
-
throw createError(
|
|
1624
|
-
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
1625
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1626
|
-
);
|
|
1627
|
-
}
|
|
1628
|
-
const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
|
|
1629
|
-
const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
|
|
1630
|
-
let filterKey;
|
|
1631
|
-
let filterVal;
|
|
1632
|
-
if (hasIs) {
|
|
1633
|
-
filterKey = "is";
|
|
1634
|
-
filterVal = value.is;
|
|
1635
|
-
} else if (hasIsNot) {
|
|
1636
|
-
filterKey = "isNot";
|
|
1637
|
-
filterVal = value.isNot;
|
|
1638
|
-
} else {
|
|
1639
|
-
filterKey = "is";
|
|
1640
|
-
filterVal = value;
|
|
1439
|
+
function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
|
|
1440
|
+
var _a;
|
|
1441
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1442
|
+
const cursorEntries = Object.entries(cursor);
|
|
1443
|
+
if (cursorEntries.length === 0) {
|
|
1444
|
+
throw new Error("cursor must have at least one field");
|
|
1641
1445
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1648
|
-
if (filterVal === null) {
|
|
1649
|
-
const wantNull = filterKey === "is";
|
|
1650
|
-
const clause2 = buildToOneNullCheck(
|
|
1446
|
+
const cursorAlias = "__tp_cursor_src";
|
|
1447
|
+
const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
|
|
1448
|
+
let orderEntries = buildOrderEntries(orderBy);
|
|
1449
|
+
if (orderEntries.length === 0) {
|
|
1450
|
+
orderEntries = cursorEntries.map(([field]) => ({
|
|
1651
1451
|
field,
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
wantNull
|
|
1657
|
-
);
|
|
1658
|
-
return Object.freeze({
|
|
1659
|
-
clause: clause2,
|
|
1660
|
-
joins: NO_JOINS
|
|
1661
|
-
});
|
|
1452
|
+
direction: "asc"
|
|
1453
|
+
}));
|
|
1454
|
+
} else {
|
|
1455
|
+
orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
|
|
1662
1456
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1457
|
+
const existsExpr = buildCursorRowExistsExpr(
|
|
1458
|
+
tableName,
|
|
1459
|
+
cursorAlias,
|
|
1460
|
+
cursorWhereSql
|
|
1461
|
+
);
|
|
1462
|
+
const outerCursorMatch = buildOuterCursorMatch(
|
|
1463
|
+
cursor,
|
|
1464
|
+
alias,
|
|
1465
|
+
placeholdersByField,
|
|
1466
|
+
params,
|
|
1467
|
+
model
|
|
1468
|
+
);
|
|
1469
|
+
const orClauses = [];
|
|
1470
|
+
for (let level = 0; level < orderEntries.length; level++) {
|
|
1471
|
+
const andParts = [];
|
|
1472
|
+
for (let i = 0; i < level; i++) {
|
|
1473
|
+
const e2 = orderEntries[i];
|
|
1474
|
+
const c2 = col(alias, e2.field, model);
|
|
1475
|
+
const v2 = cursorValueExpr(
|
|
1476
|
+
tableName,
|
|
1477
|
+
cursorAlias,
|
|
1478
|
+
cursorWhereSql,
|
|
1479
|
+
e2.field);
|
|
1480
|
+
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
1481
|
+
}
|
|
1482
|
+
const e = orderEntries[level];
|
|
1483
|
+
const c = col(alias, e.field, model);
|
|
1484
|
+
const v = cursorValueExpr(
|
|
1485
|
+
tableName,
|
|
1486
|
+
cursorAlias,
|
|
1487
|
+
cursorWhereSql,
|
|
1488
|
+
e.field);
|
|
1489
|
+
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
1490
|
+
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
1491
|
+
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
1673
1492
|
}
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
model: relModel,
|
|
1677
|
-
path: [...ctx.path, fieldName, filterKey],
|
|
1678
|
-
isSubquery: true,
|
|
1679
|
-
depth: ctx.depth + 1
|
|
1680
|
-
}));
|
|
1681
|
-
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
|
|
1682
|
-
return Object.freeze({
|
|
1683
|
-
clause,
|
|
1684
|
-
joins: NO_JOINS
|
|
1685
|
-
});
|
|
1493
|
+
const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
|
|
1494
|
+
return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
|
|
1686
1495
|
}
|
|
1687
|
-
function
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
modelName: ctx.model.name,
|
|
1693
|
-
value
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1496
|
+
function buildOrderBy(orderBy, alias, dialect, model) {
|
|
1497
|
+
const entries = buildOrderEntries(orderBy);
|
|
1498
|
+
if (entries.length === 0) return "";
|
|
1499
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1500
|
+
return buildOrderByFragment(entries, alias, d, model);
|
|
1696
1501
|
}
|
|
1697
|
-
function
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
modelName: ctx.model.name
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1706
|
-
const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
|
|
1707
|
-
if (!isNotNullish(relModel)) {
|
|
1708
|
-
throw createError(
|
|
1709
|
-
`Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
|
|
1710
|
-
{
|
|
1711
|
-
field: fieldName,
|
|
1712
|
-
path: ctx.path,
|
|
1713
|
-
modelName: ctx.model.name
|
|
1714
|
-
}
|
|
1502
|
+
function buildOrderByClause(args, alias, dialect, model) {
|
|
1503
|
+
if (!isNotNullish(args.orderBy)) return "";
|
|
1504
|
+
const result = buildOrderBy(args.orderBy, alias, dialect, model);
|
|
1505
|
+
if (!isNonEmptyString(result)) {
|
|
1506
|
+
throw new Error(
|
|
1507
|
+
"buildOrderByClause: orderBy specified but produced empty result"
|
|
1715
1508
|
);
|
|
1716
1509
|
}
|
|
1717
|
-
|
|
1718
|
-
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
1719
|
-
relModel.tableName,
|
|
1720
|
-
ctx.dialect
|
|
1721
|
-
);
|
|
1722
|
-
const relAlias = ctx.aliasGen.next(fieldName);
|
|
1723
|
-
const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
1724
|
-
const args = {
|
|
1725
|
-
fieldName,
|
|
1726
|
-
value,
|
|
1727
|
-
ctx,
|
|
1728
|
-
whereBuilder,
|
|
1729
|
-
field,
|
|
1730
|
-
relModel,
|
|
1731
|
-
relTable,
|
|
1732
|
-
relAlias,
|
|
1733
|
-
join: join3
|
|
1734
|
-
};
|
|
1735
|
-
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
1736
|
-
return buildToOneRelationFilters(args);
|
|
1510
|
+
return result;
|
|
1737
1511
|
}
|
|
1738
|
-
function
|
|
1739
|
-
|
|
1740
|
-
|
|
1512
|
+
function normalizeTakeLike(v) {
|
|
1513
|
+
const n = normalizeIntLike("take", v, {
|
|
1514
|
+
min: Number.MIN_SAFE_INTEGER,
|
|
1515
|
+
max: MAX_LIMIT_OFFSET,
|
|
1516
|
+
allowZero: true
|
|
1517
|
+
});
|
|
1518
|
+
if (typeof n === "number") {
|
|
1519
|
+
if (n === 0) return 0;
|
|
1520
|
+
}
|
|
1521
|
+
return n;
|
|
1741
1522
|
}
|
|
1742
|
-
function
|
|
1743
|
-
return
|
|
1523
|
+
function normalizeSkipLike(v) {
|
|
1524
|
+
return normalizeIntLike("skip", v, {
|
|
1525
|
+
min: 0,
|
|
1526
|
+
max: MAX_LIMIT_OFFSET,
|
|
1527
|
+
allowZero: true
|
|
1528
|
+
});
|
|
1744
1529
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
path,
|
|
1753
|
-
modelName: model.name,
|
|
1754
|
-
availableFields: model.fields.map((f) => f.name)
|
|
1755
|
-
});
|
|
1530
|
+
function getPaginationParams(method, args) {
|
|
1531
|
+
if (method === "findMany") {
|
|
1532
|
+
return {
|
|
1533
|
+
take: normalizeTakeLike(args.take),
|
|
1534
|
+
skip: normalizeSkipLike(args.skip),
|
|
1535
|
+
cursor: args.cursor
|
|
1536
|
+
};
|
|
1756
1537
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
if (!isNotNullish(fieldType)) return;
|
|
1761
|
-
const ARRAY_OPS = /* @__PURE__ */ new Set([
|
|
1762
|
-
Ops.HAS,
|
|
1763
|
-
Ops.HAS_SOME,
|
|
1764
|
-
Ops.HAS_EVERY,
|
|
1765
|
-
Ops.IS_EMPTY
|
|
1766
|
-
]);
|
|
1767
|
-
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
1768
|
-
Ops.PATH,
|
|
1769
|
-
Ops.STRING_CONTAINS,
|
|
1770
|
-
Ops.STRING_STARTS_WITH,
|
|
1771
|
-
Ops.STRING_ENDS_WITH
|
|
1772
|
-
]);
|
|
1773
|
-
const isArrayOp = ARRAY_OPS.has(op);
|
|
1774
|
-
const isFieldArray = isArrayType(fieldType);
|
|
1775
|
-
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
1776
|
-
if (arrayOpMismatch) {
|
|
1777
|
-
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
1778
|
-
operator: op,
|
|
1779
|
-
field: fieldName,
|
|
1780
|
-
path,
|
|
1781
|
-
modelName
|
|
1782
|
-
});
|
|
1538
|
+
if (method === "findFirst") {
|
|
1539
|
+
const skip = normalizeSkipLike(args.skip);
|
|
1540
|
+
return { take: 1, skip: skip != null ? skip : 0 };
|
|
1783
1541
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
1787
|
-
if (jsonOpMismatch) {
|
|
1788
|
-
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
1789
|
-
operator: op,
|
|
1790
|
-
field: fieldName,
|
|
1791
|
-
path,
|
|
1792
|
-
modelName
|
|
1793
|
-
});
|
|
1542
|
+
if (method === "findUnique") {
|
|
1543
|
+
return { take: 1, skip: 0 };
|
|
1794
1544
|
}
|
|
1545
|
+
return {};
|
|
1795
1546
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
}
|
|
1806
|
-
return buildWhereInternal(where, ctx, this);
|
|
1547
|
+
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1548
|
+
const entries = Object.entries(val).filter(
|
|
1549
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
1550
|
+
);
|
|
1551
|
+
if (entries.length === 0) return "";
|
|
1552
|
+
const clauses = [];
|
|
1553
|
+
for (const [subOp, subVal] of entries) {
|
|
1554
|
+
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1555
|
+
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1807
1556
|
}
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
var whereBuilderInstance = new WhereBuilder();
|
|
1812
|
-
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
1813
|
-
return Object.freeze({ clause, joins });
|
|
1557
|
+
if (clauses.length === 0) return "";
|
|
1558
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1559
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1814
1560
|
}
|
|
1815
|
-
function
|
|
1816
|
-
if (
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1561
|
+
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1562
|
+
if (val === void 0) return "";
|
|
1563
|
+
if (val === null) {
|
|
1564
|
+
return handleNullValue(expr, op);
|
|
1565
|
+
}
|
|
1566
|
+
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1567
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1568
|
+
}
|
|
1569
|
+
if (op === Ops.NOT) {
|
|
1570
|
+
const placeholder = params.addAuto(val);
|
|
1571
|
+
return `${expr} <> ${placeholder}`;
|
|
1572
|
+
}
|
|
1573
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1574
|
+
const placeholder = params.addAuto(val);
|
|
1575
|
+
return caseInsensitiveEquals(expr, placeholder);
|
|
1576
|
+
}
|
|
1577
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1578
|
+
Ops.CONTAINS,
|
|
1579
|
+
Ops.STARTS_WITH,
|
|
1580
|
+
Ops.ENDS_WITH
|
|
1581
|
+
]);
|
|
1582
|
+
if (STRING_LIKE_OPS.has(op)) {
|
|
1583
|
+
if (!isNotNullish(dialect)) {
|
|
1584
|
+
throw createError(`Like operators require a SQL dialect`, {
|
|
1585
|
+
operator: op
|
|
1586
|
+
});
|
|
1823
1587
|
}
|
|
1588
|
+
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1824
1589
|
}
|
|
1825
|
-
|
|
1590
|
+
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1591
|
+
if (!isNotNullish(dialect)) {
|
|
1592
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1593
|
+
}
|
|
1594
|
+
return handleInOperator(expr, op, val, params, dialect);
|
|
1595
|
+
}
|
|
1596
|
+
return handleComparisonOperator(expr, op, val, params);
|
|
1826
1597
|
}
|
|
1827
|
-
function
|
|
1828
|
-
if (
|
|
1829
|
-
if (
|
|
1598
|
+
function handleNullValue(expr, op) {
|
|
1599
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1600
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1601
|
+
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1830
1602
|
}
|
|
1831
|
-
function
|
|
1832
|
-
if (
|
|
1833
|
-
if (
|
|
1834
|
-
|
|
1835
|
-
return null;
|
|
1603
|
+
function normalizeMode(v) {
|
|
1604
|
+
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1605
|
+
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1606
|
+
return void 0;
|
|
1836
1607
|
}
|
|
1837
|
-
function
|
|
1838
|
-
|
|
1608
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1609
|
+
const innerMode = normalizeMode(val.mode);
|
|
1610
|
+
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1611
|
+
return buildNotComposite(
|
|
1612
|
+
expr,
|
|
1613
|
+
val,
|
|
1614
|
+
params,
|
|
1615
|
+
dialect,
|
|
1616
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1617
|
+
` ${SQL_TEMPLATES.AND} `
|
|
1618
|
+
);
|
|
1839
1619
|
}
|
|
1840
|
-
function
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1620
|
+
function buildDynamicLikePattern(op, placeholder, dialect) {
|
|
1621
|
+
if (dialect === "postgres") {
|
|
1622
|
+
switch (op) {
|
|
1623
|
+
case Ops.CONTAINS:
|
|
1624
|
+
return `('%' || ${placeholder} || '%')`;
|
|
1625
|
+
case Ops.STARTS_WITH:
|
|
1626
|
+
return `(${placeholder} || '%')`;
|
|
1627
|
+
case Ops.ENDS_WITH:
|
|
1628
|
+
return `('%' || ${placeholder})`;
|
|
1629
|
+
default:
|
|
1630
|
+
return placeholder;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
switch (op) {
|
|
1634
|
+
case Ops.CONTAINS:
|
|
1635
|
+
return `('%' || ${placeholder} || '%')`;
|
|
1636
|
+
case Ops.STARTS_WITH:
|
|
1637
|
+
return `(${placeholder} || '%')`;
|
|
1638
|
+
case Ops.ENDS_WITH:
|
|
1639
|
+
return `('%' || ${placeholder})`;
|
|
1640
|
+
default:
|
|
1641
|
+
return placeholder;
|
|
1844
1642
|
}
|
|
1845
|
-
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
1846
1643
|
}
|
|
1847
|
-
function
|
|
1848
|
-
|
|
1849
|
-
if (
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
field: key,
|
|
1855
|
-
modelName: ctx.model.name,
|
|
1856
|
-
value
|
|
1857
|
-
});
|
|
1644
|
+
function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
1645
|
+
if (val === void 0) return "";
|
|
1646
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1647
|
+
const placeholder2 = params.addAuto(val);
|
|
1648
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
|
|
1649
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1650
|
+
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1858
1651
|
}
|
|
1859
|
-
return
|
|
1652
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
|
|
1860
1653
|
}
|
|
1861
|
-
|
|
1654
|
+
const placeholder = params.add(Wildcards[op](String(val)));
|
|
1655
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1656
|
+
return caseInsensitiveLike(expr, placeholder, dialect);
|
|
1657
|
+
}
|
|
1658
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
|
|
1862
1659
|
}
|
|
1863
|
-
function
|
|
1864
|
-
if (
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
);
|
|
1660
|
+
function handleInOperator(expr, op, val, params, dialect) {
|
|
1661
|
+
if (val === void 0) return "";
|
|
1662
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1663
|
+
const placeholder2 = params.addAuto(val);
|
|
1664
|
+
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1869
1665
|
}
|
|
1870
|
-
if (
|
|
1871
|
-
|
|
1666
|
+
if (!Array.isArray(val)) {
|
|
1667
|
+
throw createError(`IN operators require array value`, {
|
|
1668
|
+
operator: op,
|
|
1669
|
+
value: val
|
|
1670
|
+
});
|
|
1872
1671
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
for (const [key, value] of Object.entries(where)) {
|
|
1876
|
-
if (value === void 0) continue;
|
|
1877
|
-
const result = buildWhereEntry(key, value, ctx, builder);
|
|
1878
|
-
appendResult(result, clauses, allJoins);
|
|
1672
|
+
if (val.length === 0) {
|
|
1673
|
+
return op === Ops.IN ? "0=1" : "1=1";
|
|
1879
1674
|
}
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1675
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1676
|
+
const placeholder = params.add(paramValue);
|
|
1677
|
+
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
1882
1678
|
}
|
|
1883
|
-
function
|
|
1884
|
-
if (
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
}
|
|
1896
|
-
out.push(v);
|
|
1897
|
-
}
|
|
1898
|
-
return out;
|
|
1899
|
-
}
|
|
1900
|
-
if (isPlainObject(value)) {
|
|
1901
|
-
return [value];
|
|
1679
|
+
function handleComparisonOperator(expr, op, val, params) {
|
|
1680
|
+
if (val === void 0) return "";
|
|
1681
|
+
const COMPARISON_OPS2 = {
|
|
1682
|
+
[Ops.EQUALS]: "=",
|
|
1683
|
+
[Ops.GT]: ">",
|
|
1684
|
+
[Ops.GTE]: ">=",
|
|
1685
|
+
[Ops.LT]: "<",
|
|
1686
|
+
[Ops.LTE]: "<="
|
|
1687
|
+
};
|
|
1688
|
+
const sqlOp = COMPARISON_OPS2[op];
|
|
1689
|
+
if (!sqlOp) {
|
|
1690
|
+
throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
|
|
1902
1691
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
modelName: ctx.model.name,
|
|
1906
|
-
value
|
|
1907
|
-
});
|
|
1692
|
+
const placeholder = params.addAuto(val);
|
|
1693
|
+
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1908
1694
|
}
|
|
1909
|
-
function
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
for (let i = 0; i < conditions.length; i++) {
|
|
1913
|
-
const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
|
|
1914
|
-
path: [...ctx.path, operator, String(i)],
|
|
1915
|
-
depth: ctx.depth + 1
|
|
1916
|
-
}));
|
|
1917
|
-
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
1918
|
-
if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
|
|
1919
|
-
clauses.push(`(${result.clause})`);
|
|
1920
|
-
}
|
|
1695
|
+
function buildArrayParam(val, params, dialect) {
|
|
1696
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1697
|
+
return params.addAuto(val);
|
|
1921
1698
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
clauses: Object.freeze(clauses)
|
|
1925
|
-
};
|
|
1926
|
-
}
|
|
1927
|
-
function buildLogicalClause(operator, clauses) {
|
|
1928
|
-
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
1929
|
-
if (operator === "NOT") {
|
|
1930
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1931
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
1699
|
+
if (!Array.isArray(val)) {
|
|
1700
|
+
throw createError(`Array operation requires array value`, { value: val });
|
|
1932
1701
|
}
|
|
1933
|
-
|
|
1702
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1703
|
+
return params.add(paramValue);
|
|
1934
1704
|
}
|
|
1935
|
-
function
|
|
1936
|
-
|
|
1937
|
-
if (
|
|
1938
|
-
|
|
1939
|
-
|
|
1705
|
+
function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
|
|
1706
|
+
if (val === void 0) return "";
|
|
1707
|
+
if (val === null) {
|
|
1708
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1709
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1940
1710
|
}
|
|
1941
|
-
const
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
ctx,
|
|
1945
|
-
builder
|
|
1946
|
-
);
|
|
1947
|
-
const clause = buildLogicalClause(operator, clauses);
|
|
1948
|
-
return freezeResult(clause, joins);
|
|
1949
|
-
}
|
|
1950
|
-
function buildScalarField(fieldName, value, ctx) {
|
|
1951
|
-
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
1952
|
-
const expr = col(ctx.alias, fieldName);
|
|
1953
|
-
if (value === null) {
|
|
1954
|
-
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
1711
|
+
const cast = getArrayType(fieldType, dialect);
|
|
1712
|
+
if (op === Ops.EQUALS) {
|
|
1713
|
+
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1955
1714
|
}
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
1958
|
-
const ops = Object.entries(value).filter(
|
|
1959
|
-
([k, v]) => k !== "mode" && v !== void 0
|
|
1960
|
-
);
|
|
1961
|
-
if (ops.length === 0) {
|
|
1962
|
-
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
1963
|
-
}
|
|
1964
|
-
const parts = [];
|
|
1965
|
-
for (const [op, val] of ops) {
|
|
1966
|
-
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
1967
|
-
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
1968
|
-
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
1969
|
-
}
|
|
1970
|
-
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
1971
|
-
return freezeResult(clause2, EMPTY_JOINS);
|
|
1972
|
-
}
|
|
1973
|
-
const clause = buildOperator(
|
|
1974
|
-
expr,
|
|
1975
|
-
Ops.EQUALS,
|
|
1976
|
-
value,
|
|
1977
|
-
ctx,
|
|
1978
|
-
void 0,
|
|
1979
|
-
field.type
|
|
1980
|
-
);
|
|
1981
|
-
return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
1982
|
-
}
|
|
1983
|
-
function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
1984
|
-
if (fieldType && isArrayType(fieldType)) {
|
|
1985
|
-
return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
|
|
1715
|
+
if (op === Ops.NOT) {
|
|
1716
|
+
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1986
1717
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
return
|
|
1996
|
-
|
|
1718
|
+
switch (op) {
|
|
1719
|
+
case Ops.HAS:
|
|
1720
|
+
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1721
|
+
case Ops.HAS_SOME:
|
|
1722
|
+
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1723
|
+
case Ops.HAS_EVERY:
|
|
1724
|
+
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1725
|
+
case Ops.IS_EMPTY:
|
|
1726
|
+
return handleArrayIsEmpty(expr, val, dialect);
|
|
1727
|
+
default:
|
|
1728
|
+
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
1997
1729
|
}
|
|
1998
|
-
return buildScalarOperator(
|
|
1999
|
-
expr,
|
|
2000
|
-
op,
|
|
2001
|
-
val,
|
|
2002
|
-
ctx.params,
|
|
2003
|
-
mode,
|
|
2004
|
-
fieldType,
|
|
2005
|
-
ctx.dialect
|
|
2006
|
-
);
|
|
2007
1730
|
}
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
const
|
|
2014
|
-
|
|
2015
|
-
const fallback = base.length > 0 ? base : "_t";
|
|
2016
|
-
const lowered = fallback.toLowerCase();
|
|
2017
|
-
return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
1731
|
+
function handleArrayEquals(expr, val, params, cast, dialect) {
|
|
1732
|
+
if (val === void 0) return "";
|
|
1733
|
+
if (isEmptyArray(val)) {
|
|
1734
|
+
return arrayIsEmpty(expr, dialect);
|
|
1735
|
+
}
|
|
1736
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1737
|
+
return arrayEquals(expr, placeholder, cast, dialect);
|
|
2018
1738
|
}
|
|
2019
|
-
function
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const maxLen = 63;
|
|
2032
|
-
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2033
|
-
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2034
|
-
const alias = `${trimmedBase}${suffix}`;
|
|
2035
|
-
counter += 1;
|
|
2036
|
-
if (usedAliases.has(alias)) {
|
|
2037
|
-
throw new Error(
|
|
2038
|
-
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
2039
|
-
);
|
|
2040
|
-
}
|
|
2041
|
-
usedAliases.add(alias);
|
|
2042
|
-
return alias;
|
|
1739
|
+
function handleArrayNot(expr, val, params, cast, dialect) {
|
|
1740
|
+
if (val === void 0) return "";
|
|
1741
|
+
let target = val;
|
|
1742
|
+
if (isPlainObject(val)) {
|
|
1743
|
+
const entries = Object.entries(val).filter(([, v]) => v !== void 0);
|
|
1744
|
+
if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
|
|
1745
|
+
target = entries[0][1];
|
|
1746
|
+
} else {
|
|
1747
|
+
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1748
|
+
operator: Ops.NOT,
|
|
1749
|
+
value: val
|
|
1750
|
+
});
|
|
2043
1751
|
}
|
|
2044
|
-
};
|
|
2045
|
-
}
|
|
2046
|
-
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
2047
|
-
function assertSameLength(params, mappings) {
|
|
2048
|
-
if (params.length !== mappings.length) {
|
|
2049
|
-
throw new Error(
|
|
2050
|
-
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
2051
|
-
);
|
|
2052
1752
|
}
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
if (!Number.isInteger(index) || index < 1) {
|
|
2056
|
-
throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
|
|
1753
|
+
if (target === null) {
|
|
1754
|
+
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
2057
1755
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
const expected = mappingsLength + 1;
|
|
2061
|
-
if (nextIndex !== expected) {
|
|
2062
|
-
throw new Error(
|
|
2063
|
-
`CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
|
|
2064
|
-
);
|
|
1756
|
+
if (isEmptyArray(target)) {
|
|
1757
|
+
return arrayIsNotEmpty(expr, dialect);
|
|
2065
1758
|
}
|
|
1759
|
+
const placeholder = buildArrayParam(target, params, dialect);
|
|
1760
|
+
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
2066
1761
|
}
|
|
2067
|
-
function
|
|
2068
|
-
if (
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
1762
|
+
function handleArrayHas(expr, val, params, cast, dialect) {
|
|
1763
|
+
if (val === void 0) return "";
|
|
1764
|
+
if (val === null) {
|
|
1765
|
+
throw createError(`has requires scalar value`, {
|
|
1766
|
+
operator: Ops.HAS,
|
|
1767
|
+
value: val
|
|
1768
|
+
});
|
|
2072
1769
|
}
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
throw new Error(
|
|
2079
|
-
`CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
|
|
2080
|
-
);
|
|
1770
|
+
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1771
|
+
throw createError(`has requires scalar value (single element), not array`, {
|
|
1772
|
+
operator: Ops.HAS,
|
|
1773
|
+
value: val
|
|
1774
|
+
});
|
|
2081
1775
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
1776
|
+
if (isPlainObject(val)) {
|
|
1777
|
+
throw createError(`has requires scalar value`, {
|
|
1778
|
+
operator: Ops.HAS,
|
|
1779
|
+
value: val
|
|
1780
|
+
});
|
|
2087
1781
|
}
|
|
2088
|
-
|
|
1782
|
+
const placeholder = params.addAuto(val);
|
|
1783
|
+
return arrayContains(expr, placeholder, cast, dialect);
|
|
2089
1784
|
}
|
|
2090
|
-
function
|
|
2091
|
-
if (
|
|
2092
|
-
|
|
1785
|
+
function handleArrayHasSome(expr, val, params, cast, dialect) {
|
|
1786
|
+
if (val === void 0) return "";
|
|
1787
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1788
|
+
const placeholder2 = params.addAuto(val);
|
|
1789
|
+
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
2093
1790
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
if (typeof m.dynamicName === "string") {
|
|
2100
|
-
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2101
|
-
assertUniqueDynamicName(dn, seenDynamic);
|
|
1791
|
+
if (!Array.isArray(val)) {
|
|
1792
|
+
throw createError(`hasSome requires array value`, {
|
|
1793
|
+
operator: Ops.HAS_SOME,
|
|
1794
|
+
value: val
|
|
1795
|
+
});
|
|
2102
1796
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
1797
|
+
if (val.length > LIMITS.MAX_ARRAY_SIZE) {
|
|
1798
|
+
throw createError(
|
|
1799
|
+
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1800
|
+
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1801
|
+
);
|
|
2108
1802
|
}
|
|
1803
|
+
if (val.length === 0) return "0=1";
|
|
1804
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1805
|
+
const placeholder = params.add(paramValue);
|
|
1806
|
+
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
2109
1807
|
}
|
|
2110
|
-
function
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
validateMappings(mappings);
|
|
2115
|
-
assertNextIndexMatches(mappings.length, index);
|
|
1808
|
+
function handleArrayHasEvery(expr, val, params, cast, dialect) {
|
|
1809
|
+
if (val === void 0) return "";
|
|
1810
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1811
|
+
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
2116
1812
|
}
|
|
2117
|
-
function
|
|
2118
|
-
if (
|
|
2119
|
-
|
|
1813
|
+
function handleArrayIsEmpty(expr, val, dialect) {
|
|
1814
|
+
if (typeof val !== "boolean") {
|
|
1815
|
+
throw createError(`isEmpty requires boolean value`, {
|
|
1816
|
+
operator: Ops.IS_EMPTY,
|
|
1817
|
+
value: val
|
|
1818
|
+
});
|
|
2120
1819
|
}
|
|
2121
|
-
return
|
|
1820
|
+
return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
|
|
2122
1821
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
1822
|
+
|
|
1823
|
+
// src/builder/where/operators-json.ts
|
|
1824
|
+
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1825
|
+
function validateJsonPathSegments(segments) {
|
|
1826
|
+
for (const segment of segments) {
|
|
1827
|
+
if (typeof segment !== "string") {
|
|
1828
|
+
throw createError("JSON path segments must be strings", {
|
|
1829
|
+
operator: Ops.PATH,
|
|
1830
|
+
value: segment
|
|
1831
|
+
});
|
|
2131
1832
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
1833
|
+
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1834
|
+
throw createError(
|
|
1835
|
+
`Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
|
|
1836
|
+
{ operator: Ops.PATH, value: segment }
|
|
2137
1837
|
);
|
|
2138
1838
|
}
|
|
2139
1839
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
return dn;
|
|
1840
|
+
}
|
|
1841
|
+
function buildJsonOperator(expr, op, val, params, dialect) {
|
|
1842
|
+
if (val === void 0) return "";
|
|
1843
|
+
if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
|
|
1844
|
+
return handleJsonPath(expr, val, params, dialect);
|
|
2146
1845
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
1846
|
+
const jsonWildcards = {
|
|
1847
|
+
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1848
|
+
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1849
|
+
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1850
|
+
};
|
|
1851
|
+
if (op in jsonWildcards) {
|
|
1852
|
+
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
2149
1853
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
}
|
|
2156
|
-
const position = index;
|
|
2157
|
-
dynamicNameToIndex.set(dn, position);
|
|
2158
|
-
params.push(void 0);
|
|
2159
|
-
mappings.push({ index: position, dynamicName: dn });
|
|
2160
|
-
index++;
|
|
2161
|
-
return format(position);
|
|
1854
|
+
throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
|
|
1855
|
+
}
|
|
1856
|
+
function handleJsonPath(expr, val, params, dialect) {
|
|
1857
|
+
const v = val;
|
|
1858
|
+
if (!Array.isArray(v.path)) {
|
|
1859
|
+
throw createError("JSON path must be an array", { operator: Ops.PATH });
|
|
2162
1860
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
const normalizedValue = normalizeValue(value);
|
|
2166
|
-
params.push(normalizedValue);
|
|
2167
|
-
mappings.push({ index: position, value: normalizedValue });
|
|
2168
|
-
index++;
|
|
2169
|
-
return format(position);
|
|
1861
|
+
if (v.path.length === 0) {
|
|
1862
|
+
throw createError("JSON path cannot be empty", { operator: Ops.PATH });
|
|
2170
1863
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
1864
|
+
validateJsonPathSegments(v.path);
|
|
1865
|
+
const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
|
|
1866
|
+
const rawOps = [
|
|
1867
|
+
["=", v.equals],
|
|
1868
|
+
[">", v.gt],
|
|
1869
|
+
[">=", v.gte],
|
|
1870
|
+
["<", v.lt],
|
|
1871
|
+
["<=", v.lte]
|
|
1872
|
+
];
|
|
1873
|
+
const ops = rawOps.filter(
|
|
1874
|
+
([, value]) => value !== void 0
|
|
1875
|
+
);
|
|
1876
|
+
if (ops.length === 0) {
|
|
1877
|
+
throw createError("JSON path query missing comparison operator", {
|
|
1878
|
+
operator: Ops.PATH
|
|
1879
|
+
});
|
|
2174
1880
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
1881
|
+
const parts = [];
|
|
1882
|
+
for (const [sqlOp, value] of ops) {
|
|
1883
|
+
if (value === null) {
|
|
1884
|
+
const base2 = jsonExtractText(expr, pathExpr, dialect);
|
|
1885
|
+
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1886
|
+
continue;
|
|
2179
1887
|
}
|
|
2180
|
-
|
|
1888
|
+
const valPh = params.add(value);
|
|
1889
|
+
const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
|
|
1890
|
+
parts.push(`${base} ${sqlOp} ${valPh}`);
|
|
2181
1891
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
1892
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1893
|
+
}
|
|
1894
|
+
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1895
|
+
if (!isNotNullish(val)) {
|
|
1896
|
+
throw createError(`JSON string operator requires non-null value`, {
|
|
1897
|
+
operator: op,
|
|
1898
|
+
value: val
|
|
2187
1899
|
});
|
|
2188
1900
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
return index;
|
|
2195
|
-
}
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
function createParamStore(startIndex = 1) {
|
|
2199
|
-
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2200
|
-
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
1901
|
+
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1902
|
+
throw createError(`JSON string operator requires scalar value`, {
|
|
1903
|
+
operator: op,
|
|
1904
|
+
value: val
|
|
1905
|
+
});
|
|
2201
1906
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
1907
|
+
const strVal = String(val);
|
|
1908
|
+
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1909
|
+
throw createError(
|
|
1910
|
+
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1911
|
+
{ operator: op }
|
|
2205
1912
|
);
|
|
2206
1913
|
}
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
validateState([...existingParams], [...existingMappings], nextIndex);
|
|
2211
|
-
return createStoreInternal(
|
|
2212
|
-
nextIndex,
|
|
2213
|
-
[...existingParams],
|
|
2214
|
-
[...existingMappings]
|
|
2215
|
-
);
|
|
1914
|
+
const placeholder = params.add(wildcards[op](strVal));
|
|
1915
|
+
const jsonText = jsonToText(expr, dialect);
|
|
1916
|
+
return caseInsensitiveLike(jsonText, placeholder, dialect);
|
|
2216
1917
|
}
|
|
2217
1918
|
|
|
2218
|
-
// src/builder/
|
|
2219
|
-
|
|
2220
|
-
|
|
1919
|
+
// src/builder/where/relations.ts
|
|
1920
|
+
var NO_JOINS = Object.freeze([]);
|
|
1921
|
+
function isListRelation(fieldType) {
|
|
1922
|
+
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1923
|
+
}
|
|
1924
|
+
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
1925
|
+
const isLocal = field.isForeignKeyLocal === true;
|
|
1926
|
+
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1927
|
+
if (isLocal) {
|
|
1928
|
+
if (fkFields.length === 0) {
|
|
1929
|
+
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1930
|
+
field: field.name
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
const parts = fkFields.map((fk) => {
|
|
1934
|
+
const safe = fk.replace(/"/g, '""');
|
|
1935
|
+
const expr = `${parentAlias}."${safe}"`;
|
|
1936
|
+
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1937
|
+
});
|
|
1938
|
+
if (parts.length === 1) return parts[0];
|
|
1939
|
+
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
1940
|
+
}
|
|
1941
|
+
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
|
|
1942
|
+
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
1943
|
+
}
|
|
1944
|
+
function buildToOneExistsMatch(relTable, relAlias, join3, sub) {
|
|
1945
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1946
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1947
|
+
}
|
|
1948
|
+
function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
|
|
1949
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1950
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1951
|
+
}
|
|
1952
|
+
function buildListRelationFilters(args) {
|
|
1953
|
+
const {
|
|
1954
|
+
fieldName,
|
|
1955
|
+
value,
|
|
1956
|
+
ctx,
|
|
1957
|
+
whereBuilder,
|
|
1958
|
+
relModel,
|
|
1959
|
+
relTable,
|
|
1960
|
+
relAlias,
|
|
1961
|
+
join: join3
|
|
1962
|
+
} = args;
|
|
1963
|
+
const noneValue = value[RelationFilters.NONE];
|
|
1964
|
+
if (noneValue !== void 0 && noneValue !== null) {
|
|
1965
|
+
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1966
|
+
alias: relAlias,
|
|
1967
|
+
model: relModel,
|
|
1968
|
+
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1969
|
+
isSubquery: true,
|
|
1970
|
+
depth: ctx.depth + 1
|
|
1971
|
+
}));
|
|
1972
|
+
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1973
|
+
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1974
|
+
if (canOptimize) {
|
|
1975
|
+
const checkField = relModel.fields.find(
|
|
1976
|
+
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1977
|
+
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1978
|
+
if (checkField) {
|
|
1979
|
+
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
1980
|
+
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1981
|
+
return Object.freeze({
|
|
1982
|
+
clause: whereClause,
|
|
1983
|
+
joins: [leftJoinSql]
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
const filters = [
|
|
1989
|
+
{
|
|
1990
|
+
key: RelationFilters.SOME,
|
|
1991
|
+
wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${c})`
|
|
1992
|
+
},
|
|
1993
|
+
{
|
|
1994
|
+
key: RelationFilters.EVERY,
|
|
1995
|
+
wrap: (c, j) => `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${SQL_TEMPLATES.NOT} (${c}))`
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
key: RelationFilters.NONE,
|
|
1999
|
+
wrap: (c, j) => {
|
|
2000
|
+
const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
|
|
2001
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3}${condition})`;
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
];
|
|
2005
|
+
const clauses = [];
|
|
2006
|
+
for (const { key, wrap } of filters) {
|
|
2007
|
+
const raw = value[key];
|
|
2008
|
+
if (raw === void 0 || raw === null) continue;
|
|
2009
|
+
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2010
|
+
alias: relAlias,
|
|
2011
|
+
model: relModel,
|
|
2012
|
+
path: [...ctx.path, fieldName, key],
|
|
2013
|
+
isSubquery: true,
|
|
2014
|
+
depth: ctx.depth + 1
|
|
2015
|
+
}));
|
|
2016
|
+
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2017
|
+
clauses.push(wrap(sub.clause, j));
|
|
2018
|
+
}
|
|
2019
|
+
if (clauses.length === 0) {
|
|
2020
|
+
throw createError(
|
|
2021
|
+
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
2022
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2221
2025
|
return Object.freeze({
|
|
2222
|
-
clause:
|
|
2223
|
-
joins:
|
|
2224
|
-
params: snapshot.params,
|
|
2225
|
-
paramMappings: snapshot.mappings,
|
|
2226
|
-
nextParamIndex: snapshot.index
|
|
2026
|
+
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
2027
|
+
joins: NO_JOINS
|
|
2227
2028
|
});
|
|
2228
2029
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
const result = whereBuilderInstance.build(where, ctx);
|
|
2247
|
-
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2248
|
-
if (!options.isSubquery) {
|
|
2249
|
-
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2250
|
-
(m) => parseInt(m[1], 10)
|
|
2030
|
+
function buildToOneRelationFilters(args) {
|
|
2031
|
+
const {
|
|
2032
|
+
fieldName,
|
|
2033
|
+
value,
|
|
2034
|
+
ctx,
|
|
2035
|
+
whereBuilder,
|
|
2036
|
+
field,
|
|
2037
|
+
relModel,
|
|
2038
|
+
relTable,
|
|
2039
|
+
relAlias,
|
|
2040
|
+
join: join3
|
|
2041
|
+
} = args;
|
|
2042
|
+
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
2043
|
+
if (hasSomeEveryNone) {
|
|
2044
|
+
throw createError(
|
|
2045
|
+
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
2046
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
2251
2047
|
);
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2048
|
+
}
|
|
2049
|
+
const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
|
|
2050
|
+
const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
|
|
2051
|
+
let filterKey;
|
|
2052
|
+
let filterVal;
|
|
2053
|
+
if (hasIs) {
|
|
2054
|
+
filterKey = "is";
|
|
2055
|
+
filterVal = value.is;
|
|
2056
|
+
} else if (hasIsNot) {
|
|
2057
|
+
filterKey = "isNot";
|
|
2058
|
+
filterVal = value.isNot;
|
|
2059
|
+
} else {
|
|
2060
|
+
filterKey = "is";
|
|
2061
|
+
filterVal = value;
|
|
2062
|
+
}
|
|
2063
|
+
if (filterVal === void 0) {
|
|
2064
|
+
return Object.freeze({
|
|
2065
|
+
clause: DEFAULT_WHERE_CLAUSE,
|
|
2066
|
+
joins: NO_JOINS
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
if (filterVal === null) {
|
|
2070
|
+
const wantNull = filterKey === "is";
|
|
2071
|
+
const clause2 = buildToOneNullCheck(
|
|
2072
|
+
field,
|
|
2073
|
+
ctx.alias,
|
|
2074
|
+
relTable,
|
|
2075
|
+
relAlias,
|
|
2076
|
+
join3,
|
|
2077
|
+
wantNull
|
|
2078
|
+
);
|
|
2079
|
+
return Object.freeze({
|
|
2080
|
+
clause: clause2,
|
|
2081
|
+
joins: NO_JOINS
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
if (!isPlainObject(filterVal)) {
|
|
2085
|
+
throw createError(
|
|
2086
|
+
`Relation '${fieldName}' filter must be an object or null`,
|
|
2087
|
+
{
|
|
2088
|
+
field: fieldName,
|
|
2089
|
+
path: ctx.path,
|
|
2090
|
+
modelName: ctx.model.name,
|
|
2091
|
+
value: filterVal
|
|
2092
|
+
}
|
|
2093
|
+
);
|
|
2094
|
+
}
|
|
2095
|
+
const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
|
|
2096
|
+
alias: relAlias,
|
|
2097
|
+
model: relModel,
|
|
2098
|
+
path: [...ctx.path, fieldName, filterKey],
|
|
2099
|
+
isSubquery: true,
|
|
2100
|
+
depth: ctx.depth + 1
|
|
2101
|
+
}));
|
|
2102
|
+
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
|
|
2103
|
+
return Object.freeze({
|
|
2104
|
+
clause,
|
|
2105
|
+
joins: NO_JOINS
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
function ensureRelationFilterObject(fieldName, value, ctx) {
|
|
2109
|
+
if (!isPlainObject(value)) {
|
|
2110
|
+
throw createError(`Relation filter '${fieldName}' must be an object`, {
|
|
2111
|
+
path: [...ctx.path, fieldName],
|
|
2112
|
+
field: fieldName,
|
|
2113
|
+
modelName: ctx.model.name,
|
|
2114
|
+
value
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function buildRelation(fieldName, value, ctx, whereBuilder) {
|
|
2119
|
+
const field = ctx.model.fields.find((f) => f.name === fieldName);
|
|
2120
|
+
if (!isValidRelationField(field)) {
|
|
2121
|
+
throw createError(`Invalid relation '${fieldName}'`, {
|
|
2122
|
+
field: fieldName,
|
|
2123
|
+
path: ctx.path,
|
|
2124
|
+
modelName: ctx.model.name
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
|
|
2128
|
+
if (!isNotNullish(relModel)) {
|
|
2129
|
+
throw createError(
|
|
2130
|
+
`Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
|
|
2131
|
+
{
|
|
2132
|
+
field: fieldName,
|
|
2133
|
+
path: ctx.path,
|
|
2134
|
+
modelName: ctx.model.name
|
|
2261
2135
|
}
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
const relTable = buildTableReference(
|
|
2139
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
2140
|
+
relModel.tableName,
|
|
2141
|
+
ctx.dialect
|
|
2142
|
+
);
|
|
2143
|
+
const relAlias = ctx.aliasGen.next(fieldName);
|
|
2144
|
+
const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
2145
|
+
const args = {
|
|
2146
|
+
fieldName,
|
|
2147
|
+
value,
|
|
2148
|
+
ctx,
|
|
2149
|
+
whereBuilder,
|
|
2150
|
+
field,
|
|
2151
|
+
relModel,
|
|
2152
|
+
relTable,
|
|
2153
|
+
relAlias,
|
|
2154
|
+
join: join3
|
|
2155
|
+
};
|
|
2156
|
+
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
2157
|
+
return buildToOneRelationFilters(args);
|
|
2158
|
+
}
|
|
2159
|
+
function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
|
|
2160
|
+
ensureRelationFilterObject(fieldName, value, ctx);
|
|
2161
|
+
return buildRelation(fieldName, value, ctx, whereBuilder);
|
|
2162
|
+
}
|
|
2163
|
+
function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
2164
|
+
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// src/builder/shared/validators/field-validators.ts
|
|
2168
|
+
function assertFieldExists(name, model, path) {
|
|
2169
|
+
const field = model.fields.find((f) => f.name === name);
|
|
2170
|
+
if (!isNotNullish(field)) {
|
|
2171
|
+
throw createError(`Field '${name}' does not exist on '${model.name}'`, {
|
|
2172
|
+
field: name,
|
|
2173
|
+
path,
|
|
2174
|
+
modelName: model.name,
|
|
2175
|
+
availableFields: model.fields.map((f) => f.name)
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
return field;
|
|
2179
|
+
}
|
|
2180
|
+
function assertValidOperator(fieldName, op, fieldType, path, modelName) {
|
|
2181
|
+
if (!isNotNullish(fieldType)) return;
|
|
2182
|
+
const ARRAY_OPS = /* @__PURE__ */ new Set([
|
|
2183
|
+
Ops.HAS,
|
|
2184
|
+
Ops.HAS_SOME,
|
|
2185
|
+
Ops.HAS_EVERY,
|
|
2186
|
+
Ops.IS_EMPTY
|
|
2187
|
+
]);
|
|
2188
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2189
|
+
Ops.PATH,
|
|
2190
|
+
Ops.STRING_CONTAINS,
|
|
2191
|
+
Ops.STRING_STARTS_WITH,
|
|
2192
|
+
Ops.STRING_ENDS_WITH
|
|
2193
|
+
]);
|
|
2194
|
+
const isArrayOp = ARRAY_OPS.has(op);
|
|
2195
|
+
const isFieldArray = isArrayType(fieldType);
|
|
2196
|
+
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
2197
|
+
if (arrayOpMismatch) {
|
|
2198
|
+
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
2199
|
+
operator: op,
|
|
2200
|
+
field: fieldName,
|
|
2201
|
+
path,
|
|
2202
|
+
modelName
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
const isJsonOp = JSON_OPS.has(op);
|
|
2206
|
+
const isFieldJson = isJsonType(fieldType);
|
|
2207
|
+
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
2208
|
+
if (jsonOpMismatch) {
|
|
2209
|
+
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
2210
|
+
operator: op,
|
|
2211
|
+
field: fieldName,
|
|
2212
|
+
path,
|
|
2213
|
+
modelName
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// src/builder/where/builder.ts
|
|
2219
|
+
var WhereBuilder = class {
|
|
2220
|
+
build(where, ctx) {
|
|
2221
|
+
if (!isPlainObject(where)) {
|
|
2222
|
+
throw createError("where must be an object", {
|
|
2223
|
+
path: ctx.path,
|
|
2224
|
+
modelName: ctx.model.name
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
return buildWhereInternal(where, ctx, this);
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
var MAX_QUERY_DEPTH = 50;
|
|
2231
|
+
var EMPTY_JOINS = Object.freeze([]);
|
|
2232
|
+
var whereBuilderInstance = new WhereBuilder();
|
|
2233
|
+
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
2234
|
+
return Object.freeze({ clause, joins });
|
|
2235
|
+
}
|
|
2236
|
+
function dedupePreserveOrder(items) {
|
|
2237
|
+
if (items.length <= 1) return Object.freeze([...items]);
|
|
2238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2239
|
+
const out = [];
|
|
2240
|
+
for (const s of items) {
|
|
2241
|
+
if (!seen.has(s)) {
|
|
2242
|
+
seen.add(s);
|
|
2243
|
+
out.push(s);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
return Object.freeze(out);
|
|
2247
|
+
}
|
|
2248
|
+
function appendResult(result, clauses, allJoins) {
|
|
2249
|
+
if (isValidWhereClause(result.clause)) clauses.push(result.clause);
|
|
2250
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2251
|
+
}
|
|
2252
|
+
function asLogicalOperator(key) {
|
|
2253
|
+
if (key === LogicalOps.AND) return "AND";
|
|
2254
|
+
if (key === LogicalOps.OR) return "OR";
|
|
2255
|
+
if (key === LogicalOps.NOT) return "NOT";
|
|
2256
|
+
return null;
|
|
2257
|
+
}
|
|
2258
|
+
function nextContext(ctx) {
|
|
2259
|
+
return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
|
|
2260
|
+
}
|
|
2261
|
+
function buildRelationFilter(fieldName, value, ctx, builder) {
|
|
2262
|
+
const ctx2 = nextContext(ctx);
|
|
2263
|
+
if (ctx.isSubquery) {
|
|
2264
|
+
return buildNestedRelation(fieldName, value, ctx2, builder);
|
|
2265
|
+
}
|
|
2266
|
+
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
2267
|
+
}
|
|
2268
|
+
function buildWhereEntry(key, value, ctx, builder) {
|
|
2269
|
+
const op = asLogicalOperator(key);
|
|
2270
|
+
if (op) return buildLogical(op, value, ctx, builder);
|
|
2271
|
+
if (isRelationField(key, ctx.model)) {
|
|
2272
|
+
if (!isPlainObject(value)) {
|
|
2273
|
+
throw createError(`Relation filter '${key}' must be an object`, {
|
|
2274
|
+
path: [...ctx.path, key],
|
|
2275
|
+
field: key,
|
|
2276
|
+
modelName: ctx.model.name,
|
|
2277
|
+
value
|
|
2278
|
+
});
|
|
2262
2279
|
}
|
|
2280
|
+
return buildRelationFilter(key, value, ctx, builder);
|
|
2263
2281
|
}
|
|
2264
|
-
return
|
|
2282
|
+
return buildScalarField(key, value, ctx);
|
|
2265
2283
|
}
|
|
2266
|
-
function
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
}
|
|
2273
|
-
const min = (_a = opts.min) != null ? _a : 0;
|
|
2274
|
-
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
2275
|
-
if (!allowZero && v === 0) {
|
|
2276
|
-
throw new Error(`${name} must be > 0`);
|
|
2277
|
-
}
|
|
2278
|
-
if (v < min) {
|
|
2279
|
-
throw new Error(`${name} must be >= ${min}`);
|
|
2284
|
+
function buildWhereInternal(where, ctx, builder) {
|
|
2285
|
+
if (ctx.depth > MAX_QUERY_DEPTH) {
|
|
2286
|
+
throw createError(
|
|
2287
|
+
`Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
|
|
2288
|
+
{ path: ctx.path, modelName: ctx.model.name }
|
|
2289
|
+
);
|
|
2280
2290
|
}
|
|
2281
|
-
if (
|
|
2282
|
-
|
|
2291
|
+
if (isEmptyWhere(where)) {
|
|
2292
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2283
2293
|
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
return `${s}:${dn}`;
|
|
2291
|
-
}
|
|
2292
|
-
function addAutoScoped(params, value, scope) {
|
|
2293
|
-
if (schemaParser.isDynamicParameter(value)) {
|
|
2294
|
-
const dn = schemaParser.extractDynamicName(value);
|
|
2295
|
-
return params.add(void 0, scopeName(scope, dn));
|
|
2294
|
+
const allJoins = [];
|
|
2295
|
+
const clauses = [];
|
|
2296
|
+
for (const [key, value] of Object.entries(where)) {
|
|
2297
|
+
if (value === void 0) continue;
|
|
2298
|
+
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2299
|
+
appendResult(result, clauses, allJoins);
|
|
2296
2300
|
}
|
|
2297
|
-
|
|
2301
|
+
const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2302
|
+
return freezeResult(finalClause, dedupePreserveOrder(allJoins));
|
|
2298
2303
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
if (typeof sortRaw !== "string") return sortRaw;
|
|
2316
|
-
const s = sortRaw.toLowerCase();
|
|
2317
|
-
if (s === "asc") return "desc";
|
|
2318
|
-
if (s === "desc") return "asc";
|
|
2319
|
-
return sortRaw;
|
|
2320
|
-
};
|
|
2321
|
-
var flipObjectSort = (obj) => {
|
|
2322
|
-
const sortRaw = obj.sort;
|
|
2323
|
-
const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
|
|
2324
|
-
const nullsRaw = obj.nulls;
|
|
2325
|
-
if (typeof nullsRaw === "string") {
|
|
2326
|
-
out.nulls = flipNulls(nullsRaw);
|
|
2327
|
-
}
|
|
2328
|
-
return out;
|
|
2329
|
-
};
|
|
2330
|
-
var flipValue = (v) => {
|
|
2331
|
-
if (typeof v === "string") return flipSortString(v);
|
|
2332
|
-
if (isPlainObject(v)) return flipObjectSort(v);
|
|
2333
|
-
return v;
|
|
2334
|
-
};
|
|
2335
|
-
var assertSingleFieldObject = (item) => {
|
|
2336
|
-
if (!isPlainObject(item)) {
|
|
2337
|
-
throw new Error("orderBy array entries must be objects");
|
|
2304
|
+
function normalizeLogicalValue(operator, value, ctx) {
|
|
2305
|
+
if (Array.isArray(value)) {
|
|
2306
|
+
const out = [];
|
|
2307
|
+
for (let i = 0; i < value.length; i++) {
|
|
2308
|
+
const v = value[i];
|
|
2309
|
+
if (v === void 0) continue;
|
|
2310
|
+
if (!isPlainObject(v)) {
|
|
2311
|
+
throw createError(`${operator} entries must be objects`, {
|
|
2312
|
+
path: [...ctx.path, operator, String(i)],
|
|
2313
|
+
modelName: ctx.model.name,
|
|
2314
|
+
value: v
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
out.push(v);
|
|
2318
|
+
}
|
|
2319
|
+
return out;
|
|
2338
2320
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
throw new Error("orderBy array entries must have exactly one field");
|
|
2321
|
+
if (isPlainObject(value)) {
|
|
2322
|
+
return [value];
|
|
2342
2323
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
const [k, v] = assertSingleFieldObject(item);
|
|
2348
|
-
return { [k]: flipValue(v) };
|
|
2324
|
+
throw createError(`${operator} must be an object or array of objects`, {
|
|
2325
|
+
path: [...ctx.path, operator],
|
|
2326
|
+
modelName: ctx.model.name,
|
|
2327
|
+
value
|
|
2349
2328
|
});
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2329
|
+
}
|
|
2330
|
+
function collectLogicalParts(operator, conditions, ctx, builder) {
|
|
2331
|
+
const allJoins = [];
|
|
2332
|
+
const clauses = [];
|
|
2333
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
2334
|
+
const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
|
|
2335
|
+
path: [...ctx.path, operator, String(i)],
|
|
2336
|
+
depth: ctx.depth + 1
|
|
2337
|
+
}));
|
|
2338
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2339
|
+
if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
|
|
2340
|
+
clauses.push(`(${result.clause})`);
|
|
2341
|
+
}
|
|
2355
2342
|
}
|
|
2356
|
-
return
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2343
|
+
return {
|
|
2344
|
+
joins: dedupePreserveOrder(allJoins),
|
|
2345
|
+
clauses: Object.freeze(clauses)
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
function buildLogicalClause(operator, clauses) {
|
|
2349
|
+
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
2350
|
+
if (operator === "NOT") {
|
|
2351
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
2352
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
2362
2353
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2354
|
+
return clauses.join(` ${operator} `);
|
|
2355
|
+
}
|
|
2356
|
+
function buildLogical(operator, value, ctx, builder) {
|
|
2357
|
+
const conditions = normalizeLogicalValue(operator, value, ctx);
|
|
2358
|
+
if (conditions.length === 0) {
|
|
2359
|
+
const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
|
|
2360
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2365
2361
|
}
|
|
2366
|
-
|
|
2362
|
+
const { joins, clauses } = collectLogicalParts(
|
|
2363
|
+
operator,
|
|
2364
|
+
conditions,
|
|
2365
|
+
ctx,
|
|
2366
|
+
builder
|
|
2367
|
+
);
|
|
2368
|
+
const clause = buildLogicalClause(operator, clauses);
|
|
2369
|
+
return freezeResult(clause, joins);
|
|
2367
2370
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
};
|
|
2374
|
-
});
|
|
2375
|
-
};
|
|
2376
|
-
function normalizeOrderByInput(orderBy, parseValue) {
|
|
2377
|
-
if (!isNotNullish(orderBy)) return [];
|
|
2378
|
-
if (Array.isArray(orderBy)) {
|
|
2379
|
-
const pairs = orderBy.map(assertSingleFieldObject);
|
|
2380
|
-
return normalizePairs(pairs, parseValue);
|
|
2371
|
+
function buildScalarField(fieldName, value, ctx) {
|
|
2372
|
+
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
2373
|
+
const expr = col(ctx.alias, fieldName, ctx.model);
|
|
2374
|
+
if (value === null) {
|
|
2375
|
+
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
2381
2376
|
}
|
|
2382
|
-
if (isPlainObject(
|
|
2383
|
-
|
|
2377
|
+
if (isPlainObject(value)) {
|
|
2378
|
+
const mode = value.mode;
|
|
2379
|
+
const ops = Object.entries(value).filter(
|
|
2380
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
2381
|
+
);
|
|
2382
|
+
if (ops.length === 0) {
|
|
2383
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2384
|
+
}
|
|
2385
|
+
const parts = [];
|
|
2386
|
+
for (const [op, val] of ops) {
|
|
2387
|
+
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
2388
|
+
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
2389
|
+
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
2390
|
+
}
|
|
2391
|
+
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2392
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2384
2393
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
}
|
|
2395
|
-
function parseNullsRaw(raw, errorLabel) {
|
|
2396
|
-
if (!isNotNullish(raw)) return void 0;
|
|
2397
|
-
const s = String(raw).toLowerCase();
|
|
2398
|
-
if (s === "first" || s === "last") return s;
|
|
2399
|
-
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
2394
|
+
const clause = buildOperator(
|
|
2395
|
+
expr,
|
|
2396
|
+
Ops.EQUALS,
|
|
2397
|
+
value,
|
|
2398
|
+
ctx,
|
|
2399
|
+
void 0,
|
|
2400
|
+
field.type
|
|
2401
|
+
);
|
|
2402
|
+
return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2400
2403
|
}
|
|
2401
|
-
function
|
|
2402
|
-
if (
|
|
2403
|
-
|
|
2404
|
+
function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
2405
|
+
if (fieldType && isArrayType(fieldType)) {
|
|
2406
|
+
return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
|
|
2404
2407
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
);
|
|
2408
|
+
if (fieldType && isJsonType(fieldType)) {
|
|
2409
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2410
|
+
Ops.PATH,
|
|
2411
|
+
Ops.STRING_CONTAINS,
|
|
2412
|
+
Ops.STRING_STARTS_WITH,
|
|
2413
|
+
Ops.STRING_ENDS_WITH
|
|
2414
|
+
]);
|
|
2415
|
+
if (JSON_OPS.has(op)) {
|
|
2416
|
+
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2414
2417
|
}
|
|
2415
2418
|
}
|
|
2419
|
+
return buildScalarOperator(
|
|
2420
|
+
expr,
|
|
2421
|
+
op,
|
|
2422
|
+
val,
|
|
2423
|
+
ctx.params,
|
|
2424
|
+
mode,
|
|
2425
|
+
fieldType,
|
|
2426
|
+
ctx.dialect
|
|
2427
|
+
);
|
|
2416
2428
|
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
const
|
|
2423
|
-
const
|
|
2424
|
-
const
|
|
2425
|
-
|
|
2426
|
-
return {
|
|
2429
|
+
|
|
2430
|
+
// src/builder/shared/alias-generator.ts
|
|
2431
|
+
function toSafeSqlIdentifier(input) {
|
|
2432
|
+
const raw = String(input);
|
|
2433
|
+
const cleaned = raw.replace(/\W/g, "_");
|
|
2434
|
+
const startsOk = /^[a-zA-Z_]/.test(cleaned);
|
|
2435
|
+
const base = startsOk ? cleaned : `_${cleaned}`;
|
|
2436
|
+
const fallback = base.length > 0 ? base : "_t";
|
|
2437
|
+
const lowered = fallback.toLowerCase();
|
|
2438
|
+
return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
2427
2439
|
}
|
|
2428
|
-
function
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2440
|
+
function createAliasGenerator(maxAliases = 1e4) {
|
|
2441
|
+
let counter = 0;
|
|
2442
|
+
const usedAliases = /* @__PURE__ */ new Set();
|
|
2443
|
+
return {
|
|
2444
|
+
next(baseName) {
|
|
2445
|
+
if (usedAliases.size >= maxAliases) {
|
|
2446
|
+
throw new Error(
|
|
2447
|
+
`Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
|
|
2448
|
+
);
|
|
2449
|
+
}
|
|
2450
|
+
const base = toSafeSqlIdentifier(baseName);
|
|
2451
|
+
const suffix = `_${counter}`;
|
|
2452
|
+
const maxLen = 63;
|
|
2453
|
+
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2454
|
+
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2455
|
+
const alias = `${trimmedBase}${suffix}`;
|
|
2456
|
+
counter += 1;
|
|
2457
|
+
if (usedAliases.has(alias)) {
|
|
2458
|
+
throw new Error(
|
|
2459
|
+
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
usedAliases.add(alias);
|
|
2463
|
+
return alias;
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2433
2466
|
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
if (n > MAX_LIMIT_OFFSET) {
|
|
2441
|
-
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
2467
|
+
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
2468
|
+
function assertSameLength(params, mappings) {
|
|
2469
|
+
if (params.length !== mappings.length) {
|
|
2470
|
+
throw new Error(
|
|
2471
|
+
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
2472
|
+
);
|
|
2442
2473
|
}
|
|
2443
|
-
return n;
|
|
2444
|
-
}
|
|
2445
|
-
function hasNonNullishProp(v, key) {
|
|
2446
|
-
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
2447
|
-
}
|
|
2448
|
-
function normalizeIntegerOrDynamic(name, v) {
|
|
2449
|
-
if (schemaParser.isDynamicParameter(v)) return v;
|
|
2450
|
-
return normalizeFiniteInteger(name, v);
|
|
2451
2474
|
}
|
|
2452
|
-
function
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
if (!hasSkip && !hasTake) {
|
|
2456
|
-
return {
|
|
2457
|
-
hasSkip: false,
|
|
2458
|
-
hasTake: false,
|
|
2459
|
-
skipVal: void 0,
|
|
2460
|
-
takeVal: void 0
|
|
2461
|
-
};
|
|
2475
|
+
function assertValidNextIndex(index) {
|
|
2476
|
+
if (!Number.isInteger(index) || index < 1) {
|
|
2477
|
+
throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
|
|
2462
2478
|
}
|
|
2463
|
-
const obj = relArgs;
|
|
2464
|
-
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
2465
|
-
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
2466
|
-
return { hasSkip, hasTake, skipVal, takeVal };
|
|
2467
2479
|
}
|
|
2468
|
-
function
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
if (dialect === "postgres") {
|
|
2475
|
-
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
2476
|
-
out.push(`${c} ${dir}${nulls}`);
|
|
2477
|
-
continue;
|
|
2478
|
-
}
|
|
2479
|
-
if (isNotNullish(e.nulls)) {
|
|
2480
|
-
const isNullExpr = `(${c} IS NULL)`;
|
|
2481
|
-
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
2482
|
-
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
2483
|
-
out.push(`${c} ${dir}`);
|
|
2484
|
-
continue;
|
|
2485
|
-
}
|
|
2486
|
-
out.push(`${c} ${dir}`);
|
|
2480
|
+
function assertNextIndexMatches(mappingsLength, nextIndex) {
|
|
2481
|
+
const expected = mappingsLength + 1;
|
|
2482
|
+
if (nextIndex !== expected) {
|
|
2483
|
+
throw new Error(
|
|
2484
|
+
`CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
|
|
2485
|
+
);
|
|
2487
2486
|
}
|
|
2488
|
-
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
2489
2487
|
}
|
|
2490
|
-
function
|
|
2491
|
-
if (
|
|
2492
|
-
|
|
2488
|
+
function assertSequentialIndex(actual, expected) {
|
|
2489
|
+
if (actual !== expected) {
|
|
2490
|
+
throw new Error(
|
|
2491
|
+
`CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
|
|
2492
|
+
);
|
|
2493
2493
|
}
|
|
2494
|
-
return direction === "asc" ? "first" : "last";
|
|
2495
2494
|
}
|
|
2496
|
-
function
|
|
2497
|
-
const
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
existing.set(field, out[out.length - 1]);
|
|
2504
|
-
}
|
|
2495
|
+
function assertExactlyOneOfDynamicOrValue(m) {
|
|
2496
|
+
const hasDynamic = typeof m.dynamicName === "string";
|
|
2497
|
+
const hasStatic = m.value !== void 0;
|
|
2498
|
+
if (hasDynamic === hasStatic) {
|
|
2499
|
+
throw new Error(
|
|
2500
|
+
`CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
|
|
2501
|
+
);
|
|
2505
2502
|
}
|
|
2506
|
-
return out;
|
|
2507
2503
|
}
|
|
2508
|
-
function
|
|
2509
|
-
const
|
|
2510
|
-
if (
|
|
2511
|
-
throw new Error(
|
|
2504
|
+
function normalizeDynamicNameOrThrow(dynamicName, index) {
|
|
2505
|
+
const dn = dynamicName.trim();
|
|
2506
|
+
if (dn.length === 0) {
|
|
2507
|
+
throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
|
|
2512
2508
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
parts.push(`${c} IS NULL`);
|
|
2519
|
-
continue;
|
|
2520
|
-
}
|
|
2521
|
-
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
2522
|
-
placeholdersByField.set(field, ph);
|
|
2523
|
-
parts.push(`${c} = ${ph}`);
|
|
2509
|
+
return dn;
|
|
2510
|
+
}
|
|
2511
|
+
function assertUniqueDynamicName(dn, seen) {
|
|
2512
|
+
if (seen.has(dn)) {
|
|
2513
|
+
throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
|
|
2524
2514
|
}
|
|
2525
|
-
|
|
2526
|
-
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
2527
|
-
placeholdersByField
|
|
2528
|
-
};
|
|
2515
|
+
seen.add(dn);
|
|
2529
2516
|
}
|
|
2530
|
-
function
|
|
2531
|
-
|
|
2532
|
-
|
|
2517
|
+
function validateMappingEntry(m, expectedIndex, seenDynamic) {
|
|
2518
|
+
assertSequentialIndex(m.index, expectedIndex);
|
|
2519
|
+
assertExactlyOneOfDynamicOrValue(m);
|
|
2520
|
+
if (typeof m.dynamicName === "string") {
|
|
2521
|
+
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2522
|
+
assertUniqueDynamicName(dn, seenDynamic);
|
|
2523
|
+
}
|
|
2533
2524
|
}
|
|
2534
|
-
function
|
|
2535
|
-
|
|
2525
|
+
function validateMappings(mappings) {
|
|
2526
|
+
const seenDynamic = /* @__PURE__ */ new Set();
|
|
2527
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
2528
|
+
validateMappingEntry(mappings[i], i + 1, seenDynamic);
|
|
2529
|
+
}
|
|
2536
2530
|
}
|
|
2537
|
-
function
|
|
2538
|
-
|
|
2531
|
+
function validateState(params, mappings, index) {
|
|
2532
|
+
assertSameLength(params, mappings);
|
|
2533
|
+
assertValidNextIndex(index);
|
|
2534
|
+
if (mappings.length === 0) return;
|
|
2535
|
+
validateMappings(mappings);
|
|
2536
|
+
assertNextIndexMatches(mappings.length, index);
|
|
2539
2537
|
}
|
|
2540
|
-
function
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
2538
|
+
function normalizeValue(value) {
|
|
2539
|
+
if (value instanceof Date) {
|
|
2540
|
+
return value.toISOString();
|
|
2544
2541
|
}
|
|
2545
|
-
return
|
|
2542
|
+
return value;
|
|
2546
2543
|
}
|
|
2547
|
-
function
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
const existing = placeholdersByField.get(field);
|
|
2556
|
-
if (typeof existing === "string" && existing.length > 0) {
|
|
2557
|
-
parts.push(`${c} = ${existing}`);
|
|
2558
|
-
continue;
|
|
2544
|
+
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2545
|
+
let index = startIndex;
|
|
2546
|
+
const params = [...initialParams];
|
|
2547
|
+
const mappings = [...initialMappings];
|
|
2548
|
+
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2549
|
+
for (const m of initialMappings) {
|
|
2550
|
+
if (typeof m.dynamicName === "string") {
|
|
2551
|
+
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2559
2552
|
}
|
|
2560
|
-
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
2561
|
-
parts.push(`${c} = ${ph}`);
|
|
2562
2553
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
for (const item of normalized) {
|
|
2569
|
-
for (const [field, value] of Object.entries(item)) {
|
|
2570
|
-
if (typeof value === "string") {
|
|
2571
|
-
entries.push({ field, direction: value });
|
|
2572
|
-
} else {
|
|
2573
|
-
entries.push({
|
|
2574
|
-
field,
|
|
2575
|
-
direction: value.sort,
|
|
2576
|
-
nulls: value.nulls
|
|
2577
|
-
});
|
|
2578
|
-
}
|
|
2554
|
+
function assertCanAdd() {
|
|
2555
|
+
if (index > MAX_PARAM_INDEX) {
|
|
2556
|
+
throw new Error(
|
|
2557
|
+
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2558
|
+
);
|
|
2579
2559
|
}
|
|
2580
2560
|
}
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
if (cursorEntries.length === 0) {
|
|
2588
|
-
throw new Error("cursor must have at least one field");
|
|
2561
|
+
function normalizeDynamicName(dynamicName) {
|
|
2562
|
+
const dn = dynamicName.trim();
|
|
2563
|
+
if (dn.length === 0) {
|
|
2564
|
+
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2565
|
+
}
|
|
2566
|
+
return dn;
|
|
2589
2567
|
}
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
let orderEntries = buildOrderEntries(orderBy);
|
|
2593
|
-
if (orderEntries.length === 0) {
|
|
2594
|
-
orderEntries = cursorEntries.map(([field]) => ({
|
|
2595
|
-
field,
|
|
2596
|
-
direction: "asc"
|
|
2597
|
-
}));
|
|
2598
|
-
} else {
|
|
2599
|
-
orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
|
|
2568
|
+
function format(position) {
|
|
2569
|
+
return `$${position}`;
|
|
2600
2570
|
}
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
const outerCursorMatch = buildOuterCursorMatch(
|
|
2607
|
-
cursor,
|
|
2608
|
-
alias,
|
|
2609
|
-
placeholdersByField,
|
|
2610
|
-
params,
|
|
2611
|
-
model
|
|
2612
|
-
);
|
|
2613
|
-
const orClauses = [];
|
|
2614
|
-
for (let level = 0; level < orderEntries.length; level++) {
|
|
2615
|
-
const andParts = [];
|
|
2616
|
-
for (let i = 0; i < level; i++) {
|
|
2617
|
-
const e2 = orderEntries[i];
|
|
2618
|
-
const c2 = col(alias, e2.field, model);
|
|
2619
|
-
const v2 = cursorValueExpr(
|
|
2620
|
-
tableName,
|
|
2621
|
-
cursorAlias,
|
|
2622
|
-
cursorWhereSql,
|
|
2623
|
-
e2.field);
|
|
2624
|
-
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
2571
|
+
function addDynamic(dynamicName) {
|
|
2572
|
+
const dn = normalizeDynamicName(dynamicName);
|
|
2573
|
+
const existing = dynamicNameToIndex.get(dn);
|
|
2574
|
+
if (existing !== void 0) {
|
|
2575
|
+
return format(existing);
|
|
2625
2576
|
}
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
e.field);
|
|
2633
|
-
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
2634
|
-
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
2635
|
-
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
2577
|
+
const position = index;
|
|
2578
|
+
dynamicNameToIndex.set(dn, position);
|
|
2579
|
+
params.push(void 0);
|
|
2580
|
+
mappings.push({ index: position, dynamicName: dn });
|
|
2581
|
+
index++;
|
|
2582
|
+
return format(position);
|
|
2636
2583
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2584
|
+
function addStatic(value) {
|
|
2585
|
+
const position = index;
|
|
2586
|
+
const normalizedValue = normalizeValue(value);
|
|
2587
|
+
params.push(normalizedValue);
|
|
2588
|
+
mappings.push({ index: position, value: normalizedValue });
|
|
2589
|
+
index++;
|
|
2590
|
+
return format(position);
|
|
2591
|
+
}
|
|
2592
|
+
function add(value, dynamicName) {
|
|
2593
|
+
assertCanAdd();
|
|
2594
|
+
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2595
|
+
}
|
|
2596
|
+
function addAuto(value) {
|
|
2597
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
2598
|
+
const dynamicName = schemaParser.extractDynamicName(value);
|
|
2599
|
+
return add(void 0, dynamicName);
|
|
2600
|
+
}
|
|
2601
|
+
return add(value);
|
|
2602
|
+
}
|
|
2603
|
+
function snapshot() {
|
|
2604
|
+
return Object.freeze({
|
|
2605
|
+
index,
|
|
2606
|
+
params: Object.freeze([...params]),
|
|
2607
|
+
mappings: Object.freeze([...mappings])
|
|
2608
|
+
});
|
|
2609
|
+
}
|
|
2610
|
+
return {
|
|
2611
|
+
add,
|
|
2612
|
+
addAuto,
|
|
2613
|
+
snapshot,
|
|
2614
|
+
get index() {
|
|
2615
|
+
return index;
|
|
2616
|
+
}
|
|
2617
|
+
};
|
|
2645
2618
|
}
|
|
2646
|
-
function
|
|
2647
|
-
if (!
|
|
2648
|
-
|
|
2649
|
-
|
|
2619
|
+
function createParamStore(startIndex = 1) {
|
|
2620
|
+
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2621
|
+
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
2622
|
+
}
|
|
2623
|
+
if (startIndex > MAX_PARAM_INDEX) {
|
|
2650
2624
|
throw new Error(
|
|
2651
|
-
|
|
2625
|
+
`Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
|
|
2652
2626
|
);
|
|
2653
2627
|
}
|
|
2654
|
-
return
|
|
2628
|
+
return createStoreInternal(startIndex);
|
|
2655
2629
|
}
|
|
2656
|
-
function
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
if (n === 0) return 0;
|
|
2664
|
-
}
|
|
2665
|
-
return n;
|
|
2630
|
+
function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
|
|
2631
|
+
validateState([...existingParams], [...existingMappings], nextIndex);
|
|
2632
|
+
return createStoreInternal(
|
|
2633
|
+
nextIndex,
|
|
2634
|
+
[...existingParams],
|
|
2635
|
+
[...existingMappings]
|
|
2636
|
+
);
|
|
2666
2637
|
}
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2638
|
+
|
|
2639
|
+
// src/builder/shared/state.ts
|
|
2640
|
+
function toPublicResult(clause, joins, params) {
|
|
2641
|
+
const snapshot = params.snapshot();
|
|
2642
|
+
return Object.freeze({
|
|
2643
|
+
clause: clause || DEFAULT_WHERE_CLAUSE,
|
|
2644
|
+
joins: Object.freeze([...joins]),
|
|
2645
|
+
params: snapshot.params,
|
|
2646
|
+
paramMappings: snapshot.mappings,
|
|
2647
|
+
nextParamIndex: snapshot.index
|
|
2672
2648
|
});
|
|
2673
2649
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2650
|
+
|
|
2651
|
+
// src/builder/where.ts
|
|
2652
|
+
function buildWhereClause(where, options) {
|
|
2653
|
+
var _a, _b, _c, _d, _e;
|
|
2654
|
+
const dialect = options.dialect || getGlobalDialect();
|
|
2655
|
+
const params = (_a = options.params) != null ? _a : createParamStore();
|
|
2656
|
+
const ctx = {
|
|
2657
|
+
alias: options.alias,
|
|
2658
|
+
model: options.model,
|
|
2659
|
+
schemaModels: (_b = options.schemaModels) != null ? _b : [],
|
|
2660
|
+
path: (_c = options.path) != null ? _c : [],
|
|
2661
|
+
isSubquery: (_d = options.isSubquery) != null ? _d : false,
|
|
2662
|
+
aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
|
|
2663
|
+
dialect,
|
|
2664
|
+
params,
|
|
2665
|
+
depth: 0
|
|
2666
|
+
};
|
|
2667
|
+
const result = whereBuilderInstance.build(where, ctx);
|
|
2668
|
+
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2669
|
+
if (!options.isSubquery) {
|
|
2670
|
+
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2671
|
+
(m) => parseInt(m[1], 10)
|
|
2672
|
+
);
|
|
2673
|
+
if (nums.length > 0) {
|
|
2674
|
+
const min = Math.min(...nums);
|
|
2675
|
+
if (min === 1) {
|
|
2676
|
+
validateParamConsistency(publicResult.clause, publicResult.params);
|
|
2677
|
+
} else {
|
|
2678
|
+
validateParamConsistencyFragment(
|
|
2679
|
+
publicResult.clause,
|
|
2680
|
+
publicResult.params
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2688
2684
|
}
|
|
2689
|
-
return
|
|
2685
|
+
return publicResult;
|
|
2690
2686
|
}
|
|
2691
2687
|
|
|
2692
2688
|
// src/builder/select/fields.ts
|
|
@@ -3437,8 +3433,8 @@ function replaceOrderByAlias(orderBy, fromAlias, outerAlias) {
|
|
|
3437
3433
|
const replacement = `${outerAlias}.`;
|
|
3438
3434
|
return orderBy.split(needle).join(replacement);
|
|
3439
3435
|
}
|
|
3440
|
-
function buildDistinctColumns(distinct, fromAlias) {
|
|
3441
|
-
return distinct.map((f) => col(fromAlias, f)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3436
|
+
function buildDistinctColumns(distinct, fromAlias, model) {
|
|
3437
|
+
return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3442
3438
|
}
|
|
3443
3439
|
function buildOutputColumns(scalarNames, includeNames, hasCount) {
|
|
3444
3440
|
const outputCols = [...scalarNames, ...includeNames];
|
|
@@ -3452,13 +3448,13 @@ function buildOutputColumns(scalarNames, includeNames, hasCount) {
|
|
|
3452
3448
|
return formatted;
|
|
3453
3449
|
}
|
|
3454
3450
|
function buildWindowOrder(args) {
|
|
3455
|
-
const { baseOrder, idField, fromAlias } = args;
|
|
3451
|
+
const { baseOrder, idField, fromAlias, model } = args;
|
|
3456
3452
|
const orderFields = baseOrder.split(SQL_SEPARATORS.ORDER_BY).map((s) => s.trim().toLowerCase());
|
|
3457
3453
|
const hasIdInOrder = orderFields.some(
|
|
3458
3454
|
(f) => f.startsWith(`${fromAlias}.id `) || f.startsWith(`${fromAlias}."id" `)
|
|
3459
3455
|
);
|
|
3460
3456
|
if (hasIdInOrder) return baseOrder;
|
|
3461
|
-
const idTiebreaker = idField ? `, ${col(fromAlias, "id")} ASC` : "";
|
|
3457
|
+
const idTiebreaker = idField ? `, ${col(fromAlias, "id", model)} ASC` : "";
|
|
3462
3458
|
return `${baseOrder}${idTiebreaker}`;
|
|
3463
3459
|
}
|
|
3464
3460
|
function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
@@ -3475,14 +3471,17 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
|
|
|
3475
3471
|
includeNames,
|
|
3476
3472
|
hasCount
|
|
3477
3473
|
);
|
|
3478
|
-
const distinctCols = buildDistinctColumns([...distinct], from.alias);
|
|
3479
|
-
const fallbackOrder = [...distinct].map((f) => `${col(from.alias, f)} ASC`).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3480
|
-
const idField = model.fields.find(
|
|
3474
|
+
const distinctCols = buildDistinctColumns([...distinct], from.alias, model);
|
|
3475
|
+
const fallbackOrder = [...distinct].map((f) => `${col(from.alias, f, model)} ASC`).join(SQL_SEPARATORS.FIELD_LIST);
|
|
3476
|
+
const idField = model.fields.find(
|
|
3477
|
+
(f) => f.name === "id" && !f.isRelation
|
|
3478
|
+
);
|
|
3481
3479
|
const baseOrder = isNonEmptyString(orderBy) ? orderBy : fallbackOrder;
|
|
3482
3480
|
const windowOrder = buildWindowOrder({
|
|
3483
3481
|
baseOrder,
|
|
3484
3482
|
idField,
|
|
3485
|
-
fromAlias: from.alias
|
|
3483
|
+
fromAlias: from.alias,
|
|
3484
|
+
model
|
|
3486
3485
|
});
|
|
3487
3486
|
const outerOrder = isNonEmptyString(orderBy) ? replaceOrderByAlias(orderBy, from.alias, `"__tp_distinct"`) : replaceOrderByAlias(fallbackOrder, from.alias, `"__tp_distinct"`);
|
|
3488
3487
|
const joins = buildJoinsSql(whereJoins, countJoins);
|
|
@@ -3593,9 +3592,9 @@ function withCountJoins(spec, countJoins, whereJoins) {
|
|
|
3593
3592
|
whereJoins: [...whereJoins || [], ...countJoins || []]
|
|
3594
3593
|
});
|
|
3595
3594
|
}
|
|
3596
|
-
function buildPostgresDistinctOnClause(fromAlias, distinct) {
|
|
3595
|
+
function buildPostgresDistinctOnClause(fromAlias, distinct, model) {
|
|
3597
3596
|
if (!isNonEmptyArray(distinct)) return null;
|
|
3598
|
-
const distinctCols = buildDistinctColumns([...distinct], fromAlias);
|
|
3597
|
+
const distinctCols = buildDistinctColumns([...distinct], fromAlias, model);
|
|
3599
3598
|
return `${SQL_TEMPLATES.DISTINCT_ON} (${distinctCols})`;
|
|
3600
3599
|
}
|
|
3601
3600
|
function pushJoinGroups(parts, ...groups) {
|
|
@@ -3625,7 +3624,8 @@ function constructFinalSql(spec) {
|
|
|
3625
3624
|
method,
|
|
3626
3625
|
cursorClause,
|
|
3627
3626
|
params,
|
|
3628
|
-
dialect
|
|
3627
|
+
dialect,
|
|
3628
|
+
model
|
|
3629
3629
|
} = spec;
|
|
3630
3630
|
const useWindowDistinct = hasWindowDistinct(spec);
|
|
3631
3631
|
assertDistinctAllowed(method, useWindowDistinct);
|
|
@@ -3639,7 +3639,7 @@ function constructFinalSql(spec) {
|
|
|
3639
3639
|
return finalizeSql(sql2, params);
|
|
3640
3640
|
}
|
|
3641
3641
|
const parts = [SQL_TEMPLATES.SELECT];
|
|
3642
|
-
const distinctOn = dialect === "postgres" ? buildPostgresDistinctOnClause(from.alias, distinct) : null;
|
|
3642
|
+
const distinctOn = dialect === "postgres" ? buildPostgresDistinctOnClause(from.alias, distinct, model) : null;
|
|
3643
3643
|
if (distinctOn) parts.push(distinctOn);
|
|
3644
3644
|
const baseSelect = (select != null ? select : "").trim();
|
|
3645
3645
|
const fullSelectList = buildSelectList(baseSelect, includeCols);
|
|
@@ -3899,17 +3899,17 @@ function getModelFieldMap(model) {
|
|
|
3899
3899
|
function isTruthySelection(v) {
|
|
3900
3900
|
return v === true;
|
|
3901
3901
|
}
|
|
3902
|
-
function aggExprForField(aggKey, field, alias) {
|
|
3902
|
+
function aggExprForField(aggKey, field, alias, model) {
|
|
3903
3903
|
if (aggKey === "_count") {
|
|
3904
|
-
return field === "_all" ? `COUNT(*)` : `COUNT(${col(alias, field)})`;
|
|
3904
|
+
return field === "_all" ? `COUNT(*)` : `COUNT(${col(alias, field, model)})`;
|
|
3905
3905
|
}
|
|
3906
3906
|
if (field === "_all") {
|
|
3907
3907
|
throw new Error(`'${aggKey}' does not support '_all'`);
|
|
3908
3908
|
}
|
|
3909
|
-
if (aggKey === "_sum") return `SUM(${col(alias, field)})`;
|
|
3910
|
-
if (aggKey === "_avg") return `AVG(${col(alias, field)})`;
|
|
3911
|
-
if (aggKey === "_min") return `MIN(${col(alias, field)})`;
|
|
3912
|
-
return `MAX(${col(alias, field)})`;
|
|
3909
|
+
if (aggKey === "_sum") return `SUM(${col(alias, field, model)})`;
|
|
3910
|
+
if (aggKey === "_avg") return `AVG(${col(alias, field, model)})`;
|
|
3911
|
+
if (aggKey === "_min") return `MIN(${col(alias, field, model)})`;
|
|
3912
|
+
return `MAX(${col(alias, field, model)})`;
|
|
3913
3913
|
}
|
|
3914
3914
|
function buildComparisonOp(op) {
|
|
3915
3915
|
const sqlOp = COMPARISON_OPS[op];
|
|
@@ -4090,7 +4090,7 @@ function buildHavingForAggregateFirstShape(aggKey, target, alias, params, dialec
|
|
|
4090
4090
|
for (const [field, filter] of Object.entries(target)) {
|
|
4091
4091
|
assertHavingAggTarget(aggKey, field, model);
|
|
4092
4092
|
if (!isPlainObject(filter) || Object.keys(filter).length === 0) continue;
|
|
4093
|
-
const expr = aggExprForField(aggKey, field, alias);
|
|
4093
|
+
const expr = aggExprForField(aggKey, field, alias, model);
|
|
4094
4094
|
out.push(...buildHavingOpsForExpr(expr, filter, params, dialect));
|
|
4095
4095
|
}
|
|
4096
4096
|
return out;
|
|
@@ -4107,7 +4107,7 @@ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect
|
|
|
4107
4107
|
assertAggregateFieldType(aggKey, field.type, field.name, model.name);
|
|
4108
4108
|
const entries = Object.entries(aggFilter);
|
|
4109
4109
|
if (entries.length === 0) continue;
|
|
4110
|
-
const expr = aggExprForField(aggKey, fieldName, alias);
|
|
4110
|
+
const expr = aggExprForField(aggKey, fieldName, alias, model);
|
|
4111
4111
|
for (const [op, val] of entries) {
|
|
4112
4112
|
if (op === "mode") continue;
|
|
4113
4113
|
const built = buildSimpleComparison(expr, op, val, params, dialect);
|
|
@@ -4146,10 +4146,10 @@ function assertCountableScalarField(fieldMap, model, fieldName) {
|
|
|
4146
4146
|
);
|
|
4147
4147
|
}
|
|
4148
4148
|
}
|
|
4149
|
-
function pushCountField(fields, alias, fieldName) {
|
|
4149
|
+
function pushCountField(fields, alias, fieldName, model) {
|
|
4150
4150
|
const outAlias = `_count.${fieldName}`;
|
|
4151
4151
|
fields.push(
|
|
4152
|
-
`COUNT(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
|
|
4152
|
+
`COUNT(${col(alias, fieldName, model)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
|
|
4153
4153
|
);
|
|
4154
4154
|
}
|
|
4155
4155
|
function addCountFields(fields, countArg, alias, model, fieldMap) {
|
|
@@ -4167,7 +4167,7 @@ function addCountFields(fields, countArg, alias, model, fieldMap) {
|
|
|
4167
4167
|
);
|
|
4168
4168
|
for (const [f] of selected) {
|
|
4169
4169
|
assertCountableScalarField(fieldMap, model, f);
|
|
4170
|
-
pushCountField(fields, alias, f);
|
|
4170
|
+
pushCountField(fields, alias, f, model);
|
|
4171
4171
|
}
|
|
4172
4172
|
}
|
|
4173
4173
|
function getAggregateSelectionObject(args, agg) {
|
|
@@ -4188,10 +4188,10 @@ function assertAggregatableScalarField(fieldMap, model, agg, fieldName) {
|
|
|
4188
4188
|
}
|
|
4189
4189
|
return field;
|
|
4190
4190
|
}
|
|
4191
|
-
function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName) {
|
|
4191
|
+
function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model) {
|
|
4192
4192
|
const outAlias = `${agg}.${fieldName}`;
|
|
4193
4193
|
fields.push(
|
|
4194
|
-
`${aggFn}(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
|
|
4194
|
+
`${aggFn}(${col(alias, fieldName, model)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
|
|
4195
4195
|
);
|
|
4196
4196
|
}
|
|
4197
4197
|
function addAggregateFields(fields, args, alias, model, fieldMap) {
|
|
@@ -4209,7 +4209,7 @@ function addAggregateFields(fields, args, alias, model, fieldMap) {
|
|
|
4209
4209
|
fieldName
|
|
4210
4210
|
);
|
|
4211
4211
|
assertAggregateFieldType(agg, field.type, fieldName, model.name);
|
|
4212
|
-
pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName);
|
|
4212
|
+
pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName, model);
|
|
4213
4213
|
}
|
|
4214
4214
|
}
|
|
4215
4215
|
}
|
|
@@ -4272,7 +4272,7 @@ function assertGroupByBy(args, model) {
|
|
|
4272
4272
|
return byFields;
|
|
4273
4273
|
}
|
|
4274
4274
|
function buildGroupBySelectParts(args, alias, model, byFields) {
|
|
4275
|
-
const groupCols = byFields.map((f) => col(alias, f));
|
|
4275
|
+
const groupCols = byFields.map((f) => col(alias, f, model));
|
|
4276
4276
|
const groupFields = groupCols.join(SQL_SEPARATORS.FIELD_LIST);
|
|
4277
4277
|
const aggFields = buildAggregateFields(args, alias, model);
|
|
4278
4278
|
const selectFields = isNonEmptyArray(aggFields) ? groupCols.concat(aggFields).join(SQL_SEPARATORS.FIELD_LIST) : groupCols.join(SQL_SEPARATORS.FIELD_LIST);
|
|
@@ -4565,6 +4565,29 @@ function generateSQL2(directive) {
|
|
|
4565
4565
|
}
|
|
4566
4566
|
|
|
4567
4567
|
// src/code-emitter.ts
|
|
4568
|
+
function extractEnumMappings(datamodel) {
|
|
4569
|
+
const mappings = {};
|
|
4570
|
+
const fieldTypes = {};
|
|
4571
|
+
for (const enumDef of datamodel.enums) {
|
|
4572
|
+
const enumMapping = {};
|
|
4573
|
+
for (const value of enumDef.values) {
|
|
4574
|
+
enumMapping[value.name] = value.dbName || value.name;
|
|
4575
|
+
}
|
|
4576
|
+
if (Object.keys(enumMapping).length > 0) {
|
|
4577
|
+
mappings[enumDef.name] = enumMapping;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
for (const model of datamodel.models) {
|
|
4581
|
+
fieldTypes[model.name] = {};
|
|
4582
|
+
for (const field of model.fields) {
|
|
4583
|
+
const baseType = field.type.replace(/\[\]|\?/g, "");
|
|
4584
|
+
if (mappings[baseType]) {
|
|
4585
|
+
fieldTypes[model.name][field.name] = baseType;
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
return { mappings, fieldTypes };
|
|
4590
|
+
}
|
|
4568
4591
|
function generateClient(options) {
|
|
4569
4592
|
return __async(this, null, function* () {
|
|
4570
4593
|
const { datamodel, outputDir, config } = options;
|
|
@@ -4606,7 +4629,7 @@ function generateClient(options) {
|
|
|
4606
4629
|
}
|
|
4607
4630
|
const absoluteOutputDir = path.resolve(process.cwd(), outputDir);
|
|
4608
4631
|
yield promises.mkdir(absoluteOutputDir, { recursive: true });
|
|
4609
|
-
const code = generateCode(models, queries, config.dialect);
|
|
4632
|
+
const code = generateCode(models, queries, config.dialect, datamodel);
|
|
4610
4633
|
const outputPath = path.join(absoluteOutputDir, "index.ts");
|
|
4611
4634
|
yield promises.writeFile(outputPath, code);
|
|
4612
4635
|
const totalQueries = Array.from(queries.values()).reduce(
|
|
@@ -4634,15 +4657,20 @@ function createQueryKey(processedQuery) {
|
|
|
4634
4657
|
return value;
|
|
4635
4658
|
});
|
|
4636
4659
|
}
|
|
4637
|
-
function generateCode(models, queries, dialect) {
|
|
4660
|
+
function generateCode(models, queries, dialect, datamodel) {
|
|
4638
4661
|
const cleanModels = models.map((model) => __spreadProps(__spreadValues({}, model), {
|
|
4639
4662
|
fields: model.fields.filter((f) => f !== void 0 && f !== null)
|
|
4640
4663
|
}));
|
|
4664
|
+
const { mappings, fieldTypes } = extractEnumMappings(datamodel);
|
|
4641
4665
|
return `// Generated by @prisma-sql/generator - DO NOT EDIT
|
|
4642
4666
|
import { buildSQL, transformQueryResults, type PrismaMethod, type Model } from 'prisma-sql'
|
|
4643
4667
|
|
|
4644
4668
|
export const MODELS: Model[] = ${JSON.stringify(cleanModels, null, 2)}
|
|
4645
4669
|
|
|
4670
|
+
const ENUM_MAPPINGS: Record<string, Record<string, string>> = ${JSON.stringify(mappings, null, 2)}
|
|
4671
|
+
|
|
4672
|
+
const ENUM_FIELDS: Record<string, Record<string, string>> = ${JSON.stringify(fieldTypes, null, 2)}
|
|
4673
|
+
|
|
4646
4674
|
const QUERIES: Record<string, Record<string, Record<string, {
|
|
4647
4675
|
sql: string
|
|
4648
4676
|
params: unknown[]
|
|
@@ -4656,6 +4684,79 @@ function isDynamicParam(key: string): boolean {
|
|
|
4656
4684
|
return key === 'skip' || key === 'take' || key === 'cursor'
|
|
4657
4685
|
}
|
|
4658
4686
|
|
|
4687
|
+
function transformEnumInValue(value: unknown, enumType: string | undefined): unknown {
|
|
4688
|
+
if (!enumType || value === null || value === undefined) {
|
|
4689
|
+
return value
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
const mapping = ENUM_MAPPINGS[enumType]
|
|
4693
|
+
if (!mapping) {
|
|
4694
|
+
return value
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
// Handle array of enum values
|
|
4698
|
+
if (Array.isArray(value)) {
|
|
4699
|
+
return value.map(v => {
|
|
4700
|
+
if (typeof v === 'string' && mapping[v] !== undefined) {
|
|
4701
|
+
return mapping[v]
|
|
4702
|
+
}
|
|
4703
|
+
return v
|
|
4704
|
+
})
|
|
4705
|
+
}
|
|
4706
|
+
|
|
4707
|
+
// Handle single enum value
|
|
4708
|
+
if (typeof value === 'string' && mapping[value] !== undefined) {
|
|
4709
|
+
return mapping[value]
|
|
4710
|
+
}
|
|
4711
|
+
|
|
4712
|
+
return value
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
function transformEnumValues(modelName: string, obj: any, currentPath: string[] = []): any {
|
|
4716
|
+
if (obj === null || obj === undefined) {
|
|
4717
|
+
return obj
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
if (Array.isArray(obj)) {
|
|
4721
|
+
return obj.map(item => transformEnumValues(modelName, item, currentPath))
|
|
4722
|
+
}
|
|
4723
|
+
|
|
4724
|
+
if (typeof obj === 'object') {
|
|
4725
|
+
const transformed: any = {}
|
|
4726
|
+
const modelFields = ENUM_FIELDS[modelName] || {}
|
|
4727
|
+
|
|
4728
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
4729
|
+
const newPath = [...currentPath, key]
|
|
4730
|
+
|
|
4731
|
+
// Check if current key is an enum field at root level
|
|
4732
|
+
const enumType = modelFields[key]
|
|
4733
|
+
|
|
4734
|
+
if (enumType) {
|
|
4735
|
+
// This is an enum field - check if value is direct or has operators
|
|
4736
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
4737
|
+
// Has operators like { equals: "ACTIVE" }, { in: ["ACTIVE"] }, etc.
|
|
4738
|
+
const transformedOperators: any = {}
|
|
4739
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
4740
|
+
transformedOperators[op] = transformEnumInValue(opValue, enumType)
|
|
4741
|
+
}
|
|
4742
|
+
transformed[key] = transformedOperators
|
|
4743
|
+
} else {
|
|
4744
|
+
// Direct value like { status: "ACTIVE" }
|
|
4745
|
+
transformed[key] = transformEnumInValue(value, enumType)
|
|
4746
|
+
}
|
|
4747
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
4748
|
+
// Recursively transform nested objects (relations, logical operators, etc)
|
|
4749
|
+
transformed[key] = transformEnumValues(modelName, value, newPath)
|
|
4750
|
+
} else {
|
|
4751
|
+
transformed[key] = value
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
return transformed
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4757
|
+
return obj
|
|
4758
|
+
}
|
|
4759
|
+
|
|
4659
4760
|
function normalizeQuery(args: any): string {
|
|
4660
4761
|
if (!args) return '{}'
|
|
4661
4762
|
|
|
@@ -4771,7 +4872,10 @@ export function speedExtension(config: {
|
|
|
4771
4872
|
const modelName = this?.name || this?.$name
|
|
4772
4873
|
const startTime = Date.now()
|
|
4773
4874
|
|
|
4774
|
-
|
|
4875
|
+
// Transform enum values before processing
|
|
4876
|
+
const transformedArgs = transformEnumValues(modelName, args || {})
|
|
4877
|
+
|
|
4878
|
+
const queryKey = normalizeQuery(transformedArgs)
|
|
4775
4879
|
const prebakedQuery = QUERIES[modelName]?.[method]?.[queryKey]
|
|
4776
4880
|
|
|
4777
4881
|
let sql: string
|
|
@@ -4780,7 +4884,7 @@ export function speedExtension(config: {
|
|
|
4780
4884
|
|
|
4781
4885
|
if (prebakedQuery) {
|
|
4782
4886
|
sql = prebakedQuery.sql
|
|
4783
|
-
params = [...prebakedQuery.params, ...extractDynamicParams(
|
|
4887
|
+
params = [...prebakedQuery.params, ...extractDynamicParams(transformedArgs, prebakedQuery.dynamicKeys)]
|
|
4784
4888
|
prebaked = true
|
|
4785
4889
|
} else {
|
|
4786
4890
|
const model = MODELS.find((m) => m.name === modelName)
|
|
@@ -4789,7 +4893,7 @@ export function speedExtension(config: {
|
|
|
4789
4893
|
return this.$parent[modelName][method](args)
|
|
4790
4894
|
}
|
|
4791
4895
|
|
|
4792
|
-
const result = buildSQL(model, MODELS, method,
|
|
4896
|
+
const result = buildSQL(model, MODELS, method, transformedArgs, DIALECT)
|
|
4793
4897
|
sql = result.sql
|
|
4794
4898
|
params = result.params
|
|
4795
4899
|
}
|