prisma-sql 1.38.0 → 1.40.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 +1542 -1546
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +1542 -1546
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +1863 -1581
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1863 -1581
- package/dist/index.js.map +1 -1
- package/package.json +4 -15
- 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.40.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",
|
|
@@ -97,17 +97,6 @@ var require_package = __commonJS({
|
|
|
97
97
|
"sonar-cli": "sonar-scanner -Dsonar.projectKey=prisma-sql -Dsonar.sources=./src -Dsonar.host.url=http://localhost:9000 -Dsonar.login=sqp_9fe07460d0aa83f711d0edf4f317f05019d0613b",
|
|
98
98
|
sonar: "yarn sonar-cli && npx tsx scripts/sonar.ts"
|
|
99
99
|
},
|
|
100
|
-
workspaces: {
|
|
101
|
-
packages: [
|
|
102
|
-
"demo"
|
|
103
|
-
],
|
|
104
|
-
nohoist: [
|
|
105
|
-
"**/prisma",
|
|
106
|
-
"**/prisma/**",
|
|
107
|
-
"**/@prisma/**",
|
|
108
|
-
"**/prisma-*"
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
100
|
keywords: [
|
|
112
101
|
"prisma",
|
|
113
102
|
"sql",
|
|
@@ -140,7 +129,6 @@ var require_package = __commonJS({
|
|
|
140
129
|
"@faker-js/faker": "^10.2.0",
|
|
141
130
|
"@prisma/adapter-better-sqlite3": "^7.2.0",
|
|
142
131
|
"@prisma/adapter-pg": "^7.2.0",
|
|
143
|
-
"@prisma/client": "7.2.0",
|
|
144
132
|
"@types/better-sqlite3": "^7.6.13",
|
|
145
133
|
"@types/node": "^25.0.10",
|
|
146
134
|
"@vitest/coverage-v8": "4.0.18",
|
|
@@ -148,11 +136,12 @@ var require_package = __commonJS({
|
|
|
148
136
|
"drizzle-kit": "^0.31.8",
|
|
149
137
|
"drizzle-orm": "^0.45.1",
|
|
150
138
|
postgres: "^3.4.8",
|
|
151
|
-
prisma: "7.2.0",
|
|
152
139
|
tsup: "^8.5.1",
|
|
153
140
|
tsx: "^4.21.0",
|
|
154
141
|
typescript: "^5.9.3",
|
|
155
|
-
vitest: "^4.0.18"
|
|
142
|
+
vitest: "^4.0.18",
|
|
143
|
+
"@prisma/client": "7.2.0",
|
|
144
|
+
prisma: "7.2.0"
|
|
156
145
|
},
|
|
157
146
|
engines: {
|
|
158
147
|
node: ">=16.0.0"
|
|
@@ -161,6 +150,159 @@ var require_package = __commonJS({
|
|
|
161
150
|
}
|
|
162
151
|
});
|
|
163
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
|
+
|
|
295
|
+
// src/utils/normalize-value.ts
|
|
296
|
+
function normalizeValue(value) {
|
|
297
|
+
if (value instanceof Date) {
|
|
298
|
+
return value.toISOString();
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(value)) {
|
|
301
|
+
return value.map(normalizeValue);
|
|
302
|
+
}
|
|
303
|
+
return value;
|
|
304
|
+
}
|
|
305
|
+
|
|
164
306
|
// src/sql-builder-dialect.ts
|
|
165
307
|
var globalDialect = "postgres";
|
|
166
308
|
function setGlobalDialect(dialect) {
|
|
@@ -337,153 +479,11 @@ function prepareArrayParam(value, dialect) {
|
|
|
337
479
|
throw new Error("prepareArrayParam requires array value");
|
|
338
480
|
}
|
|
339
481
|
if (dialect === "postgres") {
|
|
340
|
-
return value;
|
|
482
|
+
return value.map(normalizeValue);
|
|
341
483
|
}
|
|
342
484
|
return JSON.stringify(value);
|
|
343
485
|
}
|
|
344
486
|
|
|
345
|
-
// src/builder/shared/constants.ts
|
|
346
|
-
var SQL_SEPARATORS = Object.freeze({
|
|
347
|
-
FIELD_LIST: ", ",
|
|
348
|
-
CONDITION_AND: " AND ",
|
|
349
|
-
CONDITION_OR: " OR ",
|
|
350
|
-
ORDER_BY: ", "
|
|
351
|
-
});
|
|
352
|
-
var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
353
|
-
"select",
|
|
354
|
-
"from",
|
|
355
|
-
"where",
|
|
356
|
-
"and",
|
|
357
|
-
"or",
|
|
358
|
-
"not",
|
|
359
|
-
"in",
|
|
360
|
-
"like",
|
|
361
|
-
"between",
|
|
362
|
-
"order",
|
|
363
|
-
"by",
|
|
364
|
-
"group",
|
|
365
|
-
"having",
|
|
366
|
-
"limit",
|
|
367
|
-
"offset",
|
|
368
|
-
"join",
|
|
369
|
-
"inner",
|
|
370
|
-
"left",
|
|
371
|
-
"right",
|
|
372
|
-
"outer",
|
|
373
|
-
"on",
|
|
374
|
-
"as",
|
|
375
|
-
"table",
|
|
376
|
-
"column",
|
|
377
|
-
"index",
|
|
378
|
-
"user",
|
|
379
|
-
"users",
|
|
380
|
-
"values",
|
|
381
|
-
"update",
|
|
382
|
-
"insert",
|
|
383
|
-
"delete",
|
|
384
|
-
"create",
|
|
385
|
-
"drop",
|
|
386
|
-
"alter",
|
|
387
|
-
"truncate",
|
|
388
|
-
"grant",
|
|
389
|
-
"revoke",
|
|
390
|
-
"exec",
|
|
391
|
-
"execute",
|
|
392
|
-
"union",
|
|
393
|
-
"intersect",
|
|
394
|
-
"except",
|
|
395
|
-
"case",
|
|
396
|
-
"when",
|
|
397
|
-
"then",
|
|
398
|
-
"else",
|
|
399
|
-
"end",
|
|
400
|
-
"null",
|
|
401
|
-
"true",
|
|
402
|
-
"false",
|
|
403
|
-
"is",
|
|
404
|
-
"exists",
|
|
405
|
-
"all",
|
|
406
|
-
"any",
|
|
407
|
-
"some"
|
|
408
|
-
]);
|
|
409
|
-
var SQL_KEYWORDS = SQL_RESERVED_WORDS;
|
|
410
|
-
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
411
|
-
var SPECIAL_FIELDS = Object.freeze({
|
|
412
|
-
ID: "id"
|
|
413
|
-
});
|
|
414
|
-
var SQL_TEMPLATES = Object.freeze({
|
|
415
|
-
PUBLIC_SCHEMA: "public",
|
|
416
|
-
WHERE: "WHERE",
|
|
417
|
-
SELECT: "SELECT",
|
|
418
|
-
FROM: "FROM",
|
|
419
|
-
ORDER_BY: "ORDER BY",
|
|
420
|
-
GROUP_BY: "GROUP BY",
|
|
421
|
-
HAVING: "HAVING",
|
|
422
|
-
LIMIT: "LIMIT",
|
|
423
|
-
OFFSET: "OFFSET",
|
|
424
|
-
COUNT_ALL: "COUNT(*)",
|
|
425
|
-
AS: "AS",
|
|
426
|
-
DISTINCT_ON: "DISTINCT ON",
|
|
427
|
-
IS_NULL: "IS NULL",
|
|
428
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
429
|
-
LIKE: "LIKE",
|
|
430
|
-
AND: "AND",
|
|
431
|
-
OR: "OR",
|
|
432
|
-
NOT: "NOT"
|
|
433
|
-
});
|
|
434
|
-
var SCHEMA_PREFIXES = Object.freeze({
|
|
435
|
-
INTERNAL: "@",
|
|
436
|
-
COMMENT: "//"
|
|
437
|
-
});
|
|
438
|
-
var Ops = Object.freeze({
|
|
439
|
-
EQUALS: "equals",
|
|
440
|
-
NOT: "not",
|
|
441
|
-
GT: "gt",
|
|
442
|
-
GTE: "gte",
|
|
443
|
-
LT: "lt",
|
|
444
|
-
LTE: "lte",
|
|
445
|
-
IN: "in",
|
|
446
|
-
NOT_IN: "notIn",
|
|
447
|
-
CONTAINS: "contains",
|
|
448
|
-
STARTS_WITH: "startsWith",
|
|
449
|
-
ENDS_WITH: "endsWith",
|
|
450
|
-
HAS: "has",
|
|
451
|
-
HAS_SOME: "hasSome",
|
|
452
|
-
HAS_EVERY: "hasEvery",
|
|
453
|
-
IS_EMPTY: "isEmpty",
|
|
454
|
-
PATH: "path",
|
|
455
|
-
STRING_CONTAINS: "string_contains",
|
|
456
|
-
STRING_STARTS_WITH: "string_starts_with",
|
|
457
|
-
STRING_ENDS_WITH: "string_ends_with"
|
|
458
|
-
});
|
|
459
|
-
var LogicalOps = Object.freeze({
|
|
460
|
-
AND: "AND",
|
|
461
|
-
OR: "OR",
|
|
462
|
-
NOT: "NOT"
|
|
463
|
-
});
|
|
464
|
-
var RelationFilters = Object.freeze({
|
|
465
|
-
SOME: "some",
|
|
466
|
-
EVERY: "every",
|
|
467
|
-
NONE: "none"
|
|
468
|
-
});
|
|
469
|
-
var Modes = Object.freeze({
|
|
470
|
-
INSENSITIVE: "insensitive",
|
|
471
|
-
DEFAULT: "default"
|
|
472
|
-
});
|
|
473
|
-
var Wildcards = Object.freeze({
|
|
474
|
-
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
475
|
-
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
476
|
-
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
477
|
-
});
|
|
478
|
-
var REGEX_CACHE = {
|
|
479
|
-
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
480
|
-
};
|
|
481
|
-
var LIMITS = Object.freeze({
|
|
482
|
-
MAX_QUERY_DEPTH: 50,
|
|
483
|
-
MAX_ARRAY_SIZE: 1e4,
|
|
484
|
-
MAX_STRING_LENGTH: 1e4
|
|
485
|
-
});
|
|
486
|
-
|
|
487
487
|
// src/builder/shared/validators/type-guards.ts
|
|
488
488
|
function isNotNullish(value) {
|
|
489
489
|
return value !== null && value !== void 0;
|
|
@@ -569,6 +569,19 @@ function getRelationFieldSet(model) {
|
|
|
569
569
|
RELATION_SET_CACHE.set(model, s);
|
|
570
570
|
return s;
|
|
571
571
|
}
|
|
572
|
+
var COLUMN_MAP_CACHE = /* @__PURE__ */ new WeakMap();
|
|
573
|
+
function getColumnMap(model) {
|
|
574
|
+
const cached = COLUMN_MAP_CACHE.get(model);
|
|
575
|
+
if (cached) return cached;
|
|
576
|
+
const map = /* @__PURE__ */ new Map();
|
|
577
|
+
for (const f of model.fields) {
|
|
578
|
+
if (!f.isRelation) {
|
|
579
|
+
map.set(f.name, f.dbName || f.name);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
COLUMN_MAP_CACHE.set(model, map);
|
|
583
|
+
return map;
|
|
584
|
+
}
|
|
572
585
|
|
|
573
586
|
// src/builder/shared/validators/sql-validators.ts
|
|
574
587
|
function isValidWhereClause(clause) {
|
|
@@ -956,27 +969,10 @@ function quote(id) {
|
|
|
956
969
|
}
|
|
957
970
|
return id;
|
|
958
971
|
}
|
|
959
|
-
function pickDbFieldName(field) {
|
|
960
|
-
const candidates = [
|
|
961
|
-
field == null ? void 0 : field.dbName,
|
|
962
|
-
field == null ? void 0 : field.columnName,
|
|
963
|
-
field == null ? void 0 : field.databaseName,
|
|
964
|
-
field == null ? void 0 : field.mappedName
|
|
965
|
-
];
|
|
966
|
-
for (const c of candidates) {
|
|
967
|
-
if (typeof c === "string") {
|
|
968
|
-
const s = c.trim();
|
|
969
|
-
if (s.length > 0) return s;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
return void 0;
|
|
973
|
-
}
|
|
974
972
|
function resolveColumnName(model, fieldName) {
|
|
975
|
-
var _a;
|
|
976
973
|
if (!model) return fieldName;
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
return (_a = pickDbFieldName(f)) != null ? _a : fieldName;
|
|
974
|
+
const columnMap = getColumnMap(model);
|
|
975
|
+
return columnMap.get(fieldName) || fieldName;
|
|
980
976
|
}
|
|
981
977
|
function quoteColumn(model, fieldName) {
|
|
982
978
|
return quote(resolveColumnName(model, fieldName));
|
|
@@ -1134,1570 +1130,1570 @@ function joinCondition(field, parentModel, childModel, parentAlias, childAlias)
|
|
|
1134
1130
|
function getModelByName(schemas, name) {
|
|
1135
1131
|
return schemas.find((m) => m.name === name);
|
|
1136
1132
|
}
|
|
1137
|
-
function
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
);
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1143
|
-
for (const [subOp, subVal] of entries) {
|
|
1144
|
-
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1145
|
-
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1146
|
-
}
|
|
1147
|
-
if (clauses.length === 0) return "";
|
|
1148
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1149
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1150
|
-
}
|
|
1151
|
-
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1152
|
-
if (val === void 0) return "";
|
|
1153
|
-
if (val === null) {
|
|
1154
|
-
return handleNullValue(expr, op);
|
|
1155
|
-
}
|
|
1156
|
-
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1157
|
-
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1158
|
-
}
|
|
1159
|
-
if (op === Ops.NOT) {
|
|
1160
|
-
const placeholder = params.addAuto(val);
|
|
1161
|
-
return `${expr} <> ${placeholder}`;
|
|
1133
|
+
function normalizeIntLike(name, v, opts = {}) {
|
|
1134
|
+
var _a, _b;
|
|
1135
|
+
if (!isNotNullish(v)) return void 0;
|
|
1136
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1137
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1138
|
+
throw new Error(`${name} must be an integer`);
|
|
1162
1139
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1140
|
+
const min = (_a = opts.min) != null ? _a : 0;
|
|
1141
|
+
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
1142
|
+
if (!allowZero && v === 0) {
|
|
1143
|
+
throw new Error(`${name} must be > 0`);
|
|
1166
1144
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
Ops.STARTS_WITH,
|
|
1170
|
-
Ops.ENDS_WITH
|
|
1171
|
-
]);
|
|
1172
|
-
if (STRING_LIKE_OPS.has(op)) {
|
|
1173
|
-
if (!isNotNullish(dialect)) {
|
|
1174
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1175
|
-
operator: op
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1145
|
+
if (v < min) {
|
|
1146
|
+
throw new Error(`${name} must be >= ${min}`);
|
|
1179
1147
|
}
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1183
|
-
}
|
|
1184
|
-
return handleInOperator(expr, op, val, params, dialect);
|
|
1148
|
+
if (typeof opts.max === "number" && v > opts.max) {
|
|
1149
|
+
throw new Error(`${name} must be <= ${opts.max}`);
|
|
1185
1150
|
}
|
|
1186
|
-
return
|
|
1187
|
-
}
|
|
1188
|
-
function handleNullValue(expr, op) {
|
|
1189
|
-
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1190
|
-
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1191
|
-
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1192
|
-
}
|
|
1193
|
-
function normalizeMode(v) {
|
|
1194
|
-
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1195
|
-
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1196
|
-
return void 0;
|
|
1151
|
+
return v;
|
|
1197
1152
|
}
|
|
1198
|
-
function
|
|
1199
|
-
const
|
|
1200
|
-
const
|
|
1201
|
-
return
|
|
1202
|
-
|
|
1203
|
-
val,
|
|
1204
|
-
params,
|
|
1205
|
-
dialect,
|
|
1206
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1207
|
-
` ${SQL_TEMPLATES.AND} `
|
|
1208
|
-
);
|
|
1153
|
+
function scopeName(scope, dynamicName) {
|
|
1154
|
+
const s = String(scope).trim();
|
|
1155
|
+
const dn = String(dynamicName).trim();
|
|
1156
|
+
if (s.length === 0) return dn;
|
|
1157
|
+
return `${s}:${dn}`;
|
|
1209
1158
|
}
|
|
1210
|
-
function
|
|
1211
|
-
if (
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1215
|
-
case Ops.STARTS_WITH:
|
|
1216
|
-
return `(${placeholder} || '%')`;
|
|
1217
|
-
case Ops.ENDS_WITH:
|
|
1218
|
-
return `('%' || ${placeholder})`;
|
|
1219
|
-
default:
|
|
1220
|
-
return placeholder;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
switch (op) {
|
|
1224
|
-
case Ops.CONTAINS:
|
|
1225
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1226
|
-
case Ops.STARTS_WITH:
|
|
1227
|
-
return `(${placeholder} || '%')`;
|
|
1228
|
-
case Ops.ENDS_WITH:
|
|
1229
|
-
return `('%' || ${placeholder})`;
|
|
1230
|
-
default:
|
|
1231
|
-
return placeholder;
|
|
1159
|
+
function addAutoScoped(params, value, scope) {
|
|
1160
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
1161
|
+
const dn = schemaParser.extractDynamicName(value);
|
|
1162
|
+
return params.add(void 0, scopeName(scope, dn));
|
|
1232
1163
|
}
|
|
1164
|
+
return params.add(value);
|
|
1233
1165
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1166
|
+
|
|
1167
|
+
// src/builder/shared/order-by-utils.ts
|
|
1168
|
+
var flipNulls = (v) => {
|
|
1169
|
+
const s = String(v).toLowerCase();
|
|
1170
|
+
if (s === "first") return "last";
|
|
1171
|
+
if (s === "last") return "first";
|
|
1172
|
+
return v;
|
|
1173
|
+
};
|
|
1174
|
+
var flipSortString = (v) => {
|
|
1175
|
+
if (typeof v !== "string") return v;
|
|
1176
|
+
const s = v.toLowerCase();
|
|
1177
|
+
if (s === "asc") return "desc";
|
|
1178
|
+
if (s === "desc") return "asc";
|
|
1179
|
+
return v;
|
|
1180
|
+
};
|
|
1181
|
+
var getNextSort = (sortRaw) => {
|
|
1182
|
+
if (typeof sortRaw !== "string") return sortRaw;
|
|
1183
|
+
const s = sortRaw.toLowerCase();
|
|
1184
|
+
if (s === "asc") return "desc";
|
|
1185
|
+
if (s === "desc") return "asc";
|
|
1186
|
+
return sortRaw;
|
|
1187
|
+
};
|
|
1188
|
+
var flipObjectSort = (obj) => {
|
|
1189
|
+
const sortRaw = obj.sort;
|
|
1190
|
+
const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
|
|
1191
|
+
const nullsRaw = obj.nulls;
|
|
1192
|
+
if (typeof nullsRaw === "string") {
|
|
1193
|
+
out.nulls = flipNulls(nullsRaw);
|
|
1243
1194
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1195
|
+
return out;
|
|
1196
|
+
};
|
|
1197
|
+
var flipValue = (v) => {
|
|
1198
|
+
if (typeof v === "string") return flipSortString(v);
|
|
1199
|
+
if (isPlainObject(v)) return flipObjectSort(v);
|
|
1200
|
+
return v;
|
|
1201
|
+
};
|
|
1202
|
+
var assertSingleFieldObject = (item) => {
|
|
1203
|
+
if (!isPlainObject(item)) {
|
|
1204
|
+
throw new Error("orderBy array entries must be objects");
|
|
1247
1205
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
if (val === void 0) return "";
|
|
1252
|
-
if (schemaParser.isDynamicParameter(val)) {
|
|
1253
|
-
const placeholder2 = params.addAuto(val);
|
|
1254
|
-
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1206
|
+
const entries = Object.entries(item);
|
|
1207
|
+
if (entries.length !== 1) {
|
|
1208
|
+
throw new Error("orderBy array entries must have exactly one field");
|
|
1255
1209
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1210
|
+
return entries[0];
|
|
1211
|
+
};
|
|
1212
|
+
var flipOrderByArray = (orderBy) => {
|
|
1213
|
+
return orderBy.map((item) => {
|
|
1214
|
+
const [k, v] = assertSingleFieldObject(item);
|
|
1215
|
+
return { [k]: flipValue(v) };
|
|
1216
|
+
});
|
|
1217
|
+
};
|
|
1218
|
+
var flipOrderByObject = (orderBy) => {
|
|
1219
|
+
const out = {};
|
|
1220
|
+
for (const [k, v] of Object.entries(orderBy)) {
|
|
1221
|
+
out[k] = flipValue(v);
|
|
1261
1222
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1223
|
+
return out;
|
|
1224
|
+
};
|
|
1225
|
+
function reverseOrderByInput(orderBy) {
|
|
1226
|
+
if (!isNotNullish(orderBy)) return orderBy;
|
|
1227
|
+
if (Array.isArray(orderBy)) {
|
|
1228
|
+
return flipOrderByArray(orderBy);
|
|
1264
1229
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
1268
|
-
}
|
|
1269
|
-
function handleComparisonOperator(expr, op, val, params) {
|
|
1270
|
-
if (val === void 0) return "";
|
|
1271
|
-
const COMPARISON_OPS2 = {
|
|
1272
|
-
[Ops.EQUALS]: "=",
|
|
1273
|
-
[Ops.GT]: ">",
|
|
1274
|
-
[Ops.GTE]: ">=",
|
|
1275
|
-
[Ops.LT]: "<",
|
|
1276
|
-
[Ops.LTE]: "<="
|
|
1277
|
-
};
|
|
1278
|
-
const sqlOp = COMPARISON_OPS2[op];
|
|
1279
|
-
if (!sqlOp) {
|
|
1280
|
-
throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
|
|
1230
|
+
if (isPlainObject(orderBy)) {
|
|
1231
|
+
return flipOrderByObject(orderBy);
|
|
1281
1232
|
}
|
|
1282
|
-
|
|
1283
|
-
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1233
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1284
1234
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1235
|
+
var normalizePairs = (pairs, parseValue) => {
|
|
1236
|
+
return pairs.map(([field, rawValue]) => {
|
|
1237
|
+
const parsed = parseValue(rawValue, field);
|
|
1238
|
+
return {
|
|
1239
|
+
[field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
1240
|
+
};
|
|
1241
|
+
});
|
|
1242
|
+
};
|
|
1243
|
+
function normalizeOrderByInput(orderBy, parseValue) {
|
|
1244
|
+
if (!isNotNullish(orderBy)) return [];
|
|
1245
|
+
if (Array.isArray(orderBy)) {
|
|
1246
|
+
const pairs = orderBy.map(assertSingleFieldObject);
|
|
1247
|
+
return normalizePairs(pairs, parseValue);
|
|
1288
1248
|
}
|
|
1289
|
-
if (
|
|
1290
|
-
|
|
1249
|
+
if (isPlainObject(orderBy)) {
|
|
1250
|
+
return normalizePairs(Object.entries(orderBy), parseValue);
|
|
1291
1251
|
}
|
|
1292
|
-
|
|
1293
|
-
return params.add(paramValue);
|
|
1252
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1294
1253
|
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
if (op === Ops.EQUALS) {
|
|
1303
|
-
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1304
|
-
}
|
|
1305
|
-
if (op === Ops.NOT) {
|
|
1306
|
-
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1307
|
-
}
|
|
1308
|
-
switch (op) {
|
|
1309
|
-
case Ops.HAS:
|
|
1310
|
-
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1311
|
-
case Ops.HAS_SOME:
|
|
1312
|
-
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1313
|
-
case Ops.HAS_EVERY:
|
|
1314
|
-
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1315
|
-
case Ops.IS_EMPTY:
|
|
1316
|
-
return handleArrayIsEmpty(expr, val, dialect);
|
|
1317
|
-
default:
|
|
1318
|
-
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
1319
|
-
}
|
|
1254
|
+
|
|
1255
|
+
// src/builder/pagination.ts
|
|
1256
|
+
var MAX_LIMIT_OFFSET = 2147483647;
|
|
1257
|
+
function parseDirectionRaw(raw, errorLabel) {
|
|
1258
|
+
const s = String(raw).toLowerCase();
|
|
1259
|
+
if (s === "asc" || s === "desc") return s;
|
|
1260
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1320
1261
|
}
|
|
1321
|
-
function
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
|
|
1262
|
+
function parseNullsRaw(raw, errorLabel) {
|
|
1263
|
+
if (!isNotNullish(raw)) return void 0;
|
|
1264
|
+
const s = String(raw).toLowerCase();
|
|
1265
|
+
if (s === "first" || s === "last") return s;
|
|
1266
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1267
|
+
}
|
|
1268
|
+
function requireOrderByObject(v, errorPrefix) {
|
|
1269
|
+
if (!isPlainObject(v) || !("sort" in v)) {
|
|
1270
|
+
throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
|
|
1325
1271
|
}
|
|
1326
|
-
|
|
1327
|
-
return arrayEquals(expr, placeholder, cast, dialect);
|
|
1272
|
+
return v;
|
|
1328
1273
|
}
|
|
1329
|
-
function
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
} else {
|
|
1337
|
-
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1338
|
-
operator: Ops.NOT,
|
|
1339
|
-
value: val
|
|
1340
|
-
});
|
|
1274
|
+
function assertAllowedOrderByKeys(obj, fieldName) {
|
|
1275
|
+
const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
|
|
1276
|
+
for (const k of Object.keys(obj)) {
|
|
1277
|
+
if (!allowed.has(k)) {
|
|
1278
|
+
throw new Error(
|
|
1279
|
+
fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
|
|
1280
|
+
);
|
|
1341
1281
|
}
|
|
1342
1282
|
}
|
|
1343
|
-
if (target === null) {
|
|
1344
|
-
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1345
|
-
}
|
|
1346
|
-
if (isEmptyArray(target)) {
|
|
1347
|
-
return arrayIsNotEmpty(expr, dialect);
|
|
1348
|
-
}
|
|
1349
|
-
const placeholder = buildArrayParam(target, params, dialect);
|
|
1350
|
-
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
1351
1283
|
}
|
|
1352
|
-
function
|
|
1353
|
-
|
|
1354
|
-
if (
|
|
1355
|
-
|
|
1356
|
-
operator: Ops.HAS,
|
|
1357
|
-
value: val
|
|
1358
|
-
});
|
|
1359
|
-
}
|
|
1360
|
-
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1361
|
-
throw createError(`has requires scalar value (single element), not array`, {
|
|
1362
|
-
operator: Ops.HAS,
|
|
1363
|
-
value: val
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1366
|
-
if (isPlainObject(val)) {
|
|
1367
|
-
throw createError(`has requires scalar value`, {
|
|
1368
|
-
operator: Ops.HAS,
|
|
1369
|
-
value: val
|
|
1370
|
-
});
|
|
1284
|
+
function parseOrderByValue(v, fieldName) {
|
|
1285
|
+
const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
|
|
1286
|
+
if (typeof v === "string") {
|
|
1287
|
+
return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
|
|
1371
1288
|
}
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1289
|
+
const obj = requireOrderByObject(v, errorPrefix);
|
|
1290
|
+
const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
|
|
1291
|
+
const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
|
|
1292
|
+
assertAllowedOrderByKeys(obj, fieldName);
|
|
1293
|
+
return { direction, nulls };
|
|
1374
1294
|
}
|
|
1375
|
-
function
|
|
1376
|
-
if (
|
|
1377
|
-
|
|
1378
|
-
const placeholder2 = params.addAuto(val);
|
|
1379
|
-
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
1295
|
+
function normalizeFiniteInteger(name, v) {
|
|
1296
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1297
|
+
throw new Error(`${name} must be an integer`);
|
|
1380
1298
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1299
|
+
return v;
|
|
1300
|
+
}
|
|
1301
|
+
function normalizeNonNegativeInt(name, v) {
|
|
1302
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1303
|
+
const n = normalizeFiniteInteger(name, v);
|
|
1304
|
+
if (n < 0) {
|
|
1305
|
+
throw new Error(`${name} must be >= 0`);
|
|
1386
1306
|
}
|
|
1387
|
-
if (
|
|
1388
|
-
throw
|
|
1389
|
-
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1390
|
-
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1391
|
-
);
|
|
1307
|
+
if (n > MAX_LIMIT_OFFSET) {
|
|
1308
|
+
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
1392
1309
|
}
|
|
1393
|
-
|
|
1394
|
-
const paramValue = prepareArrayParam(val, dialect);
|
|
1395
|
-
const placeholder = params.add(paramValue);
|
|
1396
|
-
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
1310
|
+
return n;
|
|
1397
1311
|
}
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
const placeholder = buildArrayParam(val, params, dialect);
|
|
1401
|
-
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
1312
|
+
function hasNonNullishProp(v, key) {
|
|
1313
|
+
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
1402
1314
|
}
|
|
1403
|
-
function
|
|
1404
|
-
if (
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1315
|
+
function normalizeIntegerOrDynamic(name, v) {
|
|
1316
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1317
|
+
return normalizeFiniteInteger(name, v);
|
|
1318
|
+
}
|
|
1319
|
+
function readSkipTake(relArgs) {
|
|
1320
|
+
const hasSkip = hasNonNullishProp(relArgs, "skip");
|
|
1321
|
+
const hasTake = hasNonNullishProp(relArgs, "take");
|
|
1322
|
+
if (!hasSkip && !hasTake) {
|
|
1323
|
+
return {
|
|
1324
|
+
hasSkip: false,
|
|
1325
|
+
hasTake: false,
|
|
1326
|
+
skipVal: void 0,
|
|
1327
|
+
takeVal: void 0
|
|
1328
|
+
};
|
|
1409
1329
|
}
|
|
1410
|
-
|
|
1330
|
+
const obj = relArgs;
|
|
1331
|
+
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
1332
|
+
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
1333
|
+
return { hasSkip, hasTake, skipVal, takeVal };
|
|
1411
1334
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1335
|
+
function buildOrderByFragment(entries, alias, dialect, model) {
|
|
1336
|
+
if (entries.length === 0) return "";
|
|
1337
|
+
const out = [];
|
|
1338
|
+
for (const e of entries) {
|
|
1339
|
+
const dir = e.direction.toUpperCase();
|
|
1340
|
+
const c = col(alias, e.field, model);
|
|
1341
|
+
if (dialect === "postgres") {
|
|
1342
|
+
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
1343
|
+
out.push(`${c} ${dir}${nulls}`);
|
|
1344
|
+
continue;
|
|
1422
1345
|
}
|
|
1423
|
-
if (
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
);
|
|
1346
|
+
if (isNotNullish(e.nulls)) {
|
|
1347
|
+
const isNullExpr = `(${c} IS NULL)`;
|
|
1348
|
+
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
1349
|
+
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
1350
|
+
out.push(`${c} ${dir}`);
|
|
1351
|
+
continue;
|
|
1428
1352
|
}
|
|
1353
|
+
out.push(`${c} ${dir}`);
|
|
1429
1354
|
}
|
|
1355
|
+
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
1430
1356
|
}
|
|
1431
|
-
function
|
|
1432
|
-
if (
|
|
1433
|
-
|
|
1434
|
-
return handleJsonPath(expr, val, params, dialect);
|
|
1435
|
-
}
|
|
1436
|
-
const jsonWildcards = {
|
|
1437
|
-
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1438
|
-
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1439
|
-
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1440
|
-
};
|
|
1441
|
-
if (op in jsonWildcards) {
|
|
1442
|
-
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
1357
|
+
function defaultNullsFor(dialect, direction) {
|
|
1358
|
+
if (dialect === "postgres") {
|
|
1359
|
+
return direction === "asc" ? "last" : "first";
|
|
1443
1360
|
}
|
|
1444
|
-
|
|
1361
|
+
return direction === "asc" ? "first" : "last";
|
|
1445
1362
|
}
|
|
1446
|
-
function
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1363
|
+
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1364
|
+
const existing = /* @__PURE__ */ new Map();
|
|
1365
|
+
for (const e of orderEntries) existing.set(e.field, e);
|
|
1366
|
+
const out = [...orderEntries];
|
|
1367
|
+
for (const [field] of cursorEntries) {
|
|
1368
|
+
if (!existing.has(field)) {
|
|
1369
|
+
out.push({ field, direction: "asc" });
|
|
1370
|
+
existing.set(field, out[out.length - 1]);
|
|
1371
|
+
}
|
|
1453
1372
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
["<", v.lt],
|
|
1461
|
-
["<=", v.lte]
|
|
1462
|
-
];
|
|
1463
|
-
const ops = rawOps.filter(
|
|
1464
|
-
([, value]) => value !== void 0
|
|
1465
|
-
);
|
|
1466
|
-
if (ops.length === 0) {
|
|
1467
|
-
throw createError("JSON path query missing comparison operator", {
|
|
1468
|
-
operator: Ops.PATH
|
|
1469
|
-
});
|
|
1373
|
+
return out;
|
|
1374
|
+
}
|
|
1375
|
+
function buildCursorFilterParts(cursor, cursorAlias, params, model) {
|
|
1376
|
+
const entries = Object.entries(cursor);
|
|
1377
|
+
if (entries.length === 0) {
|
|
1378
|
+
throw new Error("cursor must have at least one field");
|
|
1470
1379
|
}
|
|
1380
|
+
const placeholdersByField = /* @__PURE__ */ new Map();
|
|
1471
1381
|
const parts = [];
|
|
1472
|
-
for (const [
|
|
1382
|
+
for (const [field, value] of entries) {
|
|
1383
|
+
const c = `${cursorAlias}.${quote(field)}`;
|
|
1473
1384
|
if (value === null) {
|
|
1474
|
-
|
|
1475
|
-
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1385
|
+
parts.push(`${c} IS NULL`);
|
|
1476
1386
|
continue;
|
|
1477
1387
|
}
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
parts.push(`${
|
|
1481
|
-
}
|
|
1482
|
-
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1483
|
-
}
|
|
1484
|
-
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1485
|
-
if (!isNotNullish(val)) {
|
|
1486
|
-
throw createError(`JSON string operator requires non-null value`, {
|
|
1487
|
-
operator: op,
|
|
1488
|
-
value: val
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
1491
|
-
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1492
|
-
throw createError(`JSON string operator requires scalar value`, {
|
|
1493
|
-
operator: op,
|
|
1494
|
-
value: val
|
|
1495
|
-
});
|
|
1496
|
-
}
|
|
1497
|
-
const strVal = String(val);
|
|
1498
|
-
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1499
|
-
throw createError(
|
|
1500
|
-
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1501
|
-
{ operator: op }
|
|
1502
|
-
);
|
|
1388
|
+
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
1389
|
+
placeholdersByField.set(field, ph);
|
|
1390
|
+
parts.push(`${c} = ${ph}`);
|
|
1503
1391
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1392
|
+
return {
|
|
1393
|
+
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
1394
|
+
placeholdersByField
|
|
1395
|
+
};
|
|
1507
1396
|
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
function isListRelation(fieldType) {
|
|
1512
|
-
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1397
|
+
function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
|
|
1398
|
+
const colName = quote(field);
|
|
1399
|
+
return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1513
1400
|
}
|
|
1514
|
-
function
|
|
1515
|
-
|
|
1516
|
-
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1517
|
-
if (isLocal) {
|
|
1518
|
-
if (fkFields.length === 0) {
|
|
1519
|
-
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1520
|
-
field: field.name
|
|
1521
|
-
});
|
|
1522
|
-
}
|
|
1523
|
-
const parts = fkFields.map((fk) => {
|
|
1524
|
-
const safe = fk.replace(/"/g, '""');
|
|
1525
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
1526
|
-
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1527
|
-
});
|
|
1528
|
-
if (parts.length === 1) return parts[0];
|
|
1529
|
-
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
1530
|
-
}
|
|
1531
|
-
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
|
|
1532
|
-
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
1401
|
+
function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
|
|
1402
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1533
1403
|
}
|
|
1534
|
-
function
|
|
1535
|
-
|
|
1536
|
-
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1404
|
+
function buildCursorEqualityExpr(columnExpr, valueExpr) {
|
|
1405
|
+
return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
|
|
1537
1406
|
}
|
|
1538
|
-
function
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1407
|
+
function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
|
|
1408
|
+
const op = direction === "asc" ? ">" : "<";
|
|
1409
|
+
if (nulls === "first") {
|
|
1410
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
1411
|
+
}
|
|
1412
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
|
|
1541
1413
|
}
|
|
1542
|
-
function
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
if (noneValue !== void 0 && noneValue !== null) {
|
|
1555
|
-
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1556
|
-
alias: relAlias,
|
|
1557
|
-
model: relModel,
|
|
1558
|
-
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1559
|
-
isSubquery: true,
|
|
1560
|
-
depth: ctx.depth + 1
|
|
1561
|
-
}));
|
|
1562
|
-
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1563
|
-
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1564
|
-
if (canOptimize) {
|
|
1565
|
-
const checkField = relModel.fields.find(
|
|
1566
|
-
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1567
|
-
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1568
|
-
if (checkField) {
|
|
1569
|
-
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
1570
|
-
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1571
|
-
return Object.freeze({
|
|
1572
|
-
clause: whereClause,
|
|
1573
|
-
joins: [leftJoinSql]
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1414
|
+
function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params, model) {
|
|
1415
|
+
const parts = [];
|
|
1416
|
+
for (const [field, value] of Object.entries(cursor)) {
|
|
1417
|
+
const c = col(outerAlias, field, model);
|
|
1418
|
+
if (value === null) {
|
|
1419
|
+
parts.push(`${c} IS NULL`);
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
const existing = placeholdersByField.get(field);
|
|
1423
|
+
if (typeof existing === "string" && existing.length > 0) {
|
|
1424
|
+
parts.push(`${c} = ${existing}`);
|
|
1425
|
+
continue;
|
|
1576
1426
|
}
|
|
1427
|
+
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
1428
|
+
parts.push(`${c} = ${ph}`);
|
|
1577
1429
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1430
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1431
|
+
}
|
|
1432
|
+
function buildOrderEntries(orderBy) {
|
|
1433
|
+
const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
|
|
1434
|
+
const entries = [];
|
|
1435
|
+
for (const item of normalized) {
|
|
1436
|
+
for (const [field, value] of Object.entries(item)) {
|
|
1437
|
+
if (typeof value === "string") {
|
|
1438
|
+
entries.push({ field, direction: value });
|
|
1439
|
+
} else {
|
|
1440
|
+
entries.push({
|
|
1441
|
+
field,
|
|
1442
|
+
direction: value.sort,
|
|
1443
|
+
nulls: value.nulls
|
|
1444
|
+
});
|
|
1592
1445
|
}
|
|
1593
1446
|
}
|
|
1594
|
-
];
|
|
1595
|
-
const clauses = [];
|
|
1596
|
-
for (const { key, wrap } of filters) {
|
|
1597
|
-
const raw = value[key];
|
|
1598
|
-
if (raw === void 0 || raw === null) continue;
|
|
1599
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
1600
|
-
alias: relAlias,
|
|
1601
|
-
model: relModel,
|
|
1602
|
-
path: [...ctx.path, fieldName, key],
|
|
1603
|
-
isSubquery: true,
|
|
1604
|
-
depth: ctx.depth + 1
|
|
1605
|
-
}));
|
|
1606
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1607
|
-
clauses.push(wrap(sub.clause, j));
|
|
1608
|
-
}
|
|
1609
|
-
if (clauses.length === 0) {
|
|
1610
|
-
throw createError(
|
|
1611
|
-
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
1612
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1613
|
-
);
|
|
1614
1447
|
}
|
|
1615
|
-
return
|
|
1616
|
-
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
1617
|
-
joins: NO_JOINS
|
|
1618
|
-
});
|
|
1448
|
+
return entries;
|
|
1619
1449
|
}
|
|
1620
|
-
function
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
field,
|
|
1627
|
-
relModel,
|
|
1628
|
-
relTable,
|
|
1629
|
-
relAlias,
|
|
1630
|
-
join: join3
|
|
1631
|
-
} = args;
|
|
1632
|
-
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
1633
|
-
if (hasSomeEveryNone) {
|
|
1634
|
-
throw createError(
|
|
1635
|
-
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
1636
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1637
|
-
);
|
|
1450
|
+
function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
|
|
1451
|
+
var _a;
|
|
1452
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1453
|
+
const cursorEntries = Object.entries(cursor);
|
|
1454
|
+
if (cursorEntries.length === 0) {
|
|
1455
|
+
throw new Error("cursor must have at least one field");
|
|
1638
1456
|
}
|
|
1639
|
-
const
|
|
1640
|
-
const
|
|
1641
|
-
let
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
filterKey = "isNot";
|
|
1648
|
-
filterVal = value.isNot;
|
|
1457
|
+
const cursorAlias = "__tp_cursor_src";
|
|
1458
|
+
const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
|
|
1459
|
+
let orderEntries = buildOrderEntries(orderBy);
|
|
1460
|
+
if (orderEntries.length === 0) {
|
|
1461
|
+
orderEntries = cursorEntries.map(([field]) => ({
|
|
1462
|
+
field,
|
|
1463
|
+
direction: "asc"
|
|
1464
|
+
}));
|
|
1649
1465
|
} else {
|
|
1650
|
-
|
|
1651
|
-
filterVal = value;
|
|
1466
|
+
orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
|
|
1652
1467
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1468
|
+
const existsExpr = buildCursorRowExistsExpr(
|
|
1469
|
+
tableName,
|
|
1470
|
+
cursorAlias,
|
|
1471
|
+
cursorWhereSql
|
|
1472
|
+
);
|
|
1473
|
+
const outerCursorMatch = buildOuterCursorMatch(
|
|
1474
|
+
cursor,
|
|
1475
|
+
alias,
|
|
1476
|
+
placeholdersByField,
|
|
1477
|
+
params,
|
|
1478
|
+
model
|
|
1479
|
+
);
|
|
1480
|
+
const orClauses = [];
|
|
1481
|
+
for (let level = 0; level < orderEntries.length; level++) {
|
|
1482
|
+
const andParts = [];
|
|
1483
|
+
for (let i = 0; i < level; i++) {
|
|
1484
|
+
const e2 = orderEntries[i];
|
|
1485
|
+
const c2 = col(alias, e2.field, model);
|
|
1486
|
+
const v2 = cursorValueExpr(
|
|
1487
|
+
tableName,
|
|
1488
|
+
cursorAlias,
|
|
1489
|
+
cursorWhereSql,
|
|
1490
|
+
e2.field);
|
|
1491
|
+
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
1492
|
+
}
|
|
1493
|
+
const e = orderEntries[level];
|
|
1494
|
+
const c = col(alias, e.field, model);
|
|
1495
|
+
const v = cursorValueExpr(
|
|
1496
|
+
tableName,
|
|
1497
|
+
cursorAlias,
|
|
1498
|
+
cursorWhereSql,
|
|
1499
|
+
e.field);
|
|
1500
|
+
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
1501
|
+
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
1502
|
+
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
1658
1503
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1504
|
+
const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
|
|
1505
|
+
return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
|
|
1506
|
+
}
|
|
1507
|
+
function buildOrderBy(orderBy, alias, dialect, model) {
|
|
1508
|
+
const entries = buildOrderEntries(orderBy);
|
|
1509
|
+
if (entries.length === 0) return "";
|
|
1510
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1511
|
+
return buildOrderByFragment(entries, alias, d, model);
|
|
1512
|
+
}
|
|
1513
|
+
function buildOrderByClause(args, alias, dialect, model) {
|
|
1514
|
+
if (!isNotNullish(args.orderBy)) return "";
|
|
1515
|
+
const result = buildOrderBy(args.orderBy, alias, dialect, model);
|
|
1516
|
+
if (!isNonEmptyString(result)) {
|
|
1517
|
+
throw new Error(
|
|
1518
|
+
"buildOrderByClause: orderBy specified but produced empty result"
|
|
1668
1519
|
);
|
|
1669
|
-
return Object.freeze({
|
|
1670
|
-
clause: clause2,
|
|
1671
|
-
joins: NO_JOINS
|
|
1672
|
-
});
|
|
1673
1520
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
);
|
|
1521
|
+
return result;
|
|
1522
|
+
}
|
|
1523
|
+
function normalizeTakeLike(v) {
|
|
1524
|
+
const n = normalizeIntLike("take", v, {
|
|
1525
|
+
min: Number.MIN_SAFE_INTEGER,
|
|
1526
|
+
max: MAX_LIMIT_OFFSET,
|
|
1527
|
+
allowZero: true
|
|
1528
|
+
});
|
|
1529
|
+
if (typeof n === "number") {
|
|
1530
|
+
if (n === 0) return 0;
|
|
1684
1531
|
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
|
|
1693
|
-
return Object.freeze({
|
|
1694
|
-
clause,
|
|
1695
|
-
joins: NO_JOINS
|
|
1532
|
+
return n;
|
|
1533
|
+
}
|
|
1534
|
+
function normalizeSkipLike(v) {
|
|
1535
|
+
return normalizeIntLike("skip", v, {
|
|
1536
|
+
min: 0,
|
|
1537
|
+
max: MAX_LIMIT_OFFSET,
|
|
1538
|
+
allowZero: true
|
|
1696
1539
|
});
|
|
1697
1540
|
}
|
|
1698
|
-
function
|
|
1699
|
-
if (
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
});
|
|
1541
|
+
function getPaginationParams(method, args) {
|
|
1542
|
+
if (method === "findMany") {
|
|
1543
|
+
return {
|
|
1544
|
+
take: normalizeTakeLike(args.take),
|
|
1545
|
+
skip: normalizeSkipLike(args.skip),
|
|
1546
|
+
cursor: args.cursor
|
|
1547
|
+
};
|
|
1706
1548
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
if (!isValidRelationField(field)) {
|
|
1711
|
-
throw createError(`Invalid relation '${fieldName}'`, {
|
|
1712
|
-
field: fieldName,
|
|
1713
|
-
path: ctx.path,
|
|
1714
|
-
modelName: ctx.model.name
|
|
1715
|
-
});
|
|
1549
|
+
if (method === "findFirst") {
|
|
1550
|
+
const skip = normalizeSkipLike(args.skip);
|
|
1551
|
+
return { take: 1, skip: skip != null ? skip : 0 };
|
|
1716
1552
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
throw createError(
|
|
1720
|
-
`Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
|
|
1721
|
-
{
|
|
1722
|
-
field: fieldName,
|
|
1723
|
-
path: ctx.path,
|
|
1724
|
-
modelName: ctx.model.name
|
|
1725
|
-
}
|
|
1726
|
-
);
|
|
1553
|
+
if (method === "findUnique") {
|
|
1554
|
+
return { take: 1, skip: 0 };
|
|
1727
1555
|
}
|
|
1728
|
-
|
|
1729
|
-
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
1730
|
-
relModel.tableName,
|
|
1731
|
-
ctx.dialect
|
|
1732
|
-
);
|
|
1733
|
-
const relAlias = ctx.aliasGen.next(fieldName);
|
|
1734
|
-
const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
1735
|
-
const args = {
|
|
1736
|
-
fieldName,
|
|
1737
|
-
value,
|
|
1738
|
-
ctx,
|
|
1739
|
-
whereBuilder,
|
|
1740
|
-
field,
|
|
1741
|
-
relModel,
|
|
1742
|
-
relTable,
|
|
1743
|
-
relAlias,
|
|
1744
|
-
join: join3
|
|
1745
|
-
};
|
|
1746
|
-
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
1747
|
-
return buildToOneRelationFilters(args);
|
|
1748
|
-
}
|
|
1749
|
-
function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
|
|
1750
|
-
ensureRelationFilterObject(fieldName, value, ctx);
|
|
1751
|
-
return buildRelation(fieldName, value, ctx, whereBuilder);
|
|
1752
|
-
}
|
|
1753
|
-
function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
1754
|
-
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
1556
|
+
return {};
|
|
1755
1557
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
if (
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
availableFields: model.fields.map((f) => f.name)
|
|
1766
|
-
});
|
|
1558
|
+
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1559
|
+
const entries = Object.entries(val).filter(
|
|
1560
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
1561
|
+
);
|
|
1562
|
+
if (entries.length === 0) return "";
|
|
1563
|
+
const clauses = [];
|
|
1564
|
+
for (const [subOp, subVal] of entries) {
|
|
1565
|
+
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1566
|
+
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1767
1567
|
}
|
|
1768
|
-
return
|
|
1568
|
+
if (clauses.length === 0) return "";
|
|
1569
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1570
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1769
1571
|
}
|
|
1770
|
-
function
|
|
1771
|
-
if (
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
Ops.HAS_SOME,
|
|
1775
|
-
Ops.HAS_EVERY,
|
|
1776
|
-
Ops.IS_EMPTY
|
|
1777
|
-
]);
|
|
1778
|
-
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
1779
|
-
Ops.PATH,
|
|
1780
|
-
Ops.STRING_CONTAINS,
|
|
1781
|
-
Ops.STRING_STARTS_WITH,
|
|
1782
|
-
Ops.STRING_ENDS_WITH
|
|
1783
|
-
]);
|
|
1784
|
-
const isArrayOp = ARRAY_OPS.has(op);
|
|
1785
|
-
const isFieldArray = isArrayType(fieldType);
|
|
1786
|
-
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
1787
|
-
if (arrayOpMismatch) {
|
|
1788
|
-
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
1789
|
-
operator: op,
|
|
1790
|
-
field: fieldName,
|
|
1791
|
-
path,
|
|
1792
|
-
modelName
|
|
1793
|
-
});
|
|
1572
|
+
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1573
|
+
if (val === void 0) return "";
|
|
1574
|
+
if (val === null) {
|
|
1575
|
+
return handleNullValue(expr, op);
|
|
1794
1576
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
1798
|
-
if (jsonOpMismatch) {
|
|
1799
|
-
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
1800
|
-
operator: op,
|
|
1801
|
-
field: fieldName,
|
|
1802
|
-
path,
|
|
1803
|
-
modelName
|
|
1804
|
-
});
|
|
1577
|
+
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1578
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1805
1579
|
}
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1580
|
+
if (op === Ops.NOT) {
|
|
1581
|
+
const placeholder = params.addAuto(val);
|
|
1582
|
+
return `${expr} <> ${placeholder}`;
|
|
1583
|
+
}
|
|
1584
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1585
|
+
const placeholder = params.addAuto(val);
|
|
1586
|
+
return caseInsensitiveEquals(expr, placeholder);
|
|
1587
|
+
}
|
|
1588
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1589
|
+
Ops.CONTAINS,
|
|
1590
|
+
Ops.STARTS_WITH,
|
|
1591
|
+
Ops.ENDS_WITH
|
|
1592
|
+
]);
|
|
1593
|
+
if (STRING_LIKE_OPS.has(op)) {
|
|
1594
|
+
if (!isNotNullish(dialect)) {
|
|
1595
|
+
throw createError(`Like operators require a SQL dialect`, {
|
|
1596
|
+
operator: op
|
|
1815
1597
|
});
|
|
1816
1598
|
}
|
|
1817
|
-
return
|
|
1599
|
+
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1818
1600
|
}
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
var whereBuilderInstance = new WhereBuilder();
|
|
1823
|
-
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
1824
|
-
return Object.freeze({ clause, joins });
|
|
1825
|
-
}
|
|
1826
|
-
function dedupePreserveOrder(items) {
|
|
1827
|
-
if (items.length <= 1) return Object.freeze([...items]);
|
|
1828
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1829
|
-
const out = [];
|
|
1830
|
-
for (const s of items) {
|
|
1831
|
-
if (!seen.has(s)) {
|
|
1832
|
-
seen.add(s);
|
|
1833
|
-
out.push(s);
|
|
1601
|
+
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1602
|
+
if (!isNotNullish(dialect)) {
|
|
1603
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1834
1604
|
}
|
|
1605
|
+
return handleInOperator(expr, op, val, params, dialect);
|
|
1835
1606
|
}
|
|
1836
|
-
return
|
|
1607
|
+
return handleComparisonOperator(expr, op, val, params);
|
|
1837
1608
|
}
|
|
1838
|
-
function
|
|
1839
|
-
if (
|
|
1840
|
-
if (
|
|
1609
|
+
function handleNullValue(expr, op) {
|
|
1610
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1611
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1612
|
+
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1841
1613
|
}
|
|
1842
|
-
function
|
|
1843
|
-
if (
|
|
1844
|
-
if (
|
|
1845
|
-
|
|
1846
|
-
return null;
|
|
1614
|
+
function normalizeMode(v) {
|
|
1615
|
+
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1616
|
+
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1617
|
+
return void 0;
|
|
1847
1618
|
}
|
|
1848
|
-
function
|
|
1849
|
-
|
|
1619
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1620
|
+
const innerMode = normalizeMode(val.mode);
|
|
1621
|
+
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1622
|
+
return buildNotComposite(
|
|
1623
|
+
expr,
|
|
1624
|
+
val,
|
|
1625
|
+
params,
|
|
1626
|
+
dialect,
|
|
1627
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1628
|
+
` ${SQL_TEMPLATES.AND} `
|
|
1629
|
+
);
|
|
1850
1630
|
}
|
|
1851
|
-
function
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1631
|
+
function buildDynamicLikePattern(op, placeholder, dialect) {
|
|
1632
|
+
if (dialect === "postgres") {
|
|
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;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
switch (op) {
|
|
1645
|
+
case Ops.CONTAINS:
|
|
1646
|
+
return `('%' || ${placeholder} || '%')`;
|
|
1647
|
+
case Ops.STARTS_WITH:
|
|
1648
|
+
return `(${placeholder} || '%')`;
|
|
1649
|
+
case Ops.ENDS_WITH:
|
|
1650
|
+
return `('%' || ${placeholder})`;
|
|
1651
|
+
default:
|
|
1652
|
+
return placeholder;
|
|
1855
1653
|
}
|
|
1856
|
-
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
1857
1654
|
}
|
|
1858
|
-
function
|
|
1859
|
-
|
|
1860
|
-
if (
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
field: key,
|
|
1866
|
-
modelName: ctx.model.name,
|
|
1867
|
-
value
|
|
1868
|
-
});
|
|
1655
|
+
function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
1656
|
+
if (val === void 0) return "";
|
|
1657
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1658
|
+
const placeholder2 = params.addAuto(val);
|
|
1659
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
|
|
1660
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1661
|
+
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1869
1662
|
}
|
|
1870
|
-
return
|
|
1663
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
|
|
1871
1664
|
}
|
|
1872
|
-
|
|
1665
|
+
const placeholder = params.add(Wildcards[op](String(val)));
|
|
1666
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1667
|
+
return caseInsensitiveLike(expr, placeholder, dialect);
|
|
1668
|
+
}
|
|
1669
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
|
|
1873
1670
|
}
|
|
1874
|
-
function
|
|
1875
|
-
if (
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
);
|
|
1671
|
+
function handleInOperator(expr, op, val, params, dialect) {
|
|
1672
|
+
if (val === void 0) return "";
|
|
1673
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1674
|
+
const placeholder2 = params.addAuto(val);
|
|
1675
|
+
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1880
1676
|
}
|
|
1881
|
-
if (
|
|
1882
|
-
|
|
1677
|
+
if (!Array.isArray(val)) {
|
|
1678
|
+
throw createError(`IN operators require array value`, {
|
|
1679
|
+
operator: op,
|
|
1680
|
+
value: val
|
|
1681
|
+
});
|
|
1883
1682
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
for (const [key, value] of Object.entries(where)) {
|
|
1887
|
-
if (value === void 0) continue;
|
|
1888
|
-
const result = buildWhereEntry(key, value, ctx, builder);
|
|
1889
|
-
appendResult(result, clauses, allJoins);
|
|
1683
|
+
if (val.length === 0) {
|
|
1684
|
+
return op === Ops.IN ? "0=1" : "1=1";
|
|
1890
1685
|
}
|
|
1891
|
-
const
|
|
1892
|
-
|
|
1686
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1687
|
+
const placeholder = params.add(paramValue);
|
|
1688
|
+
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
1893
1689
|
}
|
|
1894
|
-
function
|
|
1895
|
-
if (
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
out.push(v);
|
|
1908
|
-
}
|
|
1909
|
-
return out;
|
|
1910
|
-
}
|
|
1911
|
-
if (isPlainObject(value)) {
|
|
1912
|
-
return [value];
|
|
1690
|
+
function handleComparisonOperator(expr, op, val, params) {
|
|
1691
|
+
if (val === void 0) return "";
|
|
1692
|
+
const COMPARISON_OPS2 = {
|
|
1693
|
+
[Ops.EQUALS]: "=",
|
|
1694
|
+
[Ops.GT]: ">",
|
|
1695
|
+
[Ops.GTE]: ">=",
|
|
1696
|
+
[Ops.LT]: "<",
|
|
1697
|
+
[Ops.LTE]: "<="
|
|
1698
|
+
};
|
|
1699
|
+
const sqlOp = COMPARISON_OPS2[op];
|
|
1700
|
+
if (!sqlOp) {
|
|
1701
|
+
throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
|
|
1913
1702
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
modelName: ctx.model.name,
|
|
1917
|
-
value
|
|
1918
|
-
});
|
|
1703
|
+
const placeholder = params.addAuto(val);
|
|
1704
|
+
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1919
1705
|
}
|
|
1920
|
-
function
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
for (let i = 0; i < conditions.length; i++) {
|
|
1924
|
-
const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
|
|
1925
|
-
path: [...ctx.path, operator, String(i)],
|
|
1926
|
-
depth: ctx.depth + 1
|
|
1927
|
-
}));
|
|
1928
|
-
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
1929
|
-
if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
|
|
1930
|
-
clauses.push(`(${result.clause})`);
|
|
1931
|
-
}
|
|
1706
|
+
function buildArrayParam(val, params, dialect) {
|
|
1707
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1708
|
+
return params.addAuto(val);
|
|
1932
1709
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
clauses: Object.freeze(clauses)
|
|
1936
|
-
};
|
|
1937
|
-
}
|
|
1938
|
-
function buildLogicalClause(operator, clauses) {
|
|
1939
|
-
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
1940
|
-
if (operator === "NOT") {
|
|
1941
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1942
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
1710
|
+
if (!Array.isArray(val)) {
|
|
1711
|
+
throw createError(`Array operation requires array value`, { value: val });
|
|
1943
1712
|
}
|
|
1944
|
-
|
|
1713
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1714
|
+
return params.add(paramValue);
|
|
1945
1715
|
}
|
|
1946
|
-
function
|
|
1947
|
-
|
|
1948
|
-
if (
|
|
1949
|
-
|
|
1950
|
-
|
|
1716
|
+
function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
|
|
1717
|
+
if (val === void 0) return "";
|
|
1718
|
+
if (val === null) {
|
|
1719
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1720
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1951
1721
|
}
|
|
1952
|
-
const
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
ctx,
|
|
1956
|
-
builder
|
|
1957
|
-
);
|
|
1958
|
-
const clause = buildLogicalClause(operator, clauses);
|
|
1959
|
-
return freezeResult(clause, joins);
|
|
1960
|
-
}
|
|
1961
|
-
function buildScalarField(fieldName, value, ctx) {
|
|
1962
|
-
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
1963
|
-
const expr = col(ctx.alias, fieldName, ctx.model);
|
|
1964
|
-
if (value === null) {
|
|
1965
|
-
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
1722
|
+
const cast = getArrayType(fieldType, dialect);
|
|
1723
|
+
if (op === Ops.EQUALS) {
|
|
1724
|
+
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1966
1725
|
}
|
|
1967
|
-
if (
|
|
1968
|
-
|
|
1969
|
-
const ops = Object.entries(value).filter(
|
|
1970
|
-
([k, v]) => k !== "mode" && v !== void 0
|
|
1971
|
-
);
|
|
1972
|
-
if (ops.length === 0) {
|
|
1973
|
-
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
1974
|
-
}
|
|
1975
|
-
const parts = [];
|
|
1976
|
-
for (const [op, val] of ops) {
|
|
1977
|
-
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
1978
|
-
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
1979
|
-
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
1980
|
-
}
|
|
1981
|
-
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
1982
|
-
return freezeResult(clause2, EMPTY_JOINS);
|
|
1983
|
-
}
|
|
1984
|
-
const clause = buildOperator(
|
|
1985
|
-
expr,
|
|
1986
|
-
Ops.EQUALS,
|
|
1987
|
-
value,
|
|
1988
|
-
ctx,
|
|
1989
|
-
void 0,
|
|
1990
|
-
field.type
|
|
1991
|
-
);
|
|
1992
|
-
return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
1993
|
-
}
|
|
1994
|
-
function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
1995
|
-
if (fieldType && isArrayType(fieldType)) {
|
|
1996
|
-
return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
|
|
1726
|
+
if (op === Ops.NOT) {
|
|
1727
|
+
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1997
1728
|
}
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
return
|
|
2007
|
-
|
|
1729
|
+
switch (op) {
|
|
1730
|
+
case Ops.HAS:
|
|
1731
|
+
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1732
|
+
case Ops.HAS_SOME:
|
|
1733
|
+
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1734
|
+
case Ops.HAS_EVERY:
|
|
1735
|
+
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1736
|
+
case Ops.IS_EMPTY:
|
|
1737
|
+
return handleArrayIsEmpty(expr, val, dialect);
|
|
1738
|
+
default:
|
|
1739
|
+
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
2008
1740
|
}
|
|
2009
|
-
return buildScalarOperator(
|
|
2010
|
-
expr,
|
|
2011
|
-
op,
|
|
2012
|
-
val,
|
|
2013
|
-
ctx.params,
|
|
2014
|
-
mode,
|
|
2015
|
-
fieldType,
|
|
2016
|
-
ctx.dialect
|
|
2017
|
-
);
|
|
2018
1741
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2026
|
-
const fallback = base.length > 0 ? base : "_t";
|
|
2027
|
-
const lowered = fallback.toLowerCase();
|
|
2028
|
-
return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
1742
|
+
function handleArrayEquals(expr, val, params, cast, dialect) {
|
|
1743
|
+
if (val === void 0) return "";
|
|
1744
|
+
if (isEmptyArray(val)) {
|
|
1745
|
+
return arrayIsEmpty(expr, dialect);
|
|
1746
|
+
}
|
|
1747
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1748
|
+
return arrayEquals(expr, placeholder, cast, dialect);
|
|
2029
1749
|
}
|
|
2030
|
-
function
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
const maxLen = 63;
|
|
2043
|
-
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2044
|
-
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2045
|
-
const alias = `${trimmedBase}${suffix}`;
|
|
2046
|
-
counter += 1;
|
|
2047
|
-
if (usedAliases.has(alias)) {
|
|
2048
|
-
throw new Error(
|
|
2049
|
-
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
2050
|
-
);
|
|
2051
|
-
}
|
|
2052
|
-
usedAliases.add(alias);
|
|
2053
|
-
return alias;
|
|
1750
|
+
function handleArrayNot(expr, val, params, cast, dialect) {
|
|
1751
|
+
if (val === void 0) return "";
|
|
1752
|
+
let target = val;
|
|
1753
|
+
if (isPlainObject(val)) {
|
|
1754
|
+
const entries = Object.entries(val).filter(([, v]) => v !== void 0);
|
|
1755
|
+
if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
|
|
1756
|
+
target = entries[0][1];
|
|
1757
|
+
} else {
|
|
1758
|
+
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1759
|
+
operator: Ops.NOT,
|
|
1760
|
+
value: val
|
|
1761
|
+
});
|
|
2054
1762
|
}
|
|
2055
|
-
};
|
|
2056
|
-
}
|
|
2057
|
-
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
2058
|
-
function assertSameLength(params, mappings) {
|
|
2059
|
-
if (params.length !== mappings.length) {
|
|
2060
|
-
throw new Error(
|
|
2061
|
-
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
2062
|
-
);
|
|
2063
1763
|
}
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
if (!Number.isInteger(index) || index < 1) {
|
|
2067
|
-
throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
|
|
1764
|
+
if (target === null) {
|
|
1765
|
+
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
2068
1766
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
const expected = mappingsLength + 1;
|
|
2072
|
-
if (nextIndex !== expected) {
|
|
2073
|
-
throw new Error(
|
|
2074
|
-
`CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
|
|
2075
|
-
);
|
|
1767
|
+
if (isEmptyArray(target)) {
|
|
1768
|
+
return arrayIsNotEmpty(expr, dialect);
|
|
2076
1769
|
}
|
|
1770
|
+
const placeholder = buildArrayParam(target, params, dialect);
|
|
1771
|
+
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
2077
1772
|
}
|
|
2078
|
-
function
|
|
2079
|
-
if (
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
1773
|
+
function handleArrayHas(expr, val, params, cast, dialect) {
|
|
1774
|
+
if (val === void 0) return "";
|
|
1775
|
+
if (val === null) {
|
|
1776
|
+
throw createError(`has requires scalar value`, {
|
|
1777
|
+
operator: Ops.HAS,
|
|
1778
|
+
value: val
|
|
1779
|
+
});
|
|
2083
1780
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
throw new Error(
|
|
2090
|
-
`CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
|
|
2091
|
-
);
|
|
1781
|
+
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1782
|
+
throw createError(`has requires scalar value (single element), not array`, {
|
|
1783
|
+
operator: Ops.HAS,
|
|
1784
|
+
value: val
|
|
1785
|
+
});
|
|
2092
1786
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
1787
|
+
if (isPlainObject(val)) {
|
|
1788
|
+
throw createError(`has requires scalar value`, {
|
|
1789
|
+
operator: Ops.HAS,
|
|
1790
|
+
value: val
|
|
1791
|
+
});
|
|
2098
1792
|
}
|
|
2099
|
-
|
|
1793
|
+
const placeholder = params.addAuto(val);
|
|
1794
|
+
return arrayContains(expr, placeholder, cast, dialect);
|
|
2100
1795
|
}
|
|
2101
|
-
function
|
|
2102
|
-
if (
|
|
2103
|
-
|
|
1796
|
+
function handleArrayHasSome(expr, val, params, cast, dialect) {
|
|
1797
|
+
if (val === void 0) return "";
|
|
1798
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1799
|
+
const placeholder2 = params.addAuto(val);
|
|
1800
|
+
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
2104
1801
|
}
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
if (typeof m.dynamicName === "string") {
|
|
2111
|
-
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2112
|
-
assertUniqueDynamicName(dn, seenDynamic);
|
|
1802
|
+
if (!Array.isArray(val)) {
|
|
1803
|
+
throw createError(`hasSome requires array value`, {
|
|
1804
|
+
operator: Ops.HAS_SOME,
|
|
1805
|
+
value: val
|
|
1806
|
+
});
|
|
2113
1807
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
1808
|
+
if (val.length > LIMITS.MAX_ARRAY_SIZE) {
|
|
1809
|
+
throw createError(
|
|
1810
|
+
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1811
|
+
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1812
|
+
);
|
|
2119
1813
|
}
|
|
1814
|
+
if (val.length === 0) return "0=1";
|
|
1815
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1816
|
+
const placeholder = params.add(paramValue);
|
|
1817
|
+
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
2120
1818
|
}
|
|
2121
|
-
function
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
validateMappings(mappings);
|
|
2126
|
-
assertNextIndexMatches(mappings.length, index);
|
|
1819
|
+
function handleArrayHasEvery(expr, val, params, cast, dialect) {
|
|
1820
|
+
if (val === void 0) return "";
|
|
1821
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1822
|
+
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
2127
1823
|
}
|
|
2128
|
-
function
|
|
2129
|
-
if (
|
|
2130
|
-
|
|
1824
|
+
function handleArrayIsEmpty(expr, val, dialect) {
|
|
1825
|
+
if (typeof val !== "boolean") {
|
|
1826
|
+
throw createError(`isEmpty requires boolean value`, {
|
|
1827
|
+
operator: Ops.IS_EMPTY,
|
|
1828
|
+
value: val
|
|
1829
|
+
});
|
|
2131
1830
|
}
|
|
2132
|
-
return
|
|
1831
|
+
return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
|
|
2133
1832
|
}
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
1833
|
+
|
|
1834
|
+
// src/builder/where/operators-json.ts
|
|
1835
|
+
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1836
|
+
function validateJsonPathSegments(segments) {
|
|
1837
|
+
for (const segment of segments) {
|
|
1838
|
+
if (typeof segment !== "string") {
|
|
1839
|
+
throw createError("JSON path segments must be strings", {
|
|
1840
|
+
operator: Ops.PATH,
|
|
1841
|
+
value: segment
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1845
|
+
throw createError(
|
|
1846
|
+
`Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
|
|
1847
|
+
{ operator: Ops.PATH, value: segment }
|
|
1848
|
+
);
|
|
2142
1849
|
}
|
|
2143
1850
|
}
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
function normalizeDynamicName(dynamicName) {
|
|
2152
|
-
const dn = dynamicName.trim();
|
|
2153
|
-
if (dn.length === 0) {
|
|
2154
|
-
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2155
|
-
}
|
|
2156
|
-
return dn;
|
|
1851
|
+
}
|
|
1852
|
+
function buildJsonOperator(expr, op, val, params, dialect) {
|
|
1853
|
+
if (val === void 0) return "";
|
|
1854
|
+
if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
|
|
1855
|
+
return handleJsonPath(expr, val, params, dialect);
|
|
2157
1856
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
1857
|
+
const jsonWildcards = {
|
|
1858
|
+
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1859
|
+
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1860
|
+
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1861
|
+
};
|
|
1862
|
+
if (op in jsonWildcards) {
|
|
1863
|
+
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
2160
1864
|
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
}
|
|
2167
|
-
const position = index;
|
|
2168
|
-
dynamicNameToIndex.set(dn, position);
|
|
2169
|
-
params.push(void 0);
|
|
2170
|
-
mappings.push({ index: position, dynamicName: dn });
|
|
2171
|
-
index++;
|
|
2172
|
-
return format(position);
|
|
1865
|
+
throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
|
|
1866
|
+
}
|
|
1867
|
+
function handleJsonPath(expr, val, params, dialect) {
|
|
1868
|
+
const v = val;
|
|
1869
|
+
if (!Array.isArray(v.path)) {
|
|
1870
|
+
throw createError("JSON path must be an array", { operator: Ops.PATH });
|
|
2173
1871
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
const normalizedValue = normalizeValue(value);
|
|
2177
|
-
params.push(normalizedValue);
|
|
2178
|
-
mappings.push({ index: position, value: normalizedValue });
|
|
2179
|
-
index++;
|
|
2180
|
-
return format(position);
|
|
1872
|
+
if (v.path.length === 0) {
|
|
1873
|
+
throw createError("JSON path cannot be empty", { operator: Ops.PATH });
|
|
2181
1874
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
1875
|
+
validateJsonPathSegments(v.path);
|
|
1876
|
+
const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
|
|
1877
|
+
const rawOps = [
|
|
1878
|
+
["=", v.equals],
|
|
1879
|
+
[">", v.gt],
|
|
1880
|
+
[">=", v.gte],
|
|
1881
|
+
["<", v.lt],
|
|
1882
|
+
["<=", v.lte]
|
|
1883
|
+
];
|
|
1884
|
+
const ops = rawOps.filter(
|
|
1885
|
+
([, value]) => value !== void 0
|
|
1886
|
+
);
|
|
1887
|
+
if (ops.length === 0) {
|
|
1888
|
+
throw createError("JSON path query missing comparison operator", {
|
|
1889
|
+
operator: Ops.PATH
|
|
1890
|
+
});
|
|
2185
1891
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
1892
|
+
const parts = [];
|
|
1893
|
+
for (const [sqlOp, value] of ops) {
|
|
1894
|
+
if (value === null) {
|
|
1895
|
+
const base2 = jsonExtractText(expr, pathExpr, dialect);
|
|
1896
|
+
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1897
|
+
continue;
|
|
2190
1898
|
}
|
|
2191
|
-
|
|
1899
|
+
const valPh = params.add(value);
|
|
1900
|
+
const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
|
|
1901
|
+
parts.push(`${base} ${sqlOp} ${valPh}`);
|
|
2192
1902
|
}
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
1903
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1904
|
+
}
|
|
1905
|
+
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1906
|
+
if (!isNotNullish(val)) {
|
|
1907
|
+
throw createError(`JSON string operator requires non-null value`, {
|
|
1908
|
+
operator: op,
|
|
1909
|
+
value: val
|
|
2198
1910
|
});
|
|
2199
1911
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
return index;
|
|
2206
|
-
}
|
|
2207
|
-
};
|
|
2208
|
-
}
|
|
2209
|
-
function createParamStore(startIndex = 1) {
|
|
2210
|
-
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2211
|
-
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
1912
|
+
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1913
|
+
throw createError(`JSON string operator requires scalar value`, {
|
|
1914
|
+
operator: op,
|
|
1915
|
+
value: val
|
|
1916
|
+
});
|
|
2212
1917
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
1918
|
+
const strVal = String(val);
|
|
1919
|
+
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1920
|
+
throw createError(
|
|
1921
|
+
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1922
|
+
{ operator: op }
|
|
2216
1923
|
);
|
|
2217
1924
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
validateState([...existingParams], [...existingMappings], nextIndex);
|
|
2222
|
-
return createStoreInternal(
|
|
2223
|
-
nextIndex,
|
|
2224
|
-
[...existingParams],
|
|
2225
|
-
[...existingMappings]
|
|
2226
|
-
);
|
|
1925
|
+
const placeholder = params.add(wildcards[op](strVal));
|
|
1926
|
+
const jsonText = jsonToText(expr, dialect);
|
|
1927
|
+
return caseInsensitiveLike(jsonText, placeholder, dialect);
|
|
2227
1928
|
}
|
|
2228
1929
|
|
|
2229
|
-
// src/builder/
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
return
|
|
2233
|
-
clause: clause || DEFAULT_WHERE_CLAUSE,
|
|
2234
|
-
joins: Object.freeze([...joins]),
|
|
2235
|
-
params: snapshot.params,
|
|
2236
|
-
paramMappings: snapshot.mappings,
|
|
2237
|
-
nextParamIndex: snapshot.index
|
|
2238
|
-
});
|
|
1930
|
+
// src/builder/where/relations.ts
|
|
1931
|
+
var NO_JOINS = Object.freeze([]);
|
|
1932
|
+
function isListRelation(fieldType) {
|
|
1933
|
+
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
2239
1934
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
model: options.model,
|
|
2249
|
-
schemaModels: (_b = options.schemaModels) != null ? _b : [],
|
|
2250
|
-
path: (_c = options.path) != null ? _c : [],
|
|
2251
|
-
isSubquery: (_d = options.isSubquery) != null ? _d : false,
|
|
2252
|
-
aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
|
|
2253
|
-
dialect,
|
|
2254
|
-
params,
|
|
2255
|
-
depth: 0
|
|
2256
|
-
};
|
|
2257
|
-
const result = whereBuilderInstance.build(where, ctx);
|
|
2258
|
-
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2259
|
-
if (!options.isSubquery) {
|
|
2260
|
-
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2261
|
-
(m) => parseInt(m[1], 10)
|
|
2262
|
-
);
|
|
2263
|
-
if (nums.length > 0) {
|
|
2264
|
-
const min = Math.min(...nums);
|
|
2265
|
-
if (min === 1) {
|
|
2266
|
-
validateParamConsistency(publicResult.clause, publicResult.params);
|
|
2267
|
-
} else {
|
|
2268
|
-
validateParamConsistencyFragment(
|
|
2269
|
-
publicResult.clause,
|
|
2270
|
-
publicResult.params
|
|
2271
|
-
);
|
|
2272
|
-
}
|
|
1935
|
+
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
|
|
1936
|
+
const isLocal = field.isForeignKeyLocal === true;
|
|
1937
|
+
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1938
|
+
if (isLocal) {
|
|
1939
|
+
if (fkFields.length === 0) {
|
|
1940
|
+
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1941
|
+
field: field.name
|
|
1942
|
+
});
|
|
2273
1943
|
}
|
|
1944
|
+
const parts = fkFields.map((fk) => {
|
|
1945
|
+
const safe = fk.replace(/"/g, '""');
|
|
1946
|
+
const expr = `${parentAlias}."${safe}"`;
|
|
1947
|
+
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1948
|
+
});
|
|
1949
|
+
if (parts.length === 1) return parts[0];
|
|
1950
|
+
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
2274
1951
|
}
|
|
2275
|
-
|
|
2276
|
-
}
|
|
2277
|
-
function normalizeIntLike(name, v, opts = {}) {
|
|
2278
|
-
var _a, _b;
|
|
2279
|
-
if (!isNotNullish(v)) return void 0;
|
|
2280
|
-
if (schemaParser.isDynamicParameter(v)) return v;
|
|
2281
|
-
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
2282
|
-
throw new Error(`${name} must be an integer`);
|
|
2283
|
-
}
|
|
2284
|
-
const min = (_a = opts.min) != null ? _a : 0;
|
|
2285
|
-
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
2286
|
-
if (!allowZero && v === 0) {
|
|
2287
|
-
throw new Error(`${name} must be > 0`);
|
|
2288
|
-
}
|
|
2289
|
-
if (v < min) {
|
|
2290
|
-
throw new Error(`${name} must be >= ${min}`);
|
|
2291
|
-
}
|
|
2292
|
-
if (typeof opts.max === "number" && v > opts.max) {
|
|
2293
|
-
throw new Error(`${name} must be <= ${opts.max}`);
|
|
2294
|
-
}
|
|
2295
|
-
return v;
|
|
1952
|
+
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
|
|
1953
|
+
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
2296
1954
|
}
|
|
2297
|
-
function
|
|
2298
|
-
const
|
|
2299
|
-
|
|
2300
|
-
if (s.length === 0) return dn;
|
|
2301
|
-
return `${s}:${dn}`;
|
|
1955
|
+
function buildToOneExistsMatch(relTable, relAlias, join3, sub) {
|
|
1956
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1957
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
2302
1958
|
}
|
|
2303
|
-
function
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
return params.add(void 0, scopeName(scope, dn));
|
|
2307
|
-
}
|
|
2308
|
-
return params.add(value);
|
|
1959
|
+
function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
|
|
1960
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1961
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
2309
1962
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
1963
|
+
function buildListRelationFilters(args) {
|
|
1964
|
+
const {
|
|
1965
|
+
fieldName,
|
|
1966
|
+
value,
|
|
1967
|
+
ctx,
|
|
1968
|
+
whereBuilder,
|
|
1969
|
+
relModel,
|
|
1970
|
+
relTable,
|
|
1971
|
+
relAlias,
|
|
1972
|
+
join: join3
|
|
1973
|
+
} = args;
|
|
1974
|
+
const noneValue = value[RelationFilters.NONE];
|
|
1975
|
+
if (noneValue !== void 0 && noneValue !== null) {
|
|
1976
|
+
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1977
|
+
alias: relAlias,
|
|
1978
|
+
model: relModel,
|
|
1979
|
+
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1980
|
+
isSubquery: true,
|
|
1981
|
+
depth: ctx.depth + 1
|
|
1982
|
+
}));
|
|
1983
|
+
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1984
|
+
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1985
|
+
if (canOptimize) {
|
|
1986
|
+
const checkField = relModel.fields.find(
|
|
1987
|
+
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1988
|
+
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1989
|
+
if (checkField) {
|
|
1990
|
+
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
|
|
1991
|
+
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1992
|
+
return Object.freeze({
|
|
1993
|
+
clause: whereClause,
|
|
1994
|
+
joins: [leftJoinSql]
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
2338
1998
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
1999
|
+
const filters = [
|
|
2000
|
+
{
|
|
2001
|
+
key: RelationFilters.SOME,
|
|
2002
|
+
wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${c})`
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
key: RelationFilters.EVERY,
|
|
2006
|
+
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}))`
|
|
2007
|
+
},
|
|
2008
|
+
{
|
|
2009
|
+
key: RelationFilters.NONE,
|
|
2010
|
+
wrap: (c, j) => {
|
|
2011
|
+
const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
|
|
2012
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3}${condition})`;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
];
|
|
2016
|
+
const clauses = [];
|
|
2017
|
+
for (const { key, wrap } of filters) {
|
|
2018
|
+
const raw = value[key];
|
|
2019
|
+
if (raw === void 0 || raw === null) continue;
|
|
2020
|
+
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
2021
|
+
alias: relAlias,
|
|
2022
|
+
model: relModel,
|
|
2023
|
+
path: [...ctx.path, fieldName, key],
|
|
2024
|
+
isSubquery: true,
|
|
2025
|
+
depth: ctx.depth + 1
|
|
2026
|
+
}));
|
|
2027
|
+
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
2028
|
+
clauses.push(wrap(sub.clause, j));
|
|
2349
2029
|
}
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2030
|
+
if (clauses.length === 0) {
|
|
2031
|
+
throw createError(
|
|
2032
|
+
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
2033
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
2034
|
+
);
|
|
2353
2035
|
}
|
|
2354
|
-
return
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
return orderBy.map((item) => {
|
|
2358
|
-
const [k, v] = assertSingleFieldObject(item);
|
|
2359
|
-
return { [k]: flipValue(v) };
|
|
2036
|
+
return Object.freeze({
|
|
2037
|
+
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
2038
|
+
joins: NO_JOINS
|
|
2360
2039
|
});
|
|
2361
|
-
}
|
|
2362
|
-
|
|
2363
|
-
const
|
|
2364
|
-
|
|
2365
|
-
|
|
2040
|
+
}
|
|
2041
|
+
function buildToOneRelationFilters(args) {
|
|
2042
|
+
const {
|
|
2043
|
+
fieldName,
|
|
2044
|
+
value,
|
|
2045
|
+
ctx,
|
|
2046
|
+
whereBuilder,
|
|
2047
|
+
field,
|
|
2048
|
+
relModel,
|
|
2049
|
+
relTable,
|
|
2050
|
+
relAlias,
|
|
2051
|
+
join: join3
|
|
2052
|
+
} = args;
|
|
2053
|
+
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
2054
|
+
if (hasSomeEveryNone) {
|
|
2055
|
+
throw createError(
|
|
2056
|
+
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
2057
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
|
|
2061
|
+
const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
|
|
2062
|
+
let filterKey;
|
|
2063
|
+
let filterVal;
|
|
2064
|
+
if (hasIs) {
|
|
2065
|
+
filterKey = "is";
|
|
2066
|
+
filterVal = value.is;
|
|
2067
|
+
} else if (hasIsNot) {
|
|
2068
|
+
filterKey = "isNot";
|
|
2069
|
+
filterVal = value.isNot;
|
|
2070
|
+
} else {
|
|
2071
|
+
filterKey = "is";
|
|
2072
|
+
filterVal = value;
|
|
2073
|
+
}
|
|
2074
|
+
if (filterVal === void 0) {
|
|
2075
|
+
return Object.freeze({
|
|
2076
|
+
clause: DEFAULT_WHERE_CLAUSE,
|
|
2077
|
+
joins: NO_JOINS
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
if (filterVal === null) {
|
|
2081
|
+
const wantNull = filterKey === "is";
|
|
2082
|
+
const clause2 = buildToOneNullCheck(
|
|
2083
|
+
field,
|
|
2084
|
+
ctx.alias,
|
|
2085
|
+
relTable,
|
|
2086
|
+
relAlias,
|
|
2087
|
+
join3,
|
|
2088
|
+
wantNull
|
|
2089
|
+
);
|
|
2090
|
+
return Object.freeze({
|
|
2091
|
+
clause: clause2,
|
|
2092
|
+
joins: NO_JOINS
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
if (!isPlainObject(filterVal)) {
|
|
2096
|
+
throw createError(
|
|
2097
|
+
`Relation '${fieldName}' filter must be an object or null`,
|
|
2098
|
+
{
|
|
2099
|
+
field: fieldName,
|
|
2100
|
+
path: ctx.path,
|
|
2101
|
+
modelName: ctx.model.name,
|
|
2102
|
+
value: filterVal
|
|
2103
|
+
}
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
2106
|
+
const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
|
|
2107
|
+
alias: relAlias,
|
|
2108
|
+
model: relModel,
|
|
2109
|
+
path: [...ctx.path, fieldName, filterKey],
|
|
2110
|
+
isSubquery: true,
|
|
2111
|
+
depth: ctx.depth + 1
|
|
2112
|
+
}));
|
|
2113
|
+
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
|
|
2114
|
+
return Object.freeze({
|
|
2115
|
+
clause,
|
|
2116
|
+
joins: NO_JOINS
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
function ensureRelationFilterObject(fieldName, value, ctx) {
|
|
2120
|
+
if (!isPlainObject(value)) {
|
|
2121
|
+
throw createError(`Relation filter '${fieldName}' must be an object`, {
|
|
2122
|
+
path: [...ctx.path, fieldName],
|
|
2123
|
+
field: fieldName,
|
|
2124
|
+
modelName: ctx.model.name,
|
|
2125
|
+
value
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
function buildRelation(fieldName, value, ctx, whereBuilder) {
|
|
2130
|
+
const field = ctx.model.fields.find((f) => f.name === fieldName);
|
|
2131
|
+
if (!isValidRelationField(field)) {
|
|
2132
|
+
throw createError(`Invalid relation '${fieldName}'`, {
|
|
2133
|
+
field: fieldName,
|
|
2134
|
+
path: ctx.path,
|
|
2135
|
+
modelName: ctx.model.name
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
|
|
2139
|
+
if (!isNotNullish(relModel)) {
|
|
2140
|
+
throw createError(
|
|
2141
|
+
`Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
|
|
2142
|
+
{
|
|
2143
|
+
field: fieldName,
|
|
2144
|
+
path: ctx.path,
|
|
2145
|
+
modelName: ctx.model.name
|
|
2146
|
+
}
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
const relTable = buildTableReference(
|
|
2150
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
2151
|
+
relModel.tableName,
|
|
2152
|
+
ctx.dialect
|
|
2153
|
+
);
|
|
2154
|
+
const relAlias = ctx.aliasGen.next(fieldName);
|
|
2155
|
+
const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
2156
|
+
const args = {
|
|
2157
|
+
fieldName,
|
|
2158
|
+
value,
|
|
2159
|
+
ctx,
|
|
2160
|
+
whereBuilder,
|
|
2161
|
+
field,
|
|
2162
|
+
relModel,
|
|
2163
|
+
relTable,
|
|
2164
|
+
relAlias,
|
|
2165
|
+
join: join3
|
|
2166
|
+
};
|
|
2167
|
+
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
2168
|
+
return buildToOneRelationFilters(args);
|
|
2169
|
+
}
|
|
2170
|
+
function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
|
|
2171
|
+
ensureRelationFilterObject(fieldName, value, ctx);
|
|
2172
|
+
return buildRelation(fieldName, value, ctx, whereBuilder);
|
|
2173
|
+
}
|
|
2174
|
+
function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
2175
|
+
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// src/builder/shared/validators/field-validators.ts
|
|
2179
|
+
function assertFieldExists(name, model, path) {
|
|
2180
|
+
const field = model.fields.find((f) => f.name === name);
|
|
2181
|
+
if (!isNotNullish(field)) {
|
|
2182
|
+
throw createError(`Field '${name}' does not exist on '${model.name}'`, {
|
|
2183
|
+
field: name,
|
|
2184
|
+
path,
|
|
2185
|
+
modelName: model.name,
|
|
2186
|
+
availableFields: model.fields.map((f) => f.name)
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
return field;
|
|
2190
|
+
}
|
|
2191
|
+
function assertValidOperator(fieldName, op, fieldType, path, modelName) {
|
|
2192
|
+
if (!isNotNullish(fieldType)) return;
|
|
2193
|
+
const ARRAY_OPS = /* @__PURE__ */ new Set([
|
|
2194
|
+
Ops.HAS,
|
|
2195
|
+
Ops.HAS_SOME,
|
|
2196
|
+
Ops.HAS_EVERY,
|
|
2197
|
+
Ops.IS_EMPTY
|
|
2198
|
+
]);
|
|
2199
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2200
|
+
Ops.PATH,
|
|
2201
|
+
Ops.STRING_CONTAINS,
|
|
2202
|
+
Ops.STRING_STARTS_WITH,
|
|
2203
|
+
Ops.STRING_ENDS_WITH
|
|
2204
|
+
]);
|
|
2205
|
+
const isArrayOp = ARRAY_OPS.has(op);
|
|
2206
|
+
const isFieldArray = isArrayType(fieldType);
|
|
2207
|
+
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
2208
|
+
if (arrayOpMismatch) {
|
|
2209
|
+
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
2210
|
+
operator: op,
|
|
2211
|
+
field: fieldName,
|
|
2212
|
+
path,
|
|
2213
|
+
modelName
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
const isJsonOp = JSON_OPS.has(op);
|
|
2217
|
+
const isFieldJson = isJsonType(fieldType);
|
|
2218
|
+
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
2219
|
+
if (jsonOpMismatch) {
|
|
2220
|
+
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
2221
|
+
operator: op,
|
|
2222
|
+
field: fieldName,
|
|
2223
|
+
path,
|
|
2224
|
+
modelName
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// src/builder/where/builder.ts
|
|
2230
|
+
var WhereBuilder = class {
|
|
2231
|
+
build(where, ctx) {
|
|
2232
|
+
if (!isPlainObject(where)) {
|
|
2233
|
+
throw createError("where must be an object", {
|
|
2234
|
+
path: ctx.path,
|
|
2235
|
+
modelName: ctx.model.name
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
return buildWhereInternal(where, ctx, this);
|
|
2366
2239
|
}
|
|
2367
|
-
return out;
|
|
2368
2240
|
};
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2241
|
+
var MAX_QUERY_DEPTH = 50;
|
|
2242
|
+
var EMPTY_JOINS = Object.freeze([]);
|
|
2243
|
+
var whereBuilderInstance = new WhereBuilder();
|
|
2244
|
+
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
2245
|
+
return Object.freeze({ clause, joins });
|
|
2246
|
+
}
|
|
2247
|
+
function dedupePreserveOrder(items) {
|
|
2248
|
+
if (items.length <= 1) return Object.freeze([...items]);
|
|
2249
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2250
|
+
const out = [];
|
|
2251
|
+
for (const s of items) {
|
|
2252
|
+
if (!seen.has(s)) {
|
|
2253
|
+
seen.add(s);
|
|
2254
|
+
out.push(s);
|
|
2255
|
+
}
|
|
2373
2256
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2257
|
+
return Object.freeze(out);
|
|
2258
|
+
}
|
|
2259
|
+
function appendResult(result, clauses, allJoins) {
|
|
2260
|
+
if (isValidWhereClause(result.clause)) clauses.push(result.clause);
|
|
2261
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2262
|
+
}
|
|
2263
|
+
function asLogicalOperator(key) {
|
|
2264
|
+
if (key === LogicalOps.AND) return "AND";
|
|
2265
|
+
if (key === LogicalOps.OR) return "OR";
|
|
2266
|
+
if (key === LogicalOps.NOT) return "NOT";
|
|
2267
|
+
return null;
|
|
2268
|
+
}
|
|
2269
|
+
function nextContext(ctx) {
|
|
2270
|
+
return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
|
|
2271
|
+
}
|
|
2272
|
+
function buildRelationFilter(fieldName, value, ctx, builder) {
|
|
2273
|
+
const ctx2 = nextContext(ctx);
|
|
2274
|
+
if (ctx.isSubquery) {
|
|
2275
|
+
return buildNestedRelation(fieldName, value, ctx2, builder);
|
|
2376
2276
|
}
|
|
2377
|
-
|
|
2277
|
+
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
2278
|
+
}
|
|
2279
|
+
function buildWhereEntry(key, value, ctx, builder) {
|
|
2280
|
+
const op = asLogicalOperator(key);
|
|
2281
|
+
if (op) return buildLogical(op, value, ctx, builder);
|
|
2282
|
+
if (isRelationField(key, ctx.model)) {
|
|
2283
|
+
if (!isPlainObject(value)) {
|
|
2284
|
+
throw createError(`Relation filter '${key}' must be an object`, {
|
|
2285
|
+
path: [...ctx.path, key],
|
|
2286
|
+
field: key,
|
|
2287
|
+
modelName: ctx.model.name,
|
|
2288
|
+
value
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
return buildRelationFilter(key, value, ctx, builder);
|
|
2292
|
+
}
|
|
2293
|
+
return buildScalarField(key, value, ctx);
|
|
2294
|
+
}
|
|
2295
|
+
function buildWhereInternal(where, ctx, builder) {
|
|
2296
|
+
if (ctx.depth > MAX_QUERY_DEPTH) {
|
|
2297
|
+
throw createError(
|
|
2298
|
+
`Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
|
|
2299
|
+
{ path: ctx.path, modelName: ctx.model.name }
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
if (isEmptyWhere(where)) {
|
|
2303
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2304
|
+
}
|
|
2305
|
+
const allJoins = [];
|
|
2306
|
+
const clauses = [];
|
|
2307
|
+
for (const [key, value] of Object.entries(where)) {
|
|
2308
|
+
if (value === void 0) continue;
|
|
2309
|
+
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2310
|
+
appendResult(result, clauses, allJoins);
|
|
2311
|
+
}
|
|
2312
|
+
const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2313
|
+
return freezeResult(finalClause, dedupePreserveOrder(allJoins));
|
|
2314
|
+
}
|
|
2315
|
+
function normalizeLogicalValue(operator, value, ctx) {
|
|
2316
|
+
if (Array.isArray(value)) {
|
|
2317
|
+
const out = [];
|
|
2318
|
+
for (let i = 0; i < value.length; i++) {
|
|
2319
|
+
const v = value[i];
|
|
2320
|
+
if (v === void 0) continue;
|
|
2321
|
+
if (!isPlainObject(v)) {
|
|
2322
|
+
throw createError(`${operator} entries must be objects`, {
|
|
2323
|
+
path: [...ctx.path, operator, String(i)],
|
|
2324
|
+
modelName: ctx.model.name,
|
|
2325
|
+
value: v
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
out.push(v);
|
|
2329
|
+
}
|
|
2330
|
+
return out;
|
|
2331
|
+
}
|
|
2332
|
+
if (isPlainObject(value)) {
|
|
2333
|
+
return [value];
|
|
2334
|
+
}
|
|
2335
|
+
throw createError(`${operator} must be an object or array of objects`, {
|
|
2336
|
+
path: [...ctx.path, operator],
|
|
2337
|
+
modelName: ctx.model.name,
|
|
2338
|
+
value
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
function collectLogicalParts(operator, conditions, ctx, builder) {
|
|
2342
|
+
const allJoins = [];
|
|
2343
|
+
const clauses = [];
|
|
2344
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
2345
|
+
const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
|
|
2346
|
+
path: [...ctx.path, operator, String(i)],
|
|
2347
|
+
depth: ctx.depth + 1
|
|
2348
|
+
}));
|
|
2349
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2350
|
+
if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
|
|
2351
|
+
clauses.push(`(${result.clause})`);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
return {
|
|
2355
|
+
joins: dedupePreserveOrder(allJoins),
|
|
2356
|
+
clauses: Object.freeze(clauses)
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
function buildLogicalClause(operator, clauses) {
|
|
2360
|
+
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
2361
|
+
if (operator === "NOT") {
|
|
2362
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
2363
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
2364
|
+
}
|
|
2365
|
+
return clauses.join(` ${operator} `);
|
|
2366
|
+
}
|
|
2367
|
+
function buildLogical(operator, value, ctx, builder) {
|
|
2368
|
+
const conditions = normalizeLogicalValue(operator, value, ctx);
|
|
2369
|
+
if (conditions.length === 0) {
|
|
2370
|
+
const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
|
|
2371
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2372
|
+
}
|
|
2373
|
+
const { joins, clauses } = collectLogicalParts(
|
|
2374
|
+
operator,
|
|
2375
|
+
conditions,
|
|
2376
|
+
ctx,
|
|
2377
|
+
builder
|
|
2378
|
+
);
|
|
2379
|
+
const clause = buildLogicalClause(operator, clauses);
|
|
2380
|
+
return freezeResult(clause, joins);
|
|
2381
|
+
}
|
|
2382
|
+
function buildScalarField(fieldName, value, ctx) {
|
|
2383
|
+
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
2384
|
+
const expr = col(ctx.alias, fieldName, ctx.model);
|
|
2385
|
+
if (value === null) {
|
|
2386
|
+
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
2387
|
+
}
|
|
2388
|
+
if (isPlainObject(value)) {
|
|
2389
|
+
const mode = value.mode;
|
|
2390
|
+
const ops = Object.entries(value).filter(
|
|
2391
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
2392
|
+
);
|
|
2393
|
+
if (ops.length === 0) {
|
|
2394
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2395
|
+
}
|
|
2396
|
+
const parts = [];
|
|
2397
|
+
for (const [op, val] of ops) {
|
|
2398
|
+
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
2399
|
+
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
2400
|
+
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
2401
|
+
}
|
|
2402
|
+
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2403
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2404
|
+
}
|
|
2405
|
+
const clause = buildOperator(
|
|
2406
|
+
expr,
|
|
2407
|
+
Ops.EQUALS,
|
|
2408
|
+
value,
|
|
2409
|
+
ctx,
|
|
2410
|
+
void 0,
|
|
2411
|
+
field.type
|
|
2412
|
+
);
|
|
2413
|
+
return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2378
2414
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
return {
|
|
2383
|
-
[field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
2384
|
-
};
|
|
2385
|
-
});
|
|
2386
|
-
};
|
|
2387
|
-
function normalizeOrderByInput(orderBy, parseValue) {
|
|
2388
|
-
if (!isNotNullish(orderBy)) return [];
|
|
2389
|
-
if (Array.isArray(orderBy)) {
|
|
2390
|
-
const pairs = orderBy.map(assertSingleFieldObject);
|
|
2391
|
-
return normalizePairs(pairs, parseValue);
|
|
2415
|
+
function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
2416
|
+
if (fieldType && isArrayType(fieldType)) {
|
|
2417
|
+
return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
|
|
2392
2418
|
}
|
|
2393
|
-
if (
|
|
2394
|
-
|
|
2419
|
+
if (fieldType && isJsonType(fieldType)) {
|
|
2420
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2421
|
+
Ops.PATH,
|
|
2422
|
+
Ops.STRING_CONTAINS,
|
|
2423
|
+
Ops.STRING_STARTS_WITH,
|
|
2424
|
+
Ops.STRING_ENDS_WITH
|
|
2425
|
+
]);
|
|
2426
|
+
if (JSON_OPS.has(op)) {
|
|
2427
|
+
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2428
|
+
}
|
|
2395
2429
|
}
|
|
2396
|
-
|
|
2430
|
+
return buildScalarOperator(
|
|
2431
|
+
expr,
|
|
2432
|
+
op,
|
|
2433
|
+
val,
|
|
2434
|
+
ctx.params,
|
|
2435
|
+
mode,
|
|
2436
|
+
fieldType,
|
|
2437
|
+
ctx.dialect
|
|
2438
|
+
);
|
|
2397
2439
|
}
|
|
2398
2440
|
|
|
2399
|
-
// src/builder/
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
const
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
const s = String(raw).toLowerCase();
|
|
2409
|
-
if (s === "first" || s === "last") return s;
|
|
2410
|
-
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
2411
|
-
}
|
|
2412
|
-
function requireOrderByObject(v, errorPrefix) {
|
|
2413
|
-
if (!isPlainObject(v) || !("sort" in v)) {
|
|
2414
|
-
throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
|
|
2415
|
-
}
|
|
2416
|
-
return v;
|
|
2441
|
+
// src/builder/shared/alias-generator.ts
|
|
2442
|
+
function toSafeSqlIdentifier(input) {
|
|
2443
|
+
const raw = String(input);
|
|
2444
|
+
const cleaned = raw.replace(/\W/g, "_");
|
|
2445
|
+
const startsOk = /^[a-zA-Z_]/.test(cleaned);
|
|
2446
|
+
const base = startsOk ? cleaned : `_${cleaned}`;
|
|
2447
|
+
const fallback = base.length > 0 ? base : "_t";
|
|
2448
|
+
const lowered = fallback.toLowerCase();
|
|
2449
|
+
return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
2417
2450
|
}
|
|
2418
|
-
function
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2451
|
+
function createAliasGenerator(maxAliases = 1e4) {
|
|
2452
|
+
let counter = 0;
|
|
2453
|
+
const usedAliases = /* @__PURE__ */ new Set();
|
|
2454
|
+
return {
|
|
2455
|
+
next(baseName) {
|
|
2456
|
+
if (usedAliases.size >= maxAliases) {
|
|
2457
|
+
throw new Error(
|
|
2458
|
+
`Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
const base = toSafeSqlIdentifier(baseName);
|
|
2462
|
+
const suffix = `_${counter}`;
|
|
2463
|
+
const maxLen = 63;
|
|
2464
|
+
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2465
|
+
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2466
|
+
const alias = `${trimmedBase}${suffix}`;
|
|
2467
|
+
counter += 1;
|
|
2468
|
+
if (usedAliases.has(alias)) {
|
|
2469
|
+
throw new Error(
|
|
2470
|
+
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
usedAliases.add(alias);
|
|
2474
|
+
return alias;
|
|
2425
2475
|
}
|
|
2426
|
-
}
|
|
2476
|
+
};
|
|
2427
2477
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
if (
|
|
2431
|
-
|
|
2478
|
+
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
2479
|
+
function assertSameLength(params, mappings) {
|
|
2480
|
+
if (params.length !== mappings.length) {
|
|
2481
|
+
throw new Error(
|
|
2482
|
+
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
2483
|
+
);
|
|
2432
2484
|
}
|
|
2433
|
-
const obj = requireOrderByObject(v, errorPrefix);
|
|
2434
|
-
const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
|
|
2435
|
-
const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
|
|
2436
|
-
assertAllowedOrderByKeys(obj, fieldName);
|
|
2437
|
-
return { direction, nulls };
|
|
2438
2485
|
}
|
|
2439
|
-
function
|
|
2440
|
-
if (
|
|
2441
|
-
throw new Error(
|
|
2486
|
+
function assertValidNextIndex(index) {
|
|
2487
|
+
if (!Number.isInteger(index) || index < 1) {
|
|
2488
|
+
throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
|
|
2442
2489
|
}
|
|
2443
|
-
return v;
|
|
2444
2490
|
}
|
|
2445
|
-
function
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
if (n > MAX_LIMIT_OFFSET) {
|
|
2452
|
-
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
2491
|
+
function assertNextIndexMatches(mappingsLength, nextIndex) {
|
|
2492
|
+
const expected = mappingsLength + 1;
|
|
2493
|
+
if (nextIndex !== expected) {
|
|
2494
|
+
throw new Error(
|
|
2495
|
+
`CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
|
|
2496
|
+
);
|
|
2453
2497
|
}
|
|
2454
|
-
return n;
|
|
2455
|
-
}
|
|
2456
|
-
function hasNonNullishProp(v, key) {
|
|
2457
|
-
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
2458
|
-
}
|
|
2459
|
-
function normalizeIntegerOrDynamic(name, v) {
|
|
2460
|
-
if (schemaParser.isDynamicParameter(v)) return v;
|
|
2461
|
-
return normalizeFiniteInteger(name, v);
|
|
2462
2498
|
}
|
|
2463
|
-
function
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
hasSkip: false,
|
|
2469
|
-
hasTake: false,
|
|
2470
|
-
skipVal: void 0,
|
|
2471
|
-
takeVal: void 0
|
|
2472
|
-
};
|
|
2499
|
+
function assertSequentialIndex(actual, expected) {
|
|
2500
|
+
if (actual !== expected) {
|
|
2501
|
+
throw new Error(
|
|
2502
|
+
`CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
|
|
2503
|
+
);
|
|
2473
2504
|
}
|
|
2474
|
-
const obj = relArgs;
|
|
2475
|
-
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
2476
|
-
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
2477
|
-
return { hasSkip, hasTake, skipVal, takeVal };
|
|
2478
2505
|
}
|
|
2479
|
-
function
|
|
2480
|
-
|
|
2481
|
-
const
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
2487
|
-
out.push(`${c} ${dir}${nulls}`);
|
|
2488
|
-
continue;
|
|
2489
|
-
}
|
|
2490
|
-
if (isNotNullish(e.nulls)) {
|
|
2491
|
-
const isNullExpr = `(${c} IS NULL)`;
|
|
2492
|
-
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
2493
|
-
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
2494
|
-
out.push(`${c} ${dir}`);
|
|
2495
|
-
continue;
|
|
2496
|
-
}
|
|
2497
|
-
out.push(`${c} ${dir}`);
|
|
2506
|
+
function assertExactlyOneOfDynamicOrValue(m) {
|
|
2507
|
+
const hasDynamic = typeof m.dynamicName === "string";
|
|
2508
|
+
const hasStatic = m.value !== void 0;
|
|
2509
|
+
if (hasDynamic === hasStatic) {
|
|
2510
|
+
throw new Error(
|
|
2511
|
+
`CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
|
|
2512
|
+
);
|
|
2498
2513
|
}
|
|
2499
|
-
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
2500
2514
|
}
|
|
2501
|
-
function
|
|
2502
|
-
|
|
2503
|
-
|
|
2515
|
+
function normalizeDynamicNameOrThrow(dynamicName, index) {
|
|
2516
|
+
const dn = dynamicName.trim();
|
|
2517
|
+
if (dn.length === 0) {
|
|
2518
|
+
throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
|
|
2504
2519
|
}
|
|
2505
|
-
return
|
|
2520
|
+
return dn;
|
|
2506
2521
|
}
|
|
2507
|
-
function
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
const out = [...orderEntries];
|
|
2511
|
-
for (const [field] of cursorEntries) {
|
|
2512
|
-
if (!existing.has(field)) {
|
|
2513
|
-
out.push({ field, direction: "asc" });
|
|
2514
|
-
existing.set(field, out[out.length - 1]);
|
|
2515
|
-
}
|
|
2522
|
+
function assertUniqueDynamicName(dn, seen) {
|
|
2523
|
+
if (seen.has(dn)) {
|
|
2524
|
+
throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
|
|
2516
2525
|
}
|
|
2517
|
-
|
|
2526
|
+
seen.add(dn);
|
|
2518
2527
|
}
|
|
2519
|
-
function
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
const parts = [];
|
|
2526
|
-
for (const [field, value] of entries) {
|
|
2527
|
-
const c = `${cursorAlias}.${quote(field)}`;
|
|
2528
|
-
if (value === null) {
|
|
2529
|
-
parts.push(`${c} IS NULL`);
|
|
2530
|
-
continue;
|
|
2531
|
-
}
|
|
2532
|
-
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
2533
|
-
placeholdersByField.set(field, ph);
|
|
2534
|
-
parts.push(`${c} = ${ph}`);
|
|
2528
|
+
function validateMappingEntry(m, expectedIndex, seenDynamic) {
|
|
2529
|
+
assertSequentialIndex(m.index, expectedIndex);
|
|
2530
|
+
assertExactlyOneOfDynamicOrValue(m);
|
|
2531
|
+
if (typeof m.dynamicName === "string") {
|
|
2532
|
+
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2533
|
+
assertUniqueDynamicName(dn, seenDynamic);
|
|
2535
2534
|
}
|
|
2536
|
-
return {
|
|
2537
|
-
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
2538
|
-
placeholdersByField
|
|
2539
|
-
};
|
|
2540
|
-
}
|
|
2541
|
-
function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
|
|
2542
|
-
const colName = quote(field);
|
|
2543
|
-
return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
2544
2535
|
}
|
|
2545
|
-
function
|
|
2546
|
-
|
|
2536
|
+
function validateMappings(mappings) {
|
|
2537
|
+
const seenDynamic = /* @__PURE__ */ new Set();
|
|
2538
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
2539
|
+
validateMappingEntry(mappings[i], i + 1, seenDynamic);
|
|
2540
|
+
}
|
|
2547
2541
|
}
|
|
2548
|
-
function
|
|
2549
|
-
|
|
2542
|
+
function validateState(params, mappings, index) {
|
|
2543
|
+
assertSameLength(params, mappings);
|
|
2544
|
+
assertValidNextIndex(index);
|
|
2545
|
+
if (mappings.length === 0) return;
|
|
2546
|
+
validateMappings(mappings);
|
|
2547
|
+
assertNextIndexMatches(mappings.length, index);
|
|
2550
2548
|
}
|
|
2551
|
-
function
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
2549
|
+
function normalizeValue2(value) {
|
|
2550
|
+
if (value instanceof Date) {
|
|
2551
|
+
return value.toISOString();
|
|
2555
2552
|
}
|
|
2556
|
-
return
|
|
2553
|
+
return value;
|
|
2557
2554
|
}
|
|
2558
|
-
function
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2555
|
+
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2556
|
+
let index = startIndex;
|
|
2557
|
+
const params = [...initialParams];
|
|
2558
|
+
const mappings = [...initialMappings];
|
|
2559
|
+
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2560
|
+
for (const m of initialMappings) {
|
|
2561
|
+
if (typeof m.dynamicName === "string") {
|
|
2562
|
+
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2565
2563
|
}
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2564
|
+
}
|
|
2565
|
+
function assertCanAdd() {
|
|
2566
|
+
if (index > MAX_PARAM_INDEX) {
|
|
2567
|
+
throw new Error(
|
|
2568
|
+
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2569
|
+
);
|
|
2570
2570
|
}
|
|
2571
|
-
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
2572
|
-
parts.push(`${c} = ${ph}`);
|
|
2573
2571
|
}
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
const entries = [];
|
|
2579
|
-
for (const item of normalized) {
|
|
2580
|
-
for (const [field, value] of Object.entries(item)) {
|
|
2581
|
-
if (typeof value === "string") {
|
|
2582
|
-
entries.push({ field, direction: value });
|
|
2583
|
-
} else {
|
|
2584
|
-
entries.push({
|
|
2585
|
-
field,
|
|
2586
|
-
direction: value.sort,
|
|
2587
|
-
nulls: value.nulls
|
|
2588
|
-
});
|
|
2589
|
-
}
|
|
2572
|
+
function normalizeDynamicName(dynamicName) {
|
|
2573
|
+
const dn = dynamicName.trim();
|
|
2574
|
+
if (dn.length === 0) {
|
|
2575
|
+
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2590
2576
|
}
|
|
2577
|
+
return dn;
|
|
2591
2578
|
}
|
|
2592
|
-
|
|
2593
|
-
}
|
|
2594
|
-
function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
|
|
2595
|
-
var _a;
|
|
2596
|
-
const d = dialect != null ? dialect : getGlobalDialect();
|
|
2597
|
-
const cursorEntries = Object.entries(cursor);
|
|
2598
|
-
if (cursorEntries.length === 0) {
|
|
2599
|
-
throw new Error("cursor must have at least one field");
|
|
2579
|
+
function format(position) {
|
|
2580
|
+
return `$${position}`;
|
|
2600
2581
|
}
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2582
|
+
function addDynamic(dynamicName) {
|
|
2583
|
+
const dn = normalizeDynamicName(dynamicName);
|
|
2584
|
+
const existing = dynamicNameToIndex.get(dn);
|
|
2585
|
+
if (existing !== void 0) {
|
|
2586
|
+
return format(existing);
|
|
2587
|
+
}
|
|
2588
|
+
const position = index;
|
|
2589
|
+
dynamicNameToIndex.set(dn, position);
|
|
2590
|
+
params.push(void 0);
|
|
2591
|
+
mappings.push({ index: position, dynamicName: dn });
|
|
2592
|
+
index++;
|
|
2593
|
+
return format(position);
|
|
2611
2594
|
}
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
const e2 = orderEntries[i];
|
|
2629
|
-
const c2 = col(alias, e2.field, model);
|
|
2630
|
-
const v2 = cursorValueExpr(
|
|
2631
|
-
tableName,
|
|
2632
|
-
cursorAlias,
|
|
2633
|
-
cursorWhereSql,
|
|
2634
|
-
e2.field);
|
|
2635
|
-
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
2595
|
+
function addStatic(value) {
|
|
2596
|
+
const position = index;
|
|
2597
|
+
const normalizedValue = normalizeValue2(value);
|
|
2598
|
+
params.push(normalizedValue);
|
|
2599
|
+
mappings.push({ index: position, value: normalizedValue });
|
|
2600
|
+
index++;
|
|
2601
|
+
return format(position);
|
|
2602
|
+
}
|
|
2603
|
+
function add(value, dynamicName) {
|
|
2604
|
+
assertCanAdd();
|
|
2605
|
+
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2606
|
+
}
|
|
2607
|
+
function addAuto(value) {
|
|
2608
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
2609
|
+
const dynamicName = schemaParser.extractDynamicName(value);
|
|
2610
|
+
return add(void 0, dynamicName);
|
|
2636
2611
|
}
|
|
2637
|
-
|
|
2638
|
-
const c = col(alias, e.field, model);
|
|
2639
|
-
const v = cursorValueExpr(
|
|
2640
|
-
tableName,
|
|
2641
|
-
cursorAlias,
|
|
2642
|
-
cursorWhereSql,
|
|
2643
|
-
e.field);
|
|
2644
|
-
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
2645
|
-
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
2646
|
-
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
2612
|
+
return add(value);
|
|
2647
2613
|
}
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
return
|
|
2614
|
+
function snapshot() {
|
|
2615
|
+
return Object.freeze({
|
|
2616
|
+
index,
|
|
2617
|
+
params: Object.freeze([...params]),
|
|
2618
|
+
mappings: Object.freeze([...mappings])
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
return {
|
|
2622
|
+
add,
|
|
2623
|
+
addAuto,
|
|
2624
|
+
snapshot,
|
|
2625
|
+
get index() {
|
|
2626
|
+
return index;
|
|
2627
|
+
}
|
|
2628
|
+
};
|
|
2656
2629
|
}
|
|
2657
|
-
function
|
|
2658
|
-
if (!
|
|
2659
|
-
|
|
2660
|
-
|
|
2630
|
+
function createParamStore(startIndex = 1) {
|
|
2631
|
+
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2632
|
+
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
2633
|
+
}
|
|
2634
|
+
if (startIndex > MAX_PARAM_INDEX) {
|
|
2661
2635
|
throw new Error(
|
|
2662
|
-
|
|
2636
|
+
`Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
|
|
2663
2637
|
);
|
|
2664
2638
|
}
|
|
2665
|
-
return
|
|
2639
|
+
return createStoreInternal(startIndex);
|
|
2666
2640
|
}
|
|
2667
|
-
function
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
if (n === 0) return 0;
|
|
2675
|
-
}
|
|
2676
|
-
return n;
|
|
2641
|
+
function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
|
|
2642
|
+
validateState([...existingParams], [...existingMappings], nextIndex);
|
|
2643
|
+
return createStoreInternal(
|
|
2644
|
+
nextIndex,
|
|
2645
|
+
[...existingParams],
|
|
2646
|
+
[...existingMappings]
|
|
2647
|
+
);
|
|
2677
2648
|
}
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2649
|
+
|
|
2650
|
+
// src/builder/shared/state.ts
|
|
2651
|
+
function toPublicResult(clause, joins, params) {
|
|
2652
|
+
const snapshot = params.snapshot();
|
|
2653
|
+
return Object.freeze({
|
|
2654
|
+
clause: clause || DEFAULT_WHERE_CLAUSE,
|
|
2655
|
+
joins: Object.freeze([...joins]),
|
|
2656
|
+
params: snapshot.params,
|
|
2657
|
+
paramMappings: snapshot.mappings,
|
|
2658
|
+
nextParamIndex: snapshot.index
|
|
2683
2659
|
});
|
|
2684
2660
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2661
|
+
|
|
2662
|
+
// src/builder/where.ts
|
|
2663
|
+
function buildWhereClause(where, options) {
|
|
2664
|
+
var _a, _b, _c, _d, _e;
|
|
2665
|
+
const dialect = options.dialect || getGlobalDialect();
|
|
2666
|
+
const params = (_a = options.params) != null ? _a : createParamStore();
|
|
2667
|
+
const ctx = {
|
|
2668
|
+
alias: options.alias,
|
|
2669
|
+
model: options.model,
|
|
2670
|
+
schemaModels: (_b = options.schemaModels) != null ? _b : [],
|
|
2671
|
+
path: (_c = options.path) != null ? _c : [],
|
|
2672
|
+
isSubquery: (_d = options.isSubquery) != null ? _d : false,
|
|
2673
|
+
aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
|
|
2674
|
+
dialect,
|
|
2675
|
+
params,
|
|
2676
|
+
depth: 0
|
|
2677
|
+
};
|
|
2678
|
+
const result = whereBuilderInstance.build(where, ctx);
|
|
2679
|
+
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2680
|
+
if (!options.isSubquery) {
|
|
2681
|
+
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2682
|
+
(m) => parseInt(m[1], 10)
|
|
2683
|
+
);
|
|
2684
|
+
if (nums.length > 0) {
|
|
2685
|
+
const min = Math.min(...nums);
|
|
2686
|
+
if (min === 1) {
|
|
2687
|
+
validateParamConsistency(publicResult.clause, publicResult.params);
|
|
2688
|
+
} else {
|
|
2689
|
+
validateParamConsistencyFragment(
|
|
2690
|
+
publicResult.clause,
|
|
2691
|
+
publicResult.params
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2699
2695
|
}
|
|
2700
|
-
return
|
|
2696
|
+
return publicResult;
|
|
2701
2697
|
}
|
|
2702
2698
|
|
|
2703
2699
|
// src/builder/select/fields.ts
|