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