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/index.cjs
CHANGED
|
@@ -42,6 +42,159 @@ var __async = (__this, __arguments, generator) => {
|
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// src/builder/shared/constants.ts
|
|
46
|
+
var SQL_SEPARATORS = Object.freeze({
|
|
47
|
+
FIELD_LIST: ", ",
|
|
48
|
+
CONDITION_AND: " AND ",
|
|
49
|
+
CONDITION_OR: " OR ",
|
|
50
|
+
ORDER_BY: ", "
|
|
51
|
+
});
|
|
52
|
+
var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
53
|
+
"select",
|
|
54
|
+
"from",
|
|
55
|
+
"where",
|
|
56
|
+
"and",
|
|
57
|
+
"or",
|
|
58
|
+
"not",
|
|
59
|
+
"in",
|
|
60
|
+
"like",
|
|
61
|
+
"between",
|
|
62
|
+
"order",
|
|
63
|
+
"by",
|
|
64
|
+
"group",
|
|
65
|
+
"having",
|
|
66
|
+
"limit",
|
|
67
|
+
"offset",
|
|
68
|
+
"join",
|
|
69
|
+
"inner",
|
|
70
|
+
"left",
|
|
71
|
+
"right",
|
|
72
|
+
"outer",
|
|
73
|
+
"on",
|
|
74
|
+
"as",
|
|
75
|
+
"table",
|
|
76
|
+
"column",
|
|
77
|
+
"index",
|
|
78
|
+
"user",
|
|
79
|
+
"users",
|
|
80
|
+
"values",
|
|
81
|
+
"update",
|
|
82
|
+
"insert",
|
|
83
|
+
"delete",
|
|
84
|
+
"create",
|
|
85
|
+
"drop",
|
|
86
|
+
"alter",
|
|
87
|
+
"truncate",
|
|
88
|
+
"grant",
|
|
89
|
+
"revoke",
|
|
90
|
+
"exec",
|
|
91
|
+
"execute",
|
|
92
|
+
"union",
|
|
93
|
+
"intersect",
|
|
94
|
+
"except",
|
|
95
|
+
"case",
|
|
96
|
+
"when",
|
|
97
|
+
"then",
|
|
98
|
+
"else",
|
|
99
|
+
"end",
|
|
100
|
+
"null",
|
|
101
|
+
"true",
|
|
102
|
+
"false",
|
|
103
|
+
"is",
|
|
104
|
+
"exists",
|
|
105
|
+
"all",
|
|
106
|
+
"any",
|
|
107
|
+
"some"
|
|
108
|
+
]);
|
|
109
|
+
var SQL_KEYWORDS = SQL_RESERVED_WORDS;
|
|
110
|
+
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
111
|
+
var SPECIAL_FIELDS = Object.freeze({
|
|
112
|
+
ID: "id"
|
|
113
|
+
});
|
|
114
|
+
var SQL_TEMPLATES = Object.freeze({
|
|
115
|
+
PUBLIC_SCHEMA: "public",
|
|
116
|
+
WHERE: "WHERE",
|
|
117
|
+
SELECT: "SELECT",
|
|
118
|
+
FROM: "FROM",
|
|
119
|
+
ORDER_BY: "ORDER BY",
|
|
120
|
+
GROUP_BY: "GROUP BY",
|
|
121
|
+
HAVING: "HAVING",
|
|
122
|
+
LIMIT: "LIMIT",
|
|
123
|
+
OFFSET: "OFFSET",
|
|
124
|
+
COUNT_ALL: "COUNT(*)",
|
|
125
|
+
AS: "AS",
|
|
126
|
+
DISTINCT_ON: "DISTINCT ON",
|
|
127
|
+
IS_NULL: "IS NULL",
|
|
128
|
+
IS_NOT_NULL: "IS NOT NULL",
|
|
129
|
+
LIKE: "LIKE",
|
|
130
|
+
AND: "AND",
|
|
131
|
+
OR: "OR",
|
|
132
|
+
NOT: "NOT"
|
|
133
|
+
});
|
|
134
|
+
var SCHEMA_PREFIXES = Object.freeze({
|
|
135
|
+
INTERNAL: "@",
|
|
136
|
+
COMMENT: "//"
|
|
137
|
+
});
|
|
138
|
+
var Ops = Object.freeze({
|
|
139
|
+
EQUALS: "equals",
|
|
140
|
+
NOT: "not",
|
|
141
|
+
GT: "gt",
|
|
142
|
+
GTE: "gte",
|
|
143
|
+
LT: "lt",
|
|
144
|
+
LTE: "lte",
|
|
145
|
+
IN: "in",
|
|
146
|
+
NOT_IN: "notIn",
|
|
147
|
+
CONTAINS: "contains",
|
|
148
|
+
STARTS_WITH: "startsWith",
|
|
149
|
+
ENDS_WITH: "endsWith",
|
|
150
|
+
HAS: "has",
|
|
151
|
+
HAS_SOME: "hasSome",
|
|
152
|
+
HAS_EVERY: "hasEvery",
|
|
153
|
+
IS_EMPTY: "isEmpty",
|
|
154
|
+
PATH: "path",
|
|
155
|
+
STRING_CONTAINS: "string_contains",
|
|
156
|
+
STRING_STARTS_WITH: "string_starts_with",
|
|
157
|
+
STRING_ENDS_WITH: "string_ends_with"
|
|
158
|
+
});
|
|
159
|
+
var LogicalOps = Object.freeze({
|
|
160
|
+
AND: "AND",
|
|
161
|
+
OR: "OR",
|
|
162
|
+
NOT: "NOT"
|
|
163
|
+
});
|
|
164
|
+
var RelationFilters = Object.freeze({
|
|
165
|
+
SOME: "some",
|
|
166
|
+
EVERY: "every",
|
|
167
|
+
NONE: "none"
|
|
168
|
+
});
|
|
169
|
+
var Modes = Object.freeze({
|
|
170
|
+
INSENSITIVE: "insensitive",
|
|
171
|
+
DEFAULT: "default"
|
|
172
|
+
});
|
|
173
|
+
var Wildcards = Object.freeze({
|
|
174
|
+
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
175
|
+
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
176
|
+
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
177
|
+
});
|
|
178
|
+
var REGEX_CACHE = {
|
|
179
|
+
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
180
|
+
};
|
|
181
|
+
var LIMITS = Object.freeze({
|
|
182
|
+
MAX_QUERY_DEPTH: 50,
|
|
183
|
+
MAX_ARRAY_SIZE: 1e4,
|
|
184
|
+
MAX_STRING_LENGTH: 1e4
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// src/utils/normalize-value.ts
|
|
188
|
+
function normalizeValue(value) {
|
|
189
|
+
if (value instanceof Date) {
|
|
190
|
+
return value.toISOString();
|
|
191
|
+
}
|
|
192
|
+
if (Array.isArray(value)) {
|
|
193
|
+
return value.map(normalizeValue);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
45
198
|
// src/sql-builder-dialect.ts
|
|
46
199
|
var globalDialect = "postgres";
|
|
47
200
|
function setGlobalDialect(dialect) {
|
|
@@ -218,153 +371,11 @@ function prepareArrayParam(value, dialect) {
|
|
|
218
371
|
throw new Error("prepareArrayParam requires array value");
|
|
219
372
|
}
|
|
220
373
|
if (dialect === "postgres") {
|
|
221
|
-
return value;
|
|
374
|
+
return value.map(normalizeValue);
|
|
222
375
|
}
|
|
223
376
|
return JSON.stringify(value);
|
|
224
377
|
}
|
|
225
378
|
|
|
226
|
-
// src/builder/shared/constants.ts
|
|
227
|
-
var SQL_SEPARATORS = Object.freeze({
|
|
228
|
-
FIELD_LIST: ", ",
|
|
229
|
-
CONDITION_AND: " AND ",
|
|
230
|
-
CONDITION_OR: " OR ",
|
|
231
|
-
ORDER_BY: ", "
|
|
232
|
-
});
|
|
233
|
-
var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
234
|
-
"select",
|
|
235
|
-
"from",
|
|
236
|
-
"where",
|
|
237
|
-
"and",
|
|
238
|
-
"or",
|
|
239
|
-
"not",
|
|
240
|
-
"in",
|
|
241
|
-
"like",
|
|
242
|
-
"between",
|
|
243
|
-
"order",
|
|
244
|
-
"by",
|
|
245
|
-
"group",
|
|
246
|
-
"having",
|
|
247
|
-
"limit",
|
|
248
|
-
"offset",
|
|
249
|
-
"join",
|
|
250
|
-
"inner",
|
|
251
|
-
"left",
|
|
252
|
-
"right",
|
|
253
|
-
"outer",
|
|
254
|
-
"on",
|
|
255
|
-
"as",
|
|
256
|
-
"table",
|
|
257
|
-
"column",
|
|
258
|
-
"index",
|
|
259
|
-
"user",
|
|
260
|
-
"users",
|
|
261
|
-
"values",
|
|
262
|
-
"update",
|
|
263
|
-
"insert",
|
|
264
|
-
"delete",
|
|
265
|
-
"create",
|
|
266
|
-
"drop",
|
|
267
|
-
"alter",
|
|
268
|
-
"truncate",
|
|
269
|
-
"grant",
|
|
270
|
-
"revoke",
|
|
271
|
-
"exec",
|
|
272
|
-
"execute",
|
|
273
|
-
"union",
|
|
274
|
-
"intersect",
|
|
275
|
-
"except",
|
|
276
|
-
"case",
|
|
277
|
-
"when",
|
|
278
|
-
"then",
|
|
279
|
-
"else",
|
|
280
|
-
"end",
|
|
281
|
-
"null",
|
|
282
|
-
"true",
|
|
283
|
-
"false",
|
|
284
|
-
"is",
|
|
285
|
-
"exists",
|
|
286
|
-
"all",
|
|
287
|
-
"any",
|
|
288
|
-
"some"
|
|
289
|
-
]);
|
|
290
|
-
var SQL_KEYWORDS = SQL_RESERVED_WORDS;
|
|
291
|
-
var DEFAULT_WHERE_CLAUSE = "1=1";
|
|
292
|
-
var SPECIAL_FIELDS = Object.freeze({
|
|
293
|
-
ID: "id"
|
|
294
|
-
});
|
|
295
|
-
var SQL_TEMPLATES = Object.freeze({
|
|
296
|
-
PUBLIC_SCHEMA: "public",
|
|
297
|
-
WHERE: "WHERE",
|
|
298
|
-
SELECT: "SELECT",
|
|
299
|
-
FROM: "FROM",
|
|
300
|
-
ORDER_BY: "ORDER BY",
|
|
301
|
-
GROUP_BY: "GROUP BY",
|
|
302
|
-
HAVING: "HAVING",
|
|
303
|
-
LIMIT: "LIMIT",
|
|
304
|
-
OFFSET: "OFFSET",
|
|
305
|
-
COUNT_ALL: "COUNT(*)",
|
|
306
|
-
AS: "AS",
|
|
307
|
-
DISTINCT_ON: "DISTINCT ON",
|
|
308
|
-
IS_NULL: "IS NULL",
|
|
309
|
-
IS_NOT_NULL: "IS NOT NULL",
|
|
310
|
-
LIKE: "LIKE",
|
|
311
|
-
AND: "AND",
|
|
312
|
-
OR: "OR",
|
|
313
|
-
NOT: "NOT"
|
|
314
|
-
});
|
|
315
|
-
var SCHEMA_PREFIXES = Object.freeze({
|
|
316
|
-
INTERNAL: "@",
|
|
317
|
-
COMMENT: "//"
|
|
318
|
-
});
|
|
319
|
-
var Ops = Object.freeze({
|
|
320
|
-
EQUALS: "equals",
|
|
321
|
-
NOT: "not",
|
|
322
|
-
GT: "gt",
|
|
323
|
-
GTE: "gte",
|
|
324
|
-
LT: "lt",
|
|
325
|
-
LTE: "lte",
|
|
326
|
-
IN: "in",
|
|
327
|
-
NOT_IN: "notIn",
|
|
328
|
-
CONTAINS: "contains",
|
|
329
|
-
STARTS_WITH: "startsWith",
|
|
330
|
-
ENDS_WITH: "endsWith",
|
|
331
|
-
HAS: "has",
|
|
332
|
-
HAS_SOME: "hasSome",
|
|
333
|
-
HAS_EVERY: "hasEvery",
|
|
334
|
-
IS_EMPTY: "isEmpty",
|
|
335
|
-
PATH: "path",
|
|
336
|
-
STRING_CONTAINS: "string_contains",
|
|
337
|
-
STRING_STARTS_WITH: "string_starts_with",
|
|
338
|
-
STRING_ENDS_WITH: "string_ends_with"
|
|
339
|
-
});
|
|
340
|
-
var LogicalOps = Object.freeze({
|
|
341
|
-
AND: "AND",
|
|
342
|
-
OR: "OR",
|
|
343
|
-
NOT: "NOT"
|
|
344
|
-
});
|
|
345
|
-
var RelationFilters = Object.freeze({
|
|
346
|
-
SOME: "some",
|
|
347
|
-
EVERY: "every",
|
|
348
|
-
NONE: "none"
|
|
349
|
-
});
|
|
350
|
-
var Modes = Object.freeze({
|
|
351
|
-
INSENSITIVE: "insensitive",
|
|
352
|
-
DEFAULT: "default"
|
|
353
|
-
});
|
|
354
|
-
var Wildcards = Object.freeze({
|
|
355
|
-
[Ops.CONTAINS]: (v) => `%${v}%`,
|
|
356
|
-
[Ops.STARTS_WITH]: (v) => `${v}%`,
|
|
357
|
-
[Ops.ENDS_WITH]: (v) => `%${v}`
|
|
358
|
-
});
|
|
359
|
-
var REGEX_CACHE = {
|
|
360
|
-
VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
|
|
361
|
-
};
|
|
362
|
-
var LIMITS = Object.freeze({
|
|
363
|
-
MAX_QUERY_DEPTH: 50,
|
|
364
|
-
MAX_ARRAY_SIZE: 1e4,
|
|
365
|
-
MAX_STRING_LENGTH: 1e4
|
|
366
|
-
});
|
|
367
|
-
|
|
368
379
|
// src/builder/shared/validators/type-guards.ts
|
|
369
380
|
function isNotNullish(value) {
|
|
370
381
|
return value !== null && value !== void 0;
|
|
@@ -450,6 +461,19 @@ function getRelationFieldSet(model) {
|
|
|
450
461
|
RELATION_SET_CACHE.set(model, s);
|
|
451
462
|
return s;
|
|
452
463
|
}
|
|
464
|
+
var COLUMN_MAP_CACHE = /* @__PURE__ */ new WeakMap();
|
|
465
|
+
function getColumnMap(model) {
|
|
466
|
+
const cached = COLUMN_MAP_CACHE.get(model);
|
|
467
|
+
if (cached) return cached;
|
|
468
|
+
const map = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const f of model.fields) {
|
|
470
|
+
if (!f.isRelation) {
|
|
471
|
+
map.set(f.name, f.dbName || f.name);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
COLUMN_MAP_CACHE.set(model, map);
|
|
475
|
+
return map;
|
|
476
|
+
}
|
|
453
477
|
|
|
454
478
|
// src/builder/shared/validators/sql-validators.ts
|
|
455
479
|
function isValidWhereClause(clause) {
|
|
@@ -837,27 +861,10 @@ function quote(id) {
|
|
|
837
861
|
}
|
|
838
862
|
return id;
|
|
839
863
|
}
|
|
840
|
-
function pickDbFieldName(field) {
|
|
841
|
-
const candidates = [
|
|
842
|
-
field == null ? void 0 : field.dbName,
|
|
843
|
-
field == null ? void 0 : field.columnName,
|
|
844
|
-
field == null ? void 0 : field.databaseName,
|
|
845
|
-
field == null ? void 0 : field.mappedName
|
|
846
|
-
];
|
|
847
|
-
for (const c of candidates) {
|
|
848
|
-
if (typeof c === "string") {
|
|
849
|
-
const s = c.trim();
|
|
850
|
-
if (s.length > 0) return s;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
return void 0;
|
|
854
|
-
}
|
|
855
864
|
function resolveColumnName(model, fieldName) {
|
|
856
|
-
var _a;
|
|
857
865
|
if (!model) return fieldName;
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
return (_a = pickDbFieldName(f)) != null ? _a : fieldName;
|
|
866
|
+
const columnMap = getColumnMap(model);
|
|
867
|
+
return columnMap.get(fieldName) || fieldName;
|
|
861
868
|
}
|
|
862
869
|
function quoteColumn(model, fieldName) {
|
|
863
870
|
return quote(resolveColumnName(model, fieldName));
|
|
@@ -1015,1570 +1022,1570 @@ function joinCondition(field, parentModel, childModel, parentAlias, childAlias)
|
|
|
1015
1022
|
function getModelByName(schemas, name) {
|
|
1016
1023
|
return schemas.find((m) => m.name === name);
|
|
1017
1024
|
}
|
|
1018
|
-
function
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
);
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
for (const [subOp, subVal] of entries) {
|
|
1025
|
-
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1026
|
-
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1027
|
-
}
|
|
1028
|
-
if (clauses.length === 0) return "";
|
|
1029
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1030
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1031
|
-
}
|
|
1032
|
-
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1033
|
-
if (val === void 0) return "";
|
|
1034
|
-
if (val === null) {
|
|
1035
|
-
return handleNullValue(expr, op);
|
|
1036
|
-
}
|
|
1037
|
-
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1038
|
-
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1039
|
-
}
|
|
1040
|
-
if (op === Ops.NOT) {
|
|
1041
|
-
const placeholder = params.addAuto(val);
|
|
1042
|
-
return `${expr} <> ${placeholder}`;
|
|
1025
|
+
function normalizeIntLike(name, v, opts = {}) {
|
|
1026
|
+
var _a, _b;
|
|
1027
|
+
if (!isNotNullish(v)) return void 0;
|
|
1028
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1029
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1030
|
+
throw new Error(`${name} must be an integer`);
|
|
1043
1031
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1032
|
+
const min = (_a = opts.min) != null ? _a : 0;
|
|
1033
|
+
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
1034
|
+
if (!allowZero && v === 0) {
|
|
1035
|
+
throw new Error(`${name} must be > 0`);
|
|
1047
1036
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
Ops.STARTS_WITH,
|
|
1051
|
-
Ops.ENDS_WITH
|
|
1052
|
-
]);
|
|
1053
|
-
if (STRING_LIKE_OPS.has(op)) {
|
|
1054
|
-
if (!isNotNullish(dialect)) {
|
|
1055
|
-
throw createError(`Like operators require a SQL dialect`, {
|
|
1056
|
-
operator: op
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1037
|
+
if (v < min) {
|
|
1038
|
+
throw new Error(`${name} must be >= ${min}`);
|
|
1060
1039
|
}
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1064
|
-
}
|
|
1065
|
-
return handleInOperator(expr, op, val, params, dialect);
|
|
1040
|
+
if (typeof opts.max === "number" && v > opts.max) {
|
|
1041
|
+
throw new Error(`${name} must be <= ${opts.max}`);
|
|
1066
1042
|
}
|
|
1067
|
-
return
|
|
1068
|
-
}
|
|
1069
|
-
function handleNullValue(expr, op) {
|
|
1070
|
-
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1071
|
-
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1072
|
-
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1073
|
-
}
|
|
1074
|
-
function normalizeMode(v) {
|
|
1075
|
-
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1076
|
-
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1077
|
-
return void 0;
|
|
1043
|
+
return v;
|
|
1078
1044
|
}
|
|
1079
|
-
function
|
|
1080
|
-
const
|
|
1081
|
-
const
|
|
1082
|
-
return
|
|
1083
|
-
|
|
1084
|
-
val,
|
|
1085
|
-
params,
|
|
1086
|
-
dialect,
|
|
1087
|
-
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1088
|
-
` ${SQL_TEMPLATES.AND} `
|
|
1089
|
-
);
|
|
1045
|
+
function scopeName(scope, dynamicName) {
|
|
1046
|
+
const s = String(scope).trim();
|
|
1047
|
+
const dn = String(dynamicName).trim();
|
|
1048
|
+
if (s.length === 0) return dn;
|
|
1049
|
+
return `${s}:${dn}`;
|
|
1090
1050
|
}
|
|
1091
|
-
function
|
|
1092
|
-
if (
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1096
|
-
case Ops.STARTS_WITH:
|
|
1097
|
-
return `(${placeholder} || '%')`;
|
|
1098
|
-
case Ops.ENDS_WITH:
|
|
1099
|
-
return `('%' || ${placeholder})`;
|
|
1100
|
-
default:
|
|
1101
|
-
return placeholder;
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
switch (op) {
|
|
1105
|
-
case Ops.CONTAINS:
|
|
1106
|
-
return `('%' || ${placeholder} || '%')`;
|
|
1107
|
-
case Ops.STARTS_WITH:
|
|
1108
|
-
return `(${placeholder} || '%')`;
|
|
1109
|
-
case Ops.ENDS_WITH:
|
|
1110
|
-
return `('%' || ${placeholder})`;
|
|
1111
|
-
default:
|
|
1112
|
-
return placeholder;
|
|
1051
|
+
function addAutoScoped(params, value, scope) {
|
|
1052
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
1053
|
+
const dn = schemaParser.extractDynamicName(value);
|
|
1054
|
+
return params.add(void 0, scopeName(scope, dn));
|
|
1113
1055
|
}
|
|
1056
|
+
return params.add(value);
|
|
1114
1057
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1058
|
+
|
|
1059
|
+
// src/builder/shared/order-by-utils.ts
|
|
1060
|
+
var flipNulls = (v) => {
|
|
1061
|
+
const s = String(v).toLowerCase();
|
|
1062
|
+
if (s === "first") return "last";
|
|
1063
|
+
if (s === "last") return "first";
|
|
1064
|
+
return v;
|
|
1065
|
+
};
|
|
1066
|
+
var flipSortString = (v) => {
|
|
1067
|
+
if (typeof v !== "string") return v;
|
|
1068
|
+
const s = v.toLowerCase();
|
|
1069
|
+
if (s === "asc") return "desc";
|
|
1070
|
+
if (s === "desc") return "asc";
|
|
1071
|
+
return v;
|
|
1072
|
+
};
|
|
1073
|
+
var getNextSort = (sortRaw) => {
|
|
1074
|
+
if (typeof sortRaw !== "string") return sortRaw;
|
|
1075
|
+
const s = sortRaw.toLowerCase();
|
|
1076
|
+
if (s === "asc") return "desc";
|
|
1077
|
+
if (s === "desc") return "asc";
|
|
1078
|
+
return sortRaw;
|
|
1079
|
+
};
|
|
1080
|
+
var flipObjectSort = (obj) => {
|
|
1081
|
+
const sortRaw = obj.sort;
|
|
1082
|
+
const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
|
|
1083
|
+
const nullsRaw = obj.nulls;
|
|
1084
|
+
if (typeof nullsRaw === "string") {
|
|
1085
|
+
out.nulls = flipNulls(nullsRaw);
|
|
1124
1086
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1087
|
+
return out;
|
|
1088
|
+
};
|
|
1089
|
+
var flipValue = (v) => {
|
|
1090
|
+
if (typeof v === "string") return flipSortString(v);
|
|
1091
|
+
if (isPlainObject(v)) return flipObjectSort(v);
|
|
1092
|
+
return v;
|
|
1093
|
+
};
|
|
1094
|
+
var assertSingleFieldObject = (item) => {
|
|
1095
|
+
if (!isPlainObject(item)) {
|
|
1096
|
+
throw new Error("orderBy array entries must be objects");
|
|
1128
1097
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
if (val === void 0) return "";
|
|
1133
|
-
if (schemaParser.isDynamicParameter(val)) {
|
|
1134
|
-
const placeholder2 = params.addAuto(val);
|
|
1135
|
-
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1098
|
+
const entries = Object.entries(item);
|
|
1099
|
+
if (entries.length !== 1) {
|
|
1100
|
+
throw new Error("orderBy array entries must have exactly one field");
|
|
1136
1101
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1102
|
+
return entries[0];
|
|
1103
|
+
};
|
|
1104
|
+
var flipOrderByArray = (orderBy) => {
|
|
1105
|
+
return orderBy.map((item) => {
|
|
1106
|
+
const [k, v] = assertSingleFieldObject(item);
|
|
1107
|
+
return { [k]: flipValue(v) };
|
|
1108
|
+
});
|
|
1109
|
+
};
|
|
1110
|
+
var flipOrderByObject = (orderBy) => {
|
|
1111
|
+
const out = {};
|
|
1112
|
+
for (const [k, v] of Object.entries(orderBy)) {
|
|
1113
|
+
out[k] = flipValue(v);
|
|
1142
1114
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1115
|
+
return out;
|
|
1116
|
+
};
|
|
1117
|
+
function reverseOrderByInput(orderBy) {
|
|
1118
|
+
if (!isNotNullish(orderBy)) return orderBy;
|
|
1119
|
+
if (Array.isArray(orderBy)) {
|
|
1120
|
+
return flipOrderByArray(orderBy);
|
|
1145
1121
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1122
|
+
if (isPlainObject(orderBy)) {
|
|
1123
|
+
return flipOrderByObject(orderBy);
|
|
1124
|
+
}
|
|
1125
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1149
1126
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
if (
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
const placeholder = params.addAuto(val);
|
|
1164
|
-
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1165
|
-
}
|
|
1166
|
-
function buildArrayParam(val, params, dialect) {
|
|
1167
|
-
if (schemaParser.isDynamicParameter(val)) {
|
|
1168
|
-
return params.addAuto(val);
|
|
1127
|
+
var normalizePairs = (pairs, parseValue) => {
|
|
1128
|
+
return pairs.map(([field, rawValue]) => {
|
|
1129
|
+
const parsed = parseValue(rawValue, field);
|
|
1130
|
+
return {
|
|
1131
|
+
[field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
1132
|
+
};
|
|
1133
|
+
});
|
|
1134
|
+
};
|
|
1135
|
+
function normalizeOrderByInput(orderBy, parseValue) {
|
|
1136
|
+
if (!isNotNullish(orderBy)) return [];
|
|
1137
|
+
if (Array.isArray(orderBy)) {
|
|
1138
|
+
const pairs = orderBy.map(assertSingleFieldObject);
|
|
1139
|
+
return normalizePairs(pairs, parseValue);
|
|
1169
1140
|
}
|
|
1170
|
-
if (
|
|
1171
|
-
|
|
1141
|
+
if (isPlainObject(orderBy)) {
|
|
1142
|
+
return normalizePairs(Object.entries(orderBy), parseValue);
|
|
1172
1143
|
}
|
|
1173
|
-
|
|
1174
|
-
return params.add(paramValue);
|
|
1144
|
+
throw new Error("orderBy must be an object or array of objects");
|
|
1175
1145
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
if (op === Ops.EQUALS) {
|
|
1184
|
-
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1185
|
-
}
|
|
1186
|
-
if (op === Ops.NOT) {
|
|
1187
|
-
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1188
|
-
}
|
|
1189
|
-
switch (op) {
|
|
1190
|
-
case Ops.HAS:
|
|
1191
|
-
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1192
|
-
case Ops.HAS_SOME:
|
|
1193
|
-
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1194
|
-
case Ops.HAS_EVERY:
|
|
1195
|
-
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1196
|
-
case Ops.IS_EMPTY:
|
|
1197
|
-
return handleArrayIsEmpty(expr, val, dialect);
|
|
1198
|
-
default:
|
|
1199
|
-
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
1200
|
-
}
|
|
1146
|
+
|
|
1147
|
+
// src/builder/pagination.ts
|
|
1148
|
+
var MAX_LIMIT_OFFSET = 2147483647;
|
|
1149
|
+
function parseDirectionRaw(raw, errorLabel) {
|
|
1150
|
+
const s = String(raw).toLowerCase();
|
|
1151
|
+
if (s === "asc" || s === "desc") return s;
|
|
1152
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1201
1153
|
}
|
|
1202
|
-
function
|
|
1203
|
-
if (
|
|
1204
|
-
|
|
1205
|
-
|
|
1154
|
+
function parseNullsRaw(raw, errorLabel) {
|
|
1155
|
+
if (!isNotNullish(raw)) return void 0;
|
|
1156
|
+
const s = String(raw).toLowerCase();
|
|
1157
|
+
if (s === "first" || s === "last") return s;
|
|
1158
|
+
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
1159
|
+
}
|
|
1160
|
+
function requireOrderByObject(v, errorPrefix) {
|
|
1161
|
+
if (!isPlainObject(v) || !("sort" in v)) {
|
|
1162
|
+
throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
|
|
1206
1163
|
}
|
|
1207
|
-
|
|
1208
|
-
return arrayEquals(expr, placeholder, cast, dialect);
|
|
1164
|
+
return v;
|
|
1209
1165
|
}
|
|
1210
|
-
function
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
} else {
|
|
1218
|
-
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1219
|
-
operator: Ops.NOT,
|
|
1220
|
-
value: val
|
|
1221
|
-
});
|
|
1166
|
+
function assertAllowedOrderByKeys(obj, fieldName) {
|
|
1167
|
+
const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
|
|
1168
|
+
for (const k of Object.keys(obj)) {
|
|
1169
|
+
if (!allowed.has(k)) {
|
|
1170
|
+
throw new Error(
|
|
1171
|
+
fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
|
|
1172
|
+
);
|
|
1222
1173
|
}
|
|
1223
1174
|
}
|
|
1224
|
-
if (target === null) {
|
|
1225
|
-
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1226
|
-
}
|
|
1227
|
-
if (isEmptyArray(target)) {
|
|
1228
|
-
return arrayIsNotEmpty(expr, dialect);
|
|
1229
|
-
}
|
|
1230
|
-
const placeholder = buildArrayParam(target, params, dialect);
|
|
1231
|
-
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
1232
1175
|
}
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1237
|
-
operator: Ops.HAS,
|
|
1238
|
-
value: val
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1242
|
-
throw createError(`has requires scalar value (single element), not array`, {
|
|
1243
|
-
operator: Ops.HAS,
|
|
1244
|
-
value: val
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
if (isPlainObject(val)) {
|
|
1248
|
-
throw createError(`has requires scalar value`, {
|
|
1249
|
-
operator: Ops.HAS,
|
|
1250
|
-
value: val
|
|
1251
|
-
});
|
|
1176
|
+
function parseOrderByValue(v, fieldName) {
|
|
1177
|
+
const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
|
|
1178
|
+
if (typeof v === "string") {
|
|
1179
|
+
return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
|
|
1252
1180
|
}
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1181
|
+
const obj = requireOrderByObject(v, errorPrefix);
|
|
1182
|
+
const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
|
|
1183
|
+
const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
|
|
1184
|
+
assertAllowedOrderByKeys(obj, fieldName);
|
|
1185
|
+
return { direction, nulls };
|
|
1255
1186
|
}
|
|
1256
|
-
function
|
|
1257
|
-
if (
|
|
1258
|
-
|
|
1259
|
-
const placeholder2 = params.addAuto(val);
|
|
1260
|
-
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
1187
|
+
function normalizeFiniteInteger(name, v) {
|
|
1188
|
+
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
1189
|
+
throw new Error(`${name} must be an integer`);
|
|
1261
1190
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1191
|
+
return v;
|
|
1192
|
+
}
|
|
1193
|
+
function normalizeNonNegativeInt(name, v) {
|
|
1194
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1195
|
+
const n = normalizeFiniteInteger(name, v);
|
|
1196
|
+
if (n < 0) {
|
|
1197
|
+
throw new Error(`${name} must be >= 0`);
|
|
1267
1198
|
}
|
|
1268
|
-
if (
|
|
1269
|
-
throw
|
|
1270
|
-
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1271
|
-
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1272
|
-
);
|
|
1199
|
+
if (n > MAX_LIMIT_OFFSET) {
|
|
1200
|
+
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
1273
1201
|
}
|
|
1274
|
-
|
|
1275
|
-
const paramValue = prepareArrayParam(val, dialect);
|
|
1276
|
-
const placeholder = params.add(paramValue);
|
|
1277
|
-
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
1202
|
+
return n;
|
|
1278
1203
|
}
|
|
1279
|
-
function
|
|
1280
|
-
|
|
1281
|
-
const placeholder = buildArrayParam(val, params, dialect);
|
|
1282
|
-
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
1204
|
+
function hasNonNullishProp(v, key) {
|
|
1205
|
+
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
1283
1206
|
}
|
|
1284
|
-
function
|
|
1285
|
-
if (
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1207
|
+
function normalizeIntegerOrDynamic(name, v) {
|
|
1208
|
+
if (schemaParser.isDynamicParameter(v)) return v;
|
|
1209
|
+
return normalizeFiniteInteger(name, v);
|
|
1210
|
+
}
|
|
1211
|
+
function readSkipTake(relArgs) {
|
|
1212
|
+
const hasSkip = hasNonNullishProp(relArgs, "skip");
|
|
1213
|
+
const hasTake = hasNonNullishProp(relArgs, "take");
|
|
1214
|
+
if (!hasSkip && !hasTake) {
|
|
1215
|
+
return {
|
|
1216
|
+
hasSkip: false,
|
|
1217
|
+
hasTake: false,
|
|
1218
|
+
skipVal: void 0,
|
|
1219
|
+
takeVal: void 0
|
|
1220
|
+
};
|
|
1290
1221
|
}
|
|
1291
|
-
|
|
1222
|
+
const obj = relArgs;
|
|
1223
|
+
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
1224
|
+
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
1225
|
+
return { hasSkip, hasTake, skipVal, takeVal };
|
|
1292
1226
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1227
|
+
function buildOrderByFragment(entries, alias, dialect, model) {
|
|
1228
|
+
if (entries.length === 0) return "";
|
|
1229
|
+
const out = [];
|
|
1230
|
+
for (const e of entries) {
|
|
1231
|
+
const dir = e.direction.toUpperCase();
|
|
1232
|
+
const c = col(alias, e.field, model);
|
|
1233
|
+
if (dialect === "postgres") {
|
|
1234
|
+
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
1235
|
+
out.push(`${c} ${dir}${nulls}`);
|
|
1236
|
+
continue;
|
|
1303
1237
|
}
|
|
1304
|
-
if (
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
);
|
|
1238
|
+
if (isNotNullish(e.nulls)) {
|
|
1239
|
+
const isNullExpr = `(${c} IS NULL)`;
|
|
1240
|
+
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
1241
|
+
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
1242
|
+
out.push(`${c} ${dir}`);
|
|
1243
|
+
continue;
|
|
1309
1244
|
}
|
|
1245
|
+
out.push(`${c} ${dir}`);
|
|
1310
1246
|
}
|
|
1247
|
+
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
1311
1248
|
}
|
|
1312
|
-
function
|
|
1313
|
-
if (
|
|
1314
|
-
|
|
1315
|
-
return handleJsonPath(expr, val, params, dialect);
|
|
1316
|
-
}
|
|
1317
|
-
const jsonWildcards = {
|
|
1318
|
-
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1319
|
-
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1320
|
-
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1321
|
-
};
|
|
1322
|
-
if (op in jsonWildcards) {
|
|
1323
|
-
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
1249
|
+
function defaultNullsFor(dialect, direction) {
|
|
1250
|
+
if (dialect === "postgres") {
|
|
1251
|
+
return direction === "asc" ? "last" : "first";
|
|
1324
1252
|
}
|
|
1325
|
-
|
|
1253
|
+
return direction === "asc" ? "first" : "last";
|
|
1326
1254
|
}
|
|
1327
|
-
function
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1255
|
+
function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
|
|
1256
|
+
const existing = /* @__PURE__ */ new Map();
|
|
1257
|
+
for (const e of orderEntries) existing.set(e.field, e);
|
|
1258
|
+
const out = [...orderEntries];
|
|
1259
|
+
for (const [field] of cursorEntries) {
|
|
1260
|
+
if (!existing.has(field)) {
|
|
1261
|
+
out.push({ field, direction: "asc" });
|
|
1262
|
+
existing.set(field, out[out.length - 1]);
|
|
1263
|
+
}
|
|
1334
1264
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
["<", v.lt],
|
|
1342
|
-
["<=", v.lte]
|
|
1343
|
-
];
|
|
1344
|
-
const ops = rawOps.filter(
|
|
1345
|
-
([, value]) => value !== void 0
|
|
1346
|
-
);
|
|
1347
|
-
if (ops.length === 0) {
|
|
1348
|
-
throw createError("JSON path query missing comparison operator", {
|
|
1349
|
-
operator: Ops.PATH
|
|
1350
|
-
});
|
|
1265
|
+
return out;
|
|
1266
|
+
}
|
|
1267
|
+
function buildCursorFilterParts(cursor, cursorAlias, params, model) {
|
|
1268
|
+
const entries = Object.entries(cursor);
|
|
1269
|
+
if (entries.length === 0) {
|
|
1270
|
+
throw new Error("cursor must have at least one field");
|
|
1351
1271
|
}
|
|
1272
|
+
const placeholdersByField = /* @__PURE__ */ new Map();
|
|
1352
1273
|
const parts = [];
|
|
1353
|
-
for (const [
|
|
1274
|
+
for (const [field, value] of entries) {
|
|
1275
|
+
const c = `${cursorAlias}.${quote(field)}`;
|
|
1354
1276
|
if (value === null) {
|
|
1355
|
-
|
|
1356
|
-
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1277
|
+
parts.push(`${c} IS NULL`);
|
|
1357
1278
|
continue;
|
|
1358
1279
|
}
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
parts.push(`${
|
|
1362
|
-
}
|
|
1363
|
-
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1364
|
-
}
|
|
1365
|
-
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1366
|
-
if (!isNotNullish(val)) {
|
|
1367
|
-
throw createError(`JSON string operator requires non-null value`, {
|
|
1368
|
-
operator: op,
|
|
1369
|
-
value: val
|
|
1370
|
-
});
|
|
1371
|
-
}
|
|
1372
|
-
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1373
|
-
throw createError(`JSON string operator requires scalar value`, {
|
|
1374
|
-
operator: op,
|
|
1375
|
-
value: val
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
const strVal = String(val);
|
|
1379
|
-
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1380
|
-
throw createError(
|
|
1381
|
-
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1382
|
-
{ operator: op }
|
|
1383
|
-
);
|
|
1280
|
+
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
1281
|
+
placeholdersByField.set(field, ph);
|
|
1282
|
+
parts.push(`${c} = ${ph}`);
|
|
1384
1283
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1284
|
+
return {
|
|
1285
|
+
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
1286
|
+
placeholdersByField
|
|
1287
|
+
};
|
|
1388
1288
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
function isListRelation(fieldType) {
|
|
1393
|
-
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1289
|
+
function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
|
|
1290
|
+
const colName = quote(field);
|
|
1291
|
+
return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1394
1292
|
}
|
|
1395
|
-
function
|
|
1396
|
-
|
|
1397
|
-
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1398
|
-
if (isLocal) {
|
|
1399
|
-
if (fkFields.length === 0) {
|
|
1400
|
-
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1401
|
-
field: field.name
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
const parts = fkFields.map((fk) => {
|
|
1405
|
-
const safe = fk.replace(/"/g, '""');
|
|
1406
|
-
const expr = `${parentAlias}."${safe}"`;
|
|
1407
|
-
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1408
|
-
});
|
|
1409
|
-
if (parts.length === 1) return parts[0];
|
|
1410
|
-
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
1411
|
-
}
|
|
1412
|
-
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join})`;
|
|
1413
|
-
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
1293
|
+
function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
|
|
1294
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
1414
1295
|
}
|
|
1415
|
-
function
|
|
1416
|
-
|
|
1417
|
-
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1296
|
+
function buildCursorEqualityExpr(columnExpr, valueExpr) {
|
|
1297
|
+
return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
|
|
1418
1298
|
}
|
|
1419
|
-
function
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1299
|
+
function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
|
|
1300
|
+
const op = direction === "asc" ? ">" : "<";
|
|
1301
|
+
if (nulls === "first") {
|
|
1302
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
1303
|
+
}
|
|
1304
|
+
return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
|
|
1422
1305
|
}
|
|
1423
|
-
function
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
if (noneValue !== void 0 && noneValue !== null) {
|
|
1436
|
-
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1437
|
-
alias: relAlias,
|
|
1438
|
-
model: relModel,
|
|
1439
|
-
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1440
|
-
isSubquery: true,
|
|
1441
|
-
depth: ctx.depth + 1
|
|
1442
|
-
}));
|
|
1443
|
-
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1444
|
-
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1445
|
-
if (canOptimize) {
|
|
1446
|
-
const checkField = relModel.fields.find(
|
|
1447
|
-
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1448
|
-
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1449
|
-
if (checkField) {
|
|
1450
|
-
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join}`;
|
|
1451
|
-
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1452
|
-
return Object.freeze({
|
|
1453
|
-
clause: whereClause,
|
|
1454
|
-
joins: [leftJoinSql]
|
|
1455
|
-
});
|
|
1456
|
-
}
|
|
1306
|
+
function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params, model) {
|
|
1307
|
+
const parts = [];
|
|
1308
|
+
for (const [field, value] of Object.entries(cursor)) {
|
|
1309
|
+
const c = col(outerAlias, field, model);
|
|
1310
|
+
if (value === null) {
|
|
1311
|
+
parts.push(`${c} IS NULL`);
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
const existing = placeholdersByField.get(field);
|
|
1315
|
+
if (typeof existing === "string" && existing.length > 0) {
|
|
1316
|
+
parts.push(`${c} = ${existing}`);
|
|
1317
|
+
continue;
|
|
1457
1318
|
}
|
|
1319
|
+
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
1320
|
+
parts.push(`${c} = ${ph}`);
|
|
1458
1321
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1322
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1323
|
+
}
|
|
1324
|
+
function buildOrderEntries(orderBy) {
|
|
1325
|
+
const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
|
|
1326
|
+
const entries = [];
|
|
1327
|
+
for (const item of normalized) {
|
|
1328
|
+
for (const [field, value] of Object.entries(item)) {
|
|
1329
|
+
if (typeof value === "string") {
|
|
1330
|
+
entries.push({ field, direction: value });
|
|
1331
|
+
} else {
|
|
1332
|
+
entries.push({
|
|
1333
|
+
field,
|
|
1334
|
+
direction: value.sort,
|
|
1335
|
+
nulls: value.nulls
|
|
1336
|
+
});
|
|
1473
1337
|
}
|
|
1474
1338
|
}
|
|
1475
|
-
];
|
|
1476
|
-
const clauses = [];
|
|
1477
|
-
for (const { key, wrap } of filters) {
|
|
1478
|
-
const raw = value[key];
|
|
1479
|
-
if (raw === void 0 || raw === null) continue;
|
|
1480
|
-
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
1481
|
-
alias: relAlias,
|
|
1482
|
-
model: relModel,
|
|
1483
|
-
path: [...ctx.path, fieldName, key],
|
|
1484
|
-
isSubquery: true,
|
|
1485
|
-
depth: ctx.depth + 1
|
|
1486
|
-
}));
|
|
1487
|
-
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1488
|
-
clauses.push(wrap(sub.clause, j));
|
|
1489
|
-
}
|
|
1490
|
-
if (clauses.length === 0) {
|
|
1491
|
-
throw createError(
|
|
1492
|
-
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
1493
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1494
|
-
);
|
|
1495
1339
|
}
|
|
1496
|
-
return
|
|
1497
|
-
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
1498
|
-
joins: NO_JOINS
|
|
1499
|
-
});
|
|
1340
|
+
return entries;
|
|
1500
1341
|
}
|
|
1501
|
-
function
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
field,
|
|
1508
|
-
relModel,
|
|
1509
|
-
relTable,
|
|
1510
|
-
relAlias,
|
|
1511
|
-
join
|
|
1512
|
-
} = args;
|
|
1513
|
-
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
1514
|
-
if (hasSomeEveryNone) {
|
|
1515
|
-
throw createError(
|
|
1516
|
-
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
1517
|
-
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1518
|
-
);
|
|
1342
|
+
function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
|
|
1343
|
+
var _a;
|
|
1344
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1345
|
+
const cursorEntries = Object.entries(cursor);
|
|
1346
|
+
if (cursorEntries.length === 0) {
|
|
1347
|
+
throw new Error("cursor must have at least one field");
|
|
1519
1348
|
}
|
|
1520
|
-
const
|
|
1521
|
-
const
|
|
1522
|
-
let
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
filterKey = "isNot";
|
|
1529
|
-
filterVal = value.isNot;
|
|
1349
|
+
const cursorAlias = "__tp_cursor_src";
|
|
1350
|
+
const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
|
|
1351
|
+
let orderEntries = buildOrderEntries(orderBy);
|
|
1352
|
+
if (orderEntries.length === 0) {
|
|
1353
|
+
orderEntries = cursorEntries.map(([field]) => ({
|
|
1354
|
+
field,
|
|
1355
|
+
direction: "asc"
|
|
1356
|
+
}));
|
|
1530
1357
|
} else {
|
|
1531
|
-
|
|
1532
|
-
|
|
1358
|
+
orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
|
|
1359
|
+
}
|
|
1360
|
+
const existsExpr = buildCursorRowExistsExpr(
|
|
1361
|
+
tableName,
|
|
1362
|
+
cursorAlias,
|
|
1363
|
+
cursorWhereSql
|
|
1364
|
+
);
|
|
1365
|
+
const outerCursorMatch = buildOuterCursorMatch(
|
|
1366
|
+
cursor,
|
|
1367
|
+
alias,
|
|
1368
|
+
placeholdersByField,
|
|
1369
|
+
params,
|
|
1370
|
+
model
|
|
1371
|
+
);
|
|
1372
|
+
const orClauses = [];
|
|
1373
|
+
for (let level = 0; level < orderEntries.length; level++) {
|
|
1374
|
+
const andParts = [];
|
|
1375
|
+
for (let i = 0; i < level; i++) {
|
|
1376
|
+
const e2 = orderEntries[i];
|
|
1377
|
+
const c2 = col(alias, e2.field, model);
|
|
1378
|
+
const v2 = cursorValueExpr(
|
|
1379
|
+
tableName,
|
|
1380
|
+
cursorAlias,
|
|
1381
|
+
cursorWhereSql,
|
|
1382
|
+
e2.field);
|
|
1383
|
+
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
1384
|
+
}
|
|
1385
|
+
const e = orderEntries[level];
|
|
1386
|
+
const c = col(alias, e.field, model);
|
|
1387
|
+
const v = cursorValueExpr(
|
|
1388
|
+
tableName,
|
|
1389
|
+
cursorAlias,
|
|
1390
|
+
cursorWhereSql,
|
|
1391
|
+
e.field);
|
|
1392
|
+
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
1393
|
+
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
1394
|
+
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
1395
|
+
}
|
|
1396
|
+
const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
|
|
1397
|
+
return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
|
|
1398
|
+
}
|
|
1399
|
+
function buildOrderBy(orderBy, alias, dialect, model) {
|
|
1400
|
+
const entries = buildOrderEntries(orderBy);
|
|
1401
|
+
if (entries.length === 0) return "";
|
|
1402
|
+
const d = dialect != null ? dialect : getGlobalDialect();
|
|
1403
|
+
return buildOrderByFragment(entries, alias, d, model);
|
|
1404
|
+
}
|
|
1405
|
+
function buildOrderByClause(args, alias, dialect, model) {
|
|
1406
|
+
if (!isNotNullish(args.orderBy)) return "";
|
|
1407
|
+
const result = buildOrderBy(args.orderBy, alias, dialect, model);
|
|
1408
|
+
if (!isNonEmptyString(result)) {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
"buildOrderByClause: orderBy specified but produced empty result"
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
function normalizeTakeLike(v) {
|
|
1416
|
+
const n = normalizeIntLike("take", v, {
|
|
1417
|
+
min: Number.MIN_SAFE_INTEGER,
|
|
1418
|
+
max: MAX_LIMIT_OFFSET,
|
|
1419
|
+
allowZero: true
|
|
1420
|
+
});
|
|
1421
|
+
if (typeof n === "number") {
|
|
1422
|
+
if (n === 0) return 0;
|
|
1423
|
+
}
|
|
1424
|
+
return n;
|
|
1425
|
+
}
|
|
1426
|
+
function normalizeSkipLike(v) {
|
|
1427
|
+
return normalizeIntLike("skip", v, {
|
|
1428
|
+
min: 0,
|
|
1429
|
+
max: MAX_LIMIT_OFFSET,
|
|
1430
|
+
allowZero: true
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
function getPaginationParams(method, args) {
|
|
1434
|
+
if (method === "findMany") {
|
|
1435
|
+
return {
|
|
1436
|
+
take: normalizeTakeLike(args.take),
|
|
1437
|
+
skip: normalizeSkipLike(args.skip),
|
|
1438
|
+
cursor: args.cursor
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
if (method === "findFirst") {
|
|
1442
|
+
const skip = normalizeSkipLike(args.skip);
|
|
1443
|
+
return { take: 1, skip: skip != null ? skip : 0 };
|
|
1444
|
+
}
|
|
1445
|
+
if (method === "findUnique") {
|
|
1446
|
+
return { take: 1, skip: 0 };
|
|
1447
|
+
}
|
|
1448
|
+
return {};
|
|
1449
|
+
}
|
|
1450
|
+
function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
|
|
1451
|
+
const entries = Object.entries(val).filter(
|
|
1452
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
1453
|
+
);
|
|
1454
|
+
if (entries.length === 0) return "";
|
|
1455
|
+
const clauses = [];
|
|
1456
|
+
for (const [subOp, subVal] of entries) {
|
|
1457
|
+
const sub = buildOp(expr, subOp, subVal, params, dialect);
|
|
1458
|
+
if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
|
|
1459
|
+
}
|
|
1460
|
+
if (clauses.length === 0) return "";
|
|
1461
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1462
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
|
|
1463
|
+
}
|
|
1464
|
+
function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
|
|
1465
|
+
if (val === void 0) return "";
|
|
1466
|
+
if (val === null) {
|
|
1467
|
+
return handleNullValue(expr, op);
|
|
1468
|
+
}
|
|
1469
|
+
if (op === Ops.NOT && isPlainObject(val)) {
|
|
1470
|
+
return handleNotOperator(expr, val, params, mode, fieldType, dialect);
|
|
1471
|
+
}
|
|
1472
|
+
if (op === Ops.NOT) {
|
|
1473
|
+
const placeholder = params.addAuto(val);
|
|
1474
|
+
return `${expr} <> ${placeholder}`;
|
|
1475
|
+
}
|
|
1476
|
+
if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
|
|
1477
|
+
const placeholder = params.addAuto(val);
|
|
1478
|
+
return caseInsensitiveEquals(expr, placeholder);
|
|
1479
|
+
}
|
|
1480
|
+
const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
|
|
1481
|
+
Ops.CONTAINS,
|
|
1482
|
+
Ops.STARTS_WITH,
|
|
1483
|
+
Ops.ENDS_WITH
|
|
1484
|
+
]);
|
|
1485
|
+
if (STRING_LIKE_OPS.has(op)) {
|
|
1486
|
+
if (!isNotNullish(dialect)) {
|
|
1487
|
+
throw createError(`Like operators require a SQL dialect`, {
|
|
1488
|
+
operator: op
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
return handleLikeOperator(expr, op, val, params, mode, dialect);
|
|
1492
|
+
}
|
|
1493
|
+
if (op === Ops.IN || op === Ops.NOT_IN) {
|
|
1494
|
+
if (!isNotNullish(dialect)) {
|
|
1495
|
+
throw createError(`IN operators require a SQL dialect`, { operator: op });
|
|
1496
|
+
}
|
|
1497
|
+
return handleInOperator(expr, op, val, params, dialect);
|
|
1498
|
+
}
|
|
1499
|
+
return handleComparisonOperator(expr, op, val, params);
|
|
1500
|
+
}
|
|
1501
|
+
function handleNullValue(expr, op) {
|
|
1502
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1503
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1504
|
+
throw createError(`Operator '${op}' doesn't support null`, { operator: op });
|
|
1505
|
+
}
|
|
1506
|
+
function normalizeMode(v) {
|
|
1507
|
+
if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
|
|
1508
|
+
if (v === Modes.DEFAULT) return Modes.DEFAULT;
|
|
1509
|
+
return void 0;
|
|
1510
|
+
}
|
|
1511
|
+
function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
|
|
1512
|
+
const innerMode = normalizeMode(val.mode);
|
|
1513
|
+
const effectiveMode = innerMode != null ? innerMode : outerMode;
|
|
1514
|
+
return buildNotComposite(
|
|
1515
|
+
expr,
|
|
1516
|
+
val,
|
|
1517
|
+
params,
|
|
1518
|
+
dialect,
|
|
1519
|
+
(e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
|
|
1520
|
+
` ${SQL_TEMPLATES.AND} `
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
function buildDynamicLikePattern(op, placeholder, dialect) {
|
|
1524
|
+
if (dialect === "postgres") {
|
|
1525
|
+
switch (op) {
|
|
1526
|
+
case Ops.CONTAINS:
|
|
1527
|
+
return `('%' || ${placeholder} || '%')`;
|
|
1528
|
+
case Ops.STARTS_WITH:
|
|
1529
|
+
return `(${placeholder} || '%')`;
|
|
1530
|
+
case Ops.ENDS_WITH:
|
|
1531
|
+
return `('%' || ${placeholder})`;
|
|
1532
|
+
default:
|
|
1533
|
+
return placeholder;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
switch (op) {
|
|
1537
|
+
case Ops.CONTAINS:
|
|
1538
|
+
return `('%' || ${placeholder} || '%')`;
|
|
1539
|
+
case Ops.STARTS_WITH:
|
|
1540
|
+
return `(${placeholder} || '%')`;
|
|
1541
|
+
case Ops.ENDS_WITH:
|
|
1542
|
+
return `('%' || ${placeholder})`;
|
|
1543
|
+
default:
|
|
1544
|
+
return placeholder;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function handleLikeOperator(expr, op, val, params, mode, dialect) {
|
|
1548
|
+
if (val === void 0) return "";
|
|
1549
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1550
|
+
const placeholder2 = params.addAuto(val);
|
|
1551
|
+
const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
|
|
1552
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1553
|
+
return caseInsensitiveLike(expr, patternExpr, dialect);
|
|
1554
|
+
}
|
|
1555
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
|
|
1556
|
+
}
|
|
1557
|
+
const placeholder = params.add(Wildcards[op](String(val)));
|
|
1558
|
+
if (mode === Modes.INSENSITIVE) {
|
|
1559
|
+
return caseInsensitiveLike(expr, placeholder, dialect);
|
|
1560
|
+
}
|
|
1561
|
+
return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
|
|
1562
|
+
}
|
|
1563
|
+
function handleInOperator(expr, op, val, params, dialect) {
|
|
1564
|
+
if (val === void 0) return "";
|
|
1565
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1566
|
+
const placeholder2 = params.addAuto(val);
|
|
1567
|
+
return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
|
|
1568
|
+
}
|
|
1569
|
+
if (!Array.isArray(val)) {
|
|
1570
|
+
throw createError(`IN operators require array value`, {
|
|
1571
|
+
operator: op,
|
|
1572
|
+
value: val
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
if (val.length === 0) {
|
|
1576
|
+
return op === Ops.IN ? "0=1" : "1=1";
|
|
1577
|
+
}
|
|
1578
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1579
|
+
const placeholder = params.add(paramValue);
|
|
1580
|
+
return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
|
|
1581
|
+
}
|
|
1582
|
+
function handleComparisonOperator(expr, op, val, params) {
|
|
1583
|
+
if (val === void 0) return "";
|
|
1584
|
+
const COMPARISON_OPS2 = {
|
|
1585
|
+
[Ops.EQUALS]: "=",
|
|
1586
|
+
[Ops.GT]: ">",
|
|
1587
|
+
[Ops.GTE]: ">=",
|
|
1588
|
+
[Ops.LT]: "<",
|
|
1589
|
+
[Ops.LTE]: "<="
|
|
1590
|
+
};
|
|
1591
|
+
const sqlOp = COMPARISON_OPS2[op];
|
|
1592
|
+
if (!sqlOp) {
|
|
1593
|
+
throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
|
|
1594
|
+
}
|
|
1595
|
+
const placeholder = params.addAuto(val);
|
|
1596
|
+
return `${expr} ${sqlOp} ${placeholder}`;
|
|
1597
|
+
}
|
|
1598
|
+
function buildArrayParam(val, params, dialect) {
|
|
1599
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1600
|
+
return params.addAuto(val);
|
|
1601
|
+
}
|
|
1602
|
+
if (!Array.isArray(val)) {
|
|
1603
|
+
throw createError(`Array operation requires array value`, { value: val });
|
|
1604
|
+
}
|
|
1605
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1606
|
+
return params.add(paramValue);
|
|
1607
|
+
}
|
|
1608
|
+
function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
|
|
1609
|
+
if (val === void 0) return "";
|
|
1610
|
+
if (val === null) {
|
|
1611
|
+
if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
|
|
1612
|
+
if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1613
|
+
}
|
|
1614
|
+
const cast = getArrayType(fieldType, dialect);
|
|
1615
|
+
if (op === Ops.EQUALS) {
|
|
1616
|
+
return handleArrayEquals(expr, val, params, cast, dialect);
|
|
1617
|
+
}
|
|
1618
|
+
if (op === Ops.NOT) {
|
|
1619
|
+
return handleArrayNot(expr, val, params, cast, dialect);
|
|
1620
|
+
}
|
|
1621
|
+
switch (op) {
|
|
1622
|
+
case Ops.HAS:
|
|
1623
|
+
return handleArrayHas(expr, val, params, cast, dialect);
|
|
1624
|
+
case Ops.HAS_SOME:
|
|
1625
|
+
return handleArrayHasSome(expr, val, params, cast, dialect);
|
|
1626
|
+
case Ops.HAS_EVERY:
|
|
1627
|
+
return handleArrayHasEvery(expr, val, params, cast, dialect);
|
|
1628
|
+
case Ops.IS_EMPTY:
|
|
1629
|
+
return handleArrayIsEmpty(expr, val, dialect);
|
|
1630
|
+
default:
|
|
1631
|
+
throw createError(`Unknown array operator: ${op}`, { operator: op });
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
function handleArrayEquals(expr, val, params, cast, dialect) {
|
|
1635
|
+
if (val === void 0) return "";
|
|
1636
|
+
if (isEmptyArray(val)) {
|
|
1637
|
+
return arrayIsEmpty(expr, dialect);
|
|
1638
|
+
}
|
|
1639
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1640
|
+
return arrayEquals(expr, placeholder, cast, dialect);
|
|
1641
|
+
}
|
|
1642
|
+
function handleArrayNot(expr, val, params, cast, dialect) {
|
|
1643
|
+
if (val === void 0) return "";
|
|
1644
|
+
let target = val;
|
|
1645
|
+
if (isPlainObject(val)) {
|
|
1646
|
+
const entries = Object.entries(val).filter(([, v]) => v !== void 0);
|
|
1647
|
+
if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
|
|
1648
|
+
target = entries[0][1];
|
|
1649
|
+
} else {
|
|
1650
|
+
throw createError(`Array NOT only supports { equals: ... } shape`, {
|
|
1651
|
+
operator: Ops.NOT,
|
|
1652
|
+
value: val
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
if (target === null) {
|
|
1657
|
+
return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1658
|
+
}
|
|
1659
|
+
if (isEmptyArray(target)) {
|
|
1660
|
+
return arrayIsNotEmpty(expr, dialect);
|
|
1533
1661
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1662
|
+
const placeholder = buildArrayParam(target, params, dialect);
|
|
1663
|
+
return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
|
|
1664
|
+
}
|
|
1665
|
+
function handleArrayHas(expr, val, params, cast, dialect) {
|
|
1666
|
+
if (val === void 0) return "";
|
|
1667
|
+
if (val === null) {
|
|
1668
|
+
throw createError(`has requires scalar value`, {
|
|
1669
|
+
operator: Ops.HAS,
|
|
1670
|
+
value: val
|
|
1538
1671
|
});
|
|
1539
1672
|
}
|
|
1540
|
-
if (
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
ctx.alias,
|
|
1545
|
-
relTable,
|
|
1546
|
-
relAlias,
|
|
1547
|
-
join,
|
|
1548
|
-
wantNull
|
|
1549
|
-
);
|
|
1550
|
-
return Object.freeze({
|
|
1551
|
-
clause: clause2,
|
|
1552
|
-
joins: NO_JOINS
|
|
1673
|
+
if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
|
|
1674
|
+
throw createError(`has requires scalar value (single element), not array`, {
|
|
1675
|
+
operator: Ops.HAS,
|
|
1676
|
+
value: val
|
|
1553
1677
|
});
|
|
1554
1678
|
}
|
|
1555
|
-
if (
|
|
1556
|
-
throw createError(
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
field: fieldName,
|
|
1560
|
-
path: ctx.path,
|
|
1561
|
-
modelName: ctx.model.name,
|
|
1562
|
-
value: filterVal
|
|
1563
|
-
}
|
|
1564
|
-
);
|
|
1565
|
-
}
|
|
1566
|
-
const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
|
|
1567
|
-
alias: relAlias,
|
|
1568
|
-
model: relModel,
|
|
1569
|
-
path: [...ctx.path, fieldName, filterKey],
|
|
1570
|
-
isSubquery: true,
|
|
1571
|
-
depth: ctx.depth + 1
|
|
1572
|
-
}));
|
|
1573
|
-
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join, sub) : buildToOneNotExistsMatch(relTable, relAlias, join, sub);
|
|
1574
|
-
return Object.freeze({
|
|
1575
|
-
clause,
|
|
1576
|
-
joins: NO_JOINS
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
function ensureRelationFilterObject(fieldName, value, ctx) {
|
|
1580
|
-
if (!isPlainObject(value)) {
|
|
1581
|
-
throw createError(`Relation filter '${fieldName}' must be an object`, {
|
|
1582
|
-
path: [...ctx.path, fieldName],
|
|
1583
|
-
field: fieldName,
|
|
1584
|
-
modelName: ctx.model.name,
|
|
1585
|
-
value
|
|
1679
|
+
if (isPlainObject(val)) {
|
|
1680
|
+
throw createError(`has requires scalar value`, {
|
|
1681
|
+
operator: Ops.HAS,
|
|
1682
|
+
value: val
|
|
1586
1683
|
});
|
|
1587
1684
|
}
|
|
1685
|
+
const placeholder = params.addAuto(val);
|
|
1686
|
+
return arrayContains(expr, placeholder, cast, dialect);
|
|
1588
1687
|
}
|
|
1589
|
-
function
|
|
1590
|
-
|
|
1591
|
-
if (
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1688
|
+
function handleArrayHasSome(expr, val, params, cast, dialect) {
|
|
1689
|
+
if (val === void 0) return "";
|
|
1690
|
+
if (schemaParser.isDynamicParameter(val)) {
|
|
1691
|
+
const placeholder2 = params.addAuto(val);
|
|
1692
|
+
return arrayOverlaps(expr, placeholder2, cast, dialect);
|
|
1693
|
+
}
|
|
1694
|
+
if (!Array.isArray(val)) {
|
|
1695
|
+
throw createError(`hasSome requires array value`, {
|
|
1696
|
+
operator: Ops.HAS_SOME,
|
|
1697
|
+
value: val
|
|
1596
1698
|
});
|
|
1597
1699
|
}
|
|
1598
|
-
|
|
1599
|
-
if (!isNotNullish(relModel)) {
|
|
1700
|
+
if (val.length > LIMITS.MAX_ARRAY_SIZE) {
|
|
1600
1701
|
throw createError(
|
|
1601
|
-
`
|
|
1602
|
-
{
|
|
1603
|
-
field: fieldName,
|
|
1604
|
-
path: ctx.path,
|
|
1605
|
-
modelName: ctx.model.name
|
|
1606
|
-
}
|
|
1702
|
+
`Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
|
|
1703
|
+
{ operator: Ops.HAS_SOME, value: `[${val.length} items]` }
|
|
1607
1704
|
);
|
|
1608
1705
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
);
|
|
1614
|
-
const relAlias = ctx.aliasGen.next(fieldName);
|
|
1615
|
-
const join = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
1616
|
-
const args = {
|
|
1617
|
-
fieldName,
|
|
1618
|
-
value,
|
|
1619
|
-
ctx,
|
|
1620
|
-
whereBuilder,
|
|
1621
|
-
field,
|
|
1622
|
-
relModel,
|
|
1623
|
-
relTable,
|
|
1624
|
-
relAlias,
|
|
1625
|
-
join
|
|
1626
|
-
};
|
|
1627
|
-
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
1628
|
-
return buildToOneRelationFilters(args);
|
|
1629
|
-
}
|
|
1630
|
-
function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
|
|
1631
|
-
ensureRelationFilterObject(fieldName, value, ctx);
|
|
1632
|
-
return buildRelation(fieldName, value, ctx, whereBuilder);
|
|
1633
|
-
}
|
|
1634
|
-
function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
1635
|
-
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
1706
|
+
if (val.length === 0) return "0=1";
|
|
1707
|
+
const paramValue = prepareArrayParam(val, dialect);
|
|
1708
|
+
const placeholder = params.add(paramValue);
|
|
1709
|
+
return arrayOverlaps(expr, placeholder, cast, dialect);
|
|
1636
1710
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
if (!isNotNullish(field)) {
|
|
1642
|
-
throw createError(`Field '${name}' does not exist on '${model.name}'`, {
|
|
1643
|
-
field: name,
|
|
1644
|
-
path,
|
|
1645
|
-
modelName: model.name,
|
|
1646
|
-
availableFields: model.fields.map((f) => f.name)
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
return field;
|
|
1711
|
+
function handleArrayHasEvery(expr, val, params, cast, dialect) {
|
|
1712
|
+
if (val === void 0) return "";
|
|
1713
|
+
const placeholder = buildArrayParam(val, params, dialect);
|
|
1714
|
+
return arrayContainsAll(expr, placeholder, cast, dialect);
|
|
1650
1715
|
}
|
|
1651
|
-
function
|
|
1652
|
-
if (
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
Ops.HAS_EVERY,
|
|
1657
|
-
Ops.IS_EMPTY
|
|
1658
|
-
]);
|
|
1659
|
-
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
1660
|
-
Ops.PATH,
|
|
1661
|
-
Ops.STRING_CONTAINS,
|
|
1662
|
-
Ops.STRING_STARTS_WITH,
|
|
1663
|
-
Ops.STRING_ENDS_WITH
|
|
1664
|
-
]);
|
|
1665
|
-
const isArrayOp = ARRAY_OPS.has(op);
|
|
1666
|
-
const isFieldArray = isArrayType(fieldType);
|
|
1667
|
-
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
1668
|
-
if (arrayOpMismatch) {
|
|
1669
|
-
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
1670
|
-
operator: op,
|
|
1671
|
-
field: fieldName,
|
|
1672
|
-
path,
|
|
1673
|
-
modelName
|
|
1674
|
-
});
|
|
1675
|
-
}
|
|
1676
|
-
const isJsonOp = JSON_OPS.has(op);
|
|
1677
|
-
const isFieldJson = isJsonType(fieldType);
|
|
1678
|
-
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
1679
|
-
if (jsonOpMismatch) {
|
|
1680
|
-
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
1681
|
-
operator: op,
|
|
1682
|
-
field: fieldName,
|
|
1683
|
-
path,
|
|
1684
|
-
modelName
|
|
1716
|
+
function handleArrayIsEmpty(expr, val, dialect) {
|
|
1717
|
+
if (typeof val !== "boolean") {
|
|
1718
|
+
throw createError(`isEmpty requires boolean value`, {
|
|
1719
|
+
operator: Ops.IS_EMPTY,
|
|
1720
|
+
value: val
|
|
1685
1721
|
});
|
|
1686
1722
|
}
|
|
1723
|
+
return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
|
|
1687
1724
|
}
|
|
1688
1725
|
|
|
1689
|
-
// src/builder/where/
|
|
1690
|
-
var
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1726
|
+
// src/builder/where/operators-json.ts
|
|
1727
|
+
var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
|
|
1728
|
+
function validateJsonPathSegments(segments) {
|
|
1729
|
+
for (const segment of segments) {
|
|
1730
|
+
if (typeof segment !== "string") {
|
|
1731
|
+
throw createError("JSON path segments must be strings", {
|
|
1732
|
+
operator: Ops.PATH,
|
|
1733
|
+
value: segment
|
|
1696
1734
|
});
|
|
1697
1735
|
}
|
|
1698
|
-
|
|
1736
|
+
if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
|
|
1737
|
+
throw createError(
|
|
1738
|
+
`Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
|
|
1739
|
+
{ operator: Ops.PATH, value: segment }
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1699
1742
|
}
|
|
1700
|
-
};
|
|
1701
|
-
var MAX_QUERY_DEPTH = 50;
|
|
1702
|
-
var EMPTY_JOINS = Object.freeze([]);
|
|
1703
|
-
var whereBuilderInstance = new WhereBuilder();
|
|
1704
|
-
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
1705
|
-
return Object.freeze({ clause, joins });
|
|
1706
1743
|
}
|
|
1707
|
-
function
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1744
|
+
function buildJsonOperator(expr, op, val, params, dialect) {
|
|
1745
|
+
if (val === void 0) return "";
|
|
1746
|
+
if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
|
|
1747
|
+
return handleJsonPath(expr, val, params, dialect);
|
|
1748
|
+
}
|
|
1749
|
+
const jsonWildcards = {
|
|
1750
|
+
[Ops.STRING_CONTAINS]: (v) => `%${v}%`,
|
|
1751
|
+
[Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
|
|
1752
|
+
[Ops.STRING_ENDS_WITH]: (v) => `%${v}`
|
|
1753
|
+
};
|
|
1754
|
+
if (op in jsonWildcards) {
|
|
1755
|
+
return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
|
|
1756
|
+
}
|
|
1757
|
+
throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
|
|
1758
|
+
}
|
|
1759
|
+
function handleJsonPath(expr, val, params, dialect) {
|
|
1760
|
+
const v = val;
|
|
1761
|
+
if (!Array.isArray(v.path)) {
|
|
1762
|
+
throw createError("JSON path must be an array", { operator: Ops.PATH });
|
|
1763
|
+
}
|
|
1764
|
+
if (v.path.length === 0) {
|
|
1765
|
+
throw createError("JSON path cannot be empty", { operator: Ops.PATH });
|
|
1766
|
+
}
|
|
1767
|
+
validateJsonPathSegments(v.path);
|
|
1768
|
+
const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
|
|
1769
|
+
const rawOps = [
|
|
1770
|
+
["=", v.equals],
|
|
1771
|
+
[">", v.gt],
|
|
1772
|
+
[">=", v.gte],
|
|
1773
|
+
["<", v.lt],
|
|
1774
|
+
["<=", v.lte]
|
|
1775
|
+
];
|
|
1776
|
+
const ops = rawOps.filter(
|
|
1777
|
+
([, value]) => value !== void 0
|
|
1778
|
+
);
|
|
1779
|
+
if (ops.length === 0) {
|
|
1780
|
+
throw createError("JSON path query missing comparison operator", {
|
|
1781
|
+
operator: Ops.PATH
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
const parts = [];
|
|
1785
|
+
for (const [sqlOp, value] of ops) {
|
|
1786
|
+
if (value === null) {
|
|
1787
|
+
const base2 = jsonExtractText(expr, pathExpr, dialect);
|
|
1788
|
+
parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
|
|
1789
|
+
continue;
|
|
1715
1790
|
}
|
|
1791
|
+
const valPh = params.add(value);
|
|
1792
|
+
const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
|
|
1793
|
+
parts.push(`${base} ${sqlOp} ${valPh}`);
|
|
1716
1794
|
}
|
|
1717
|
-
return
|
|
1718
|
-
}
|
|
1719
|
-
function appendResult(result, clauses, allJoins) {
|
|
1720
|
-
if (isValidWhereClause(result.clause)) clauses.push(result.clause);
|
|
1721
|
-
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
1722
|
-
}
|
|
1723
|
-
function asLogicalOperator(key) {
|
|
1724
|
-
if (key === LogicalOps.AND) return "AND";
|
|
1725
|
-
if (key === LogicalOps.OR) return "OR";
|
|
1726
|
-
if (key === LogicalOps.NOT) return "NOT";
|
|
1727
|
-
return null;
|
|
1728
|
-
}
|
|
1729
|
-
function nextContext(ctx) {
|
|
1730
|
-
return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
|
|
1795
|
+
return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
1731
1796
|
}
|
|
1732
|
-
function
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1797
|
+
function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
|
|
1798
|
+
if (!isNotNullish(val)) {
|
|
1799
|
+
throw createError(`JSON string operator requires non-null value`, {
|
|
1800
|
+
operator: op,
|
|
1801
|
+
value: val
|
|
1802
|
+
});
|
|
1736
1803
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
if (isRelationField(key, ctx.model)) {
|
|
1743
|
-
if (!isPlainObject(value)) {
|
|
1744
|
-
throw createError(`Relation filter '${key}' must be an object`, {
|
|
1745
|
-
path: [...ctx.path, key],
|
|
1746
|
-
field: key,
|
|
1747
|
-
modelName: ctx.model.name,
|
|
1748
|
-
value
|
|
1749
|
-
});
|
|
1750
|
-
}
|
|
1751
|
-
return buildRelationFilter(key, value, ctx, builder);
|
|
1804
|
+
if (isPlainObject(val) || Array.isArray(val)) {
|
|
1805
|
+
throw createError(`JSON string operator requires scalar value`, {
|
|
1806
|
+
operator: op,
|
|
1807
|
+
value: val
|
|
1808
|
+
});
|
|
1752
1809
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
function buildWhereInternal(where, ctx, builder) {
|
|
1756
|
-
if (ctx.depth > MAX_QUERY_DEPTH) {
|
|
1810
|
+
const strVal = String(val);
|
|
1811
|
+
if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
|
|
1757
1812
|
throw createError(
|
|
1758
|
-
`
|
|
1759
|
-
{
|
|
1813
|
+
`String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
|
|
1814
|
+
{ operator: op }
|
|
1760
1815
|
);
|
|
1761
1816
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1817
|
+
const placeholder = params.add(wildcards[op](strVal));
|
|
1818
|
+
const jsonText = jsonToText(expr, dialect);
|
|
1819
|
+
return caseInsensitiveLike(jsonText, placeholder, dialect);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// src/builder/where/relations.ts
|
|
1823
|
+
var NO_JOINS = Object.freeze([]);
|
|
1824
|
+
function isListRelation(fieldType) {
|
|
1825
|
+
return typeof fieldType === "string" && fieldType.endsWith("[]");
|
|
1826
|
+
}
|
|
1827
|
+
function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join, wantNull) {
|
|
1828
|
+
const isLocal = field.isForeignKeyLocal === true;
|
|
1829
|
+
const fkFields = normalizeKeyList(field.foreignKey);
|
|
1830
|
+
if (isLocal) {
|
|
1831
|
+
if (fkFields.length === 0) {
|
|
1832
|
+
throw createError(`Relation '${field.name}' is missing foreignKey`, {
|
|
1833
|
+
field: field.name
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
const parts = fkFields.map((fk) => {
|
|
1837
|
+
const safe = fk.replace(/"/g, '""');
|
|
1838
|
+
const expr = `${parentAlias}."${safe}"`;
|
|
1839
|
+
return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
|
|
1840
|
+
});
|
|
1841
|
+
if (parts.length === 1) return parts[0];
|
|
1842
|
+
return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
|
|
1771
1843
|
}
|
|
1772
|
-
const
|
|
1773
|
-
return
|
|
1844
|
+
const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join})`;
|
|
1845
|
+
return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
|
|
1774
1846
|
}
|
|
1775
|
-
function
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1847
|
+
function buildToOneExistsMatch(relTable, relAlias, join, sub) {
|
|
1848
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1849
|
+
return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1850
|
+
}
|
|
1851
|
+
function buildToOneNotExistsMatch(relTable, relAlias, join, sub) {
|
|
1852
|
+
const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1853
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${sub.clause})`;
|
|
1854
|
+
}
|
|
1855
|
+
function buildListRelationFilters(args) {
|
|
1856
|
+
const {
|
|
1857
|
+
fieldName,
|
|
1858
|
+
value,
|
|
1859
|
+
ctx,
|
|
1860
|
+
whereBuilder,
|
|
1861
|
+
relModel,
|
|
1862
|
+
relTable,
|
|
1863
|
+
relAlias,
|
|
1864
|
+
join
|
|
1865
|
+
} = args;
|
|
1866
|
+
const noneValue = value[RelationFilters.NONE];
|
|
1867
|
+
if (noneValue !== void 0 && noneValue !== null) {
|
|
1868
|
+
const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
|
|
1869
|
+
alias: relAlias,
|
|
1870
|
+
model: relModel,
|
|
1871
|
+
path: [...ctx.path, fieldName, RelationFilters.NONE],
|
|
1872
|
+
isSubquery: true,
|
|
1873
|
+
depth: ctx.depth + 1
|
|
1874
|
+
}));
|
|
1875
|
+
const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
|
|
1876
|
+
const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
|
|
1877
|
+
if (canOptimize) {
|
|
1878
|
+
const checkField = relModel.fields.find(
|
|
1879
|
+
(f) => !f.isRelation && f.isRequired && f.name !== "id"
|
|
1880
|
+
) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
|
|
1881
|
+
if (checkField) {
|
|
1882
|
+
const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join}`;
|
|
1883
|
+
const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
|
|
1884
|
+
return Object.freeze({
|
|
1885
|
+
clause: whereClause,
|
|
1886
|
+
joins: [leftJoinSql]
|
|
1786
1887
|
});
|
|
1787
1888
|
}
|
|
1788
|
-
out.push(v);
|
|
1789
1889
|
}
|
|
1790
|
-
return out;
|
|
1791
|
-
}
|
|
1792
|
-
if (isPlainObject(value)) {
|
|
1793
|
-
return [value];
|
|
1794
1890
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1891
|
+
const filters = [
|
|
1892
|
+
{
|
|
1893
|
+
key: RelationFilters.SOME,
|
|
1894
|
+
wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${c})`
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
key: RelationFilters.EVERY,
|
|
1898
|
+
wrap: (c, j) => `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${SQL_TEMPLATES.NOT} (${c}))`
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
key: RelationFilters.NONE,
|
|
1902
|
+
wrap: (c, j) => {
|
|
1903
|
+
const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
|
|
1904
|
+
return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join}${condition})`;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
];
|
|
1803
1908
|
const clauses = [];
|
|
1804
|
-
for (
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1909
|
+
for (const { key, wrap } of filters) {
|
|
1910
|
+
const raw = value[key];
|
|
1911
|
+
if (raw === void 0 || raw === null) continue;
|
|
1912
|
+
const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
|
|
1913
|
+
alias: relAlias,
|
|
1914
|
+
model: relModel,
|
|
1915
|
+
path: [...ctx.path, fieldName, key],
|
|
1916
|
+
isSubquery: true,
|
|
1807
1917
|
depth: ctx.depth + 1
|
|
1808
1918
|
}));
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
clauses.push(`(${result.clause})`);
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
return {
|
|
1815
|
-
joins: dedupePreserveOrder(allJoins),
|
|
1816
|
-
clauses: Object.freeze(clauses)
|
|
1817
|
-
};
|
|
1818
|
-
}
|
|
1819
|
-
function buildLogicalClause(operator, clauses) {
|
|
1820
|
-
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
1821
|
-
if (operator === "NOT") {
|
|
1822
|
-
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
1823
|
-
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
1824
|
-
}
|
|
1825
|
-
return clauses.join(` ${operator} `);
|
|
1826
|
-
}
|
|
1827
|
-
function buildLogical(operator, value, ctx, builder) {
|
|
1828
|
-
const conditions = normalizeLogicalValue(operator, value, ctx);
|
|
1829
|
-
if (conditions.length === 0) {
|
|
1830
|
-
const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
|
|
1831
|
-
return freezeResult(clause2, EMPTY_JOINS);
|
|
1832
|
-
}
|
|
1833
|
-
const { joins, clauses } = collectLogicalParts(
|
|
1834
|
-
operator,
|
|
1835
|
-
conditions,
|
|
1836
|
-
ctx,
|
|
1837
|
-
builder
|
|
1838
|
-
);
|
|
1839
|
-
const clause = buildLogicalClause(operator, clauses);
|
|
1840
|
-
return freezeResult(clause, joins);
|
|
1841
|
-
}
|
|
1842
|
-
function buildScalarField(fieldName, value, ctx) {
|
|
1843
|
-
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
1844
|
-
const expr = col(ctx.alias, fieldName, ctx.model);
|
|
1845
|
-
if (value === null) {
|
|
1846
|
-
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
1919
|
+
const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
|
|
1920
|
+
clauses.push(wrap(sub.clause, j));
|
|
1847
1921
|
}
|
|
1848
|
-
if (
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
);
|
|
1853
|
-
if (ops.length === 0) {
|
|
1854
|
-
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
1855
|
-
}
|
|
1856
|
-
const parts = [];
|
|
1857
|
-
for (const [op, val] of ops) {
|
|
1858
|
-
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
1859
|
-
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
1860
|
-
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
1861
|
-
}
|
|
1862
|
-
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
1863
|
-
return freezeResult(clause2, EMPTY_JOINS);
|
|
1922
|
+
if (clauses.length === 0) {
|
|
1923
|
+
throw createError(
|
|
1924
|
+
`List relation '${fieldName}' requires one of { some, every, none }`,
|
|
1925
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1926
|
+
);
|
|
1864
1927
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1928
|
+
return Object.freeze({
|
|
1929
|
+
clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
|
|
1930
|
+
joins: NO_JOINS
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
function buildToOneRelationFilters(args) {
|
|
1934
|
+
const {
|
|
1935
|
+
fieldName,
|
|
1868
1936
|
value,
|
|
1869
1937
|
ctx,
|
|
1870
|
-
|
|
1871
|
-
field
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1938
|
+
whereBuilder,
|
|
1939
|
+
field,
|
|
1940
|
+
relModel,
|
|
1941
|
+
relTable,
|
|
1942
|
+
relAlias,
|
|
1943
|
+
join
|
|
1944
|
+
} = args;
|
|
1945
|
+
const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
|
|
1946
|
+
if (hasSomeEveryNone) {
|
|
1947
|
+
throw createError(
|
|
1948
|
+
`To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
|
|
1949
|
+
{ field: fieldName, path: ctx.path, modelName: ctx.model.name }
|
|
1950
|
+
);
|
|
1878
1951
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1952
|
+
const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
|
|
1953
|
+
const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
|
|
1954
|
+
let filterKey;
|
|
1955
|
+
let filterVal;
|
|
1956
|
+
if (hasIs) {
|
|
1957
|
+
filterKey = "is";
|
|
1958
|
+
filterVal = value.is;
|
|
1959
|
+
} else if (hasIsNot) {
|
|
1960
|
+
filterKey = "isNot";
|
|
1961
|
+
filterVal = value.isNot;
|
|
1962
|
+
} else {
|
|
1963
|
+
filterKey = "is";
|
|
1964
|
+
filterVal = value;
|
|
1889
1965
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
);
|
|
1920
|
-
}
|
|
1921
|
-
const base = toSafeSqlIdentifier(baseName);
|
|
1922
|
-
const suffix = `_${counter}`;
|
|
1923
|
-
const maxLen = 63;
|
|
1924
|
-
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
1925
|
-
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
1926
|
-
const alias = `${trimmedBase}${suffix}`;
|
|
1927
|
-
counter += 1;
|
|
1928
|
-
if (usedAliases.has(alias)) {
|
|
1929
|
-
throw new Error(
|
|
1930
|
-
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
1931
|
-
);
|
|
1966
|
+
if (filterVal === void 0) {
|
|
1967
|
+
return Object.freeze({
|
|
1968
|
+
clause: DEFAULT_WHERE_CLAUSE,
|
|
1969
|
+
joins: NO_JOINS
|
|
1970
|
+
});
|
|
1971
|
+
}
|
|
1972
|
+
if (filterVal === null) {
|
|
1973
|
+
const wantNull = filterKey === "is";
|
|
1974
|
+
const clause2 = buildToOneNullCheck(
|
|
1975
|
+
field,
|
|
1976
|
+
ctx.alias,
|
|
1977
|
+
relTable,
|
|
1978
|
+
relAlias,
|
|
1979
|
+
join,
|
|
1980
|
+
wantNull
|
|
1981
|
+
);
|
|
1982
|
+
return Object.freeze({
|
|
1983
|
+
clause: clause2,
|
|
1984
|
+
joins: NO_JOINS
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
if (!isPlainObject(filterVal)) {
|
|
1988
|
+
throw createError(
|
|
1989
|
+
`Relation '${fieldName}' filter must be an object or null`,
|
|
1990
|
+
{
|
|
1991
|
+
field: fieldName,
|
|
1992
|
+
path: ctx.path,
|
|
1993
|
+
modelName: ctx.model.name,
|
|
1994
|
+
value: filterVal
|
|
1932
1995
|
}
|
|
1933
|
-
usedAliases.add(alias);
|
|
1934
|
-
return alias;
|
|
1935
|
-
}
|
|
1936
|
-
};
|
|
1937
|
-
}
|
|
1938
|
-
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
1939
|
-
function assertSameLength(params, mappings) {
|
|
1940
|
-
if (params.length !== mappings.length) {
|
|
1941
|
-
throw new Error(
|
|
1942
|
-
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
1943
1996
|
);
|
|
1944
1997
|
}
|
|
1998
|
+
const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
|
|
1999
|
+
alias: relAlias,
|
|
2000
|
+
model: relModel,
|
|
2001
|
+
path: [...ctx.path, fieldName, filterKey],
|
|
2002
|
+
isSubquery: true,
|
|
2003
|
+
depth: ctx.depth + 1
|
|
2004
|
+
}));
|
|
2005
|
+
const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join, sub) : buildToOneNotExistsMatch(relTable, relAlias, join, sub);
|
|
2006
|
+
return Object.freeze({
|
|
2007
|
+
clause,
|
|
2008
|
+
joins: NO_JOINS
|
|
2009
|
+
});
|
|
1945
2010
|
}
|
|
1946
|
-
function
|
|
1947
|
-
if (!
|
|
1948
|
-
throw
|
|
2011
|
+
function ensureRelationFilterObject(fieldName, value, ctx) {
|
|
2012
|
+
if (!isPlainObject(value)) {
|
|
2013
|
+
throw createError(`Relation filter '${fieldName}' must be an object`, {
|
|
2014
|
+
path: [...ctx.path, fieldName],
|
|
2015
|
+
field: fieldName,
|
|
2016
|
+
modelName: ctx.model.name,
|
|
2017
|
+
value
|
|
2018
|
+
});
|
|
1949
2019
|
}
|
|
1950
2020
|
}
|
|
1951
|
-
function
|
|
1952
|
-
const
|
|
1953
|
-
if (
|
|
1954
|
-
throw
|
|
1955
|
-
|
|
2021
|
+
function buildRelation(fieldName, value, ctx, whereBuilder) {
|
|
2022
|
+
const field = ctx.model.fields.find((f) => f.name === fieldName);
|
|
2023
|
+
if (!isValidRelationField(field)) {
|
|
2024
|
+
throw createError(`Invalid relation '${fieldName}'`, {
|
|
2025
|
+
field: fieldName,
|
|
2026
|
+
path: ctx.path,
|
|
2027
|
+
modelName: ctx.model.name
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
|
|
2031
|
+
if (!isNotNullish(relModel)) {
|
|
2032
|
+
throw createError(
|
|
2033
|
+
`Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
|
|
2034
|
+
{
|
|
2035
|
+
field: fieldName,
|
|
2036
|
+
path: ctx.path,
|
|
2037
|
+
modelName: ctx.model.name
|
|
2038
|
+
}
|
|
1956
2039
|
);
|
|
1957
2040
|
}
|
|
2041
|
+
const relTable = buildTableReference(
|
|
2042
|
+
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
2043
|
+
relModel.tableName,
|
|
2044
|
+
ctx.dialect
|
|
2045
|
+
);
|
|
2046
|
+
const relAlias = ctx.aliasGen.next(fieldName);
|
|
2047
|
+
const join = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
|
|
2048
|
+
const args = {
|
|
2049
|
+
fieldName,
|
|
2050
|
+
value,
|
|
2051
|
+
ctx,
|
|
2052
|
+
whereBuilder,
|
|
2053
|
+
field,
|
|
2054
|
+
relModel,
|
|
2055
|
+
relTable,
|
|
2056
|
+
relAlias,
|
|
2057
|
+
join
|
|
2058
|
+
};
|
|
2059
|
+
if (isListRelation(field.type)) return buildListRelationFilters(args);
|
|
2060
|
+
return buildToOneRelationFilters(args);
|
|
1958
2061
|
}
|
|
1959
|
-
function
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
2062
|
+
function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
|
|
2063
|
+
ensureRelationFilterObject(fieldName, value, ctx);
|
|
2064
|
+
return buildRelation(fieldName, value, ctx, whereBuilder);
|
|
2065
|
+
}
|
|
2066
|
+
function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
|
|
2067
|
+
return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/builder/shared/validators/field-validators.ts
|
|
2071
|
+
function assertFieldExists(name, model, path) {
|
|
2072
|
+
const field = model.fields.find((f) => f.name === name);
|
|
2073
|
+
if (!isNotNullish(field)) {
|
|
2074
|
+
throw createError(`Field '${name}' does not exist on '${model.name}'`, {
|
|
2075
|
+
field: name,
|
|
2076
|
+
path,
|
|
2077
|
+
modelName: model.name,
|
|
2078
|
+
availableFields: model.fields.map((f) => f.name)
|
|
2079
|
+
});
|
|
1964
2080
|
}
|
|
2081
|
+
return field;
|
|
1965
2082
|
}
|
|
1966
|
-
function
|
|
1967
|
-
|
|
1968
|
-
const
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2083
|
+
function assertValidOperator(fieldName, op, fieldType, path, modelName) {
|
|
2084
|
+
if (!isNotNullish(fieldType)) return;
|
|
2085
|
+
const ARRAY_OPS = /* @__PURE__ */ new Set([
|
|
2086
|
+
Ops.HAS,
|
|
2087
|
+
Ops.HAS_SOME,
|
|
2088
|
+
Ops.HAS_EVERY,
|
|
2089
|
+
Ops.IS_EMPTY
|
|
2090
|
+
]);
|
|
2091
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2092
|
+
Ops.PATH,
|
|
2093
|
+
Ops.STRING_CONTAINS,
|
|
2094
|
+
Ops.STRING_STARTS_WITH,
|
|
2095
|
+
Ops.STRING_ENDS_WITH
|
|
2096
|
+
]);
|
|
2097
|
+
const isArrayOp = ARRAY_OPS.has(op);
|
|
2098
|
+
const isFieldArray = isArrayType(fieldType);
|
|
2099
|
+
const arrayOpMismatch = isArrayOp && !isFieldArray;
|
|
2100
|
+
if (arrayOpMismatch) {
|
|
2101
|
+
throw createError(`'${op}' requires array field, got '${fieldType}'`, {
|
|
2102
|
+
operator: op,
|
|
2103
|
+
field: fieldName,
|
|
2104
|
+
path,
|
|
2105
|
+
modelName
|
|
2106
|
+
});
|
|
1973
2107
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
const
|
|
1977
|
-
if (
|
|
1978
|
-
throw
|
|
2108
|
+
const isJsonOp = JSON_OPS.has(op);
|
|
2109
|
+
const isFieldJson = isJsonType(fieldType);
|
|
2110
|
+
const jsonOpMismatch = isJsonOp && !isFieldJson;
|
|
2111
|
+
if (jsonOpMismatch) {
|
|
2112
|
+
throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
|
|
2113
|
+
operator: op,
|
|
2114
|
+
field: fieldName,
|
|
2115
|
+
path,
|
|
2116
|
+
modelName
|
|
2117
|
+
});
|
|
1979
2118
|
}
|
|
1980
|
-
return dn;
|
|
1981
2119
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2120
|
+
|
|
2121
|
+
// src/builder/where/builder.ts
|
|
2122
|
+
var WhereBuilder = class {
|
|
2123
|
+
build(where, ctx) {
|
|
2124
|
+
if (!isPlainObject(where)) {
|
|
2125
|
+
throw createError("where must be an object", {
|
|
2126
|
+
path: ctx.path,
|
|
2127
|
+
modelName: ctx.model.name
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
return buildWhereInternal(where, ctx, this);
|
|
1985
2131
|
}
|
|
1986
|
-
|
|
2132
|
+
};
|
|
2133
|
+
var MAX_QUERY_DEPTH = 50;
|
|
2134
|
+
var EMPTY_JOINS = Object.freeze([]);
|
|
2135
|
+
var whereBuilderInstance = new WhereBuilder();
|
|
2136
|
+
function freezeResult(clause, joins = EMPTY_JOINS) {
|
|
2137
|
+
return Object.freeze({ clause, joins });
|
|
1987
2138
|
}
|
|
1988
|
-
function
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2139
|
+
function dedupePreserveOrder(items) {
|
|
2140
|
+
if (items.length <= 1) return Object.freeze([...items]);
|
|
2141
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2142
|
+
const out = [];
|
|
2143
|
+
for (const s of items) {
|
|
2144
|
+
if (!seen.has(s)) {
|
|
2145
|
+
seen.add(s);
|
|
2146
|
+
out.push(s);
|
|
2147
|
+
}
|
|
1994
2148
|
}
|
|
2149
|
+
return Object.freeze(out);
|
|
1995
2150
|
}
|
|
1996
|
-
function
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
validateMappingEntry(mappings[i], i + 1, seenDynamic);
|
|
2000
|
-
}
|
|
2151
|
+
function appendResult(result, clauses, allJoins) {
|
|
2152
|
+
if (isValidWhereClause(result.clause)) clauses.push(result.clause);
|
|
2153
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2001
2154
|
}
|
|
2002
|
-
function
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
if (
|
|
2006
|
-
|
|
2007
|
-
assertNextIndexMatches(mappings.length, index);
|
|
2155
|
+
function asLogicalOperator(key) {
|
|
2156
|
+
if (key === LogicalOps.AND) return "AND";
|
|
2157
|
+
if (key === LogicalOps.OR) return "OR";
|
|
2158
|
+
if (key === LogicalOps.NOT) return "NOT";
|
|
2159
|
+
return null;
|
|
2008
2160
|
}
|
|
2009
|
-
function
|
|
2010
|
-
|
|
2011
|
-
return value.toISOString();
|
|
2012
|
-
}
|
|
2013
|
-
return value;
|
|
2161
|
+
function nextContext(ctx) {
|
|
2162
|
+
return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
|
|
2014
2163
|
}
|
|
2015
|
-
function
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2020
|
-
for (const m of initialMappings) {
|
|
2021
|
-
if (typeof m.dynamicName === "string") {
|
|
2022
|
-
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2023
|
-
}
|
|
2024
|
-
}
|
|
2025
|
-
function assertCanAdd() {
|
|
2026
|
-
if (index > MAX_PARAM_INDEX) {
|
|
2027
|
-
throw new Error(
|
|
2028
|
-
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2029
|
-
);
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
function normalizeDynamicName(dynamicName) {
|
|
2033
|
-
const dn = dynamicName.trim();
|
|
2034
|
-
if (dn.length === 0) {
|
|
2035
|
-
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2036
|
-
}
|
|
2037
|
-
return dn;
|
|
2038
|
-
}
|
|
2039
|
-
function format(position) {
|
|
2040
|
-
return `$${position}`;
|
|
2041
|
-
}
|
|
2042
|
-
function addDynamic(dynamicName) {
|
|
2043
|
-
const dn = normalizeDynamicName(dynamicName);
|
|
2044
|
-
const existing = dynamicNameToIndex.get(dn);
|
|
2045
|
-
if (existing !== void 0) {
|
|
2046
|
-
return format(existing);
|
|
2047
|
-
}
|
|
2048
|
-
const position = index;
|
|
2049
|
-
dynamicNameToIndex.set(dn, position);
|
|
2050
|
-
params.push(void 0);
|
|
2051
|
-
mappings.push({ index: position, dynamicName: dn });
|
|
2052
|
-
index++;
|
|
2053
|
-
return format(position);
|
|
2054
|
-
}
|
|
2055
|
-
function addStatic(value) {
|
|
2056
|
-
const position = index;
|
|
2057
|
-
const normalizedValue = normalizeValue(value);
|
|
2058
|
-
params.push(normalizedValue);
|
|
2059
|
-
mappings.push({ index: position, value: normalizedValue });
|
|
2060
|
-
index++;
|
|
2061
|
-
return format(position);
|
|
2062
|
-
}
|
|
2063
|
-
function add(value, dynamicName) {
|
|
2064
|
-
assertCanAdd();
|
|
2065
|
-
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2164
|
+
function buildRelationFilter(fieldName, value, ctx, builder) {
|
|
2165
|
+
const ctx2 = nextContext(ctx);
|
|
2166
|
+
if (ctx.isSubquery) {
|
|
2167
|
+
return buildNestedRelation(fieldName, value, ctx2, builder);
|
|
2066
2168
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2169
|
+
return buildTopLevelRelation(fieldName, value, ctx2, builder);
|
|
2170
|
+
}
|
|
2171
|
+
function buildWhereEntry(key, value, ctx, builder) {
|
|
2172
|
+
const op = asLogicalOperator(key);
|
|
2173
|
+
if (op) return buildLogical(op, value, ctx, builder);
|
|
2174
|
+
if (isRelationField(key, ctx.model)) {
|
|
2175
|
+
if (!isPlainObject(value)) {
|
|
2176
|
+
throw createError(`Relation filter '${key}' must be an object`, {
|
|
2177
|
+
path: [...ctx.path, key],
|
|
2178
|
+
field: key,
|
|
2179
|
+
modelName: ctx.model.name,
|
|
2180
|
+
value
|
|
2181
|
+
});
|
|
2071
2182
|
}
|
|
2072
|
-
return
|
|
2073
|
-
}
|
|
2074
|
-
function snapshot() {
|
|
2075
|
-
return Object.freeze({
|
|
2076
|
-
index,
|
|
2077
|
-
params: Object.freeze([...params]),
|
|
2078
|
-
mappings: Object.freeze([...mappings])
|
|
2079
|
-
});
|
|
2183
|
+
return buildRelationFilter(key, value, ctx, builder);
|
|
2080
2184
|
}
|
|
2081
|
-
return
|
|
2082
|
-
add,
|
|
2083
|
-
addAuto,
|
|
2084
|
-
snapshot,
|
|
2085
|
-
get index() {
|
|
2086
|
-
return index;
|
|
2087
|
-
}
|
|
2088
|
-
};
|
|
2185
|
+
return buildScalarField(key, value, ctx);
|
|
2089
2186
|
}
|
|
2090
|
-
function
|
|
2091
|
-
if (
|
|
2092
|
-
throw
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
throw new Error(
|
|
2096
|
-
`Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
|
|
2187
|
+
function buildWhereInternal(where, ctx, builder) {
|
|
2188
|
+
if (ctx.depth > MAX_QUERY_DEPTH) {
|
|
2189
|
+
throw createError(
|
|
2190
|
+
`Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
|
|
2191
|
+
{ path: ctx.path, modelName: ctx.model.name }
|
|
2097
2192
|
);
|
|
2098
2193
|
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
function toPublicResult(clause, joins, params) {
|
|
2112
|
-
const snapshot = params.snapshot();
|
|
2113
|
-
return Object.freeze({
|
|
2114
|
-
clause: clause || DEFAULT_WHERE_CLAUSE,
|
|
2115
|
-
joins: Object.freeze([...joins]),
|
|
2116
|
-
params: snapshot.params,
|
|
2117
|
-
paramMappings: snapshot.mappings,
|
|
2118
|
-
nextParamIndex: snapshot.index
|
|
2119
|
-
});
|
|
2194
|
+
if (isEmptyWhere(where)) {
|
|
2195
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2196
|
+
}
|
|
2197
|
+
const allJoins = [];
|
|
2198
|
+
const clauses = [];
|
|
2199
|
+
for (const [key, value] of Object.entries(where)) {
|
|
2200
|
+
if (value === void 0) continue;
|
|
2201
|
+
const result = buildWhereEntry(key, value, ctx, builder);
|
|
2202
|
+
appendResult(result, clauses, allJoins);
|
|
2203
|
+
}
|
|
2204
|
+
const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2205
|
+
return freezeResult(finalClause, dedupePreserveOrder(allJoins));
|
|
2120
2206
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
|
|
2134
|
-
dialect,
|
|
2135
|
-
params,
|
|
2136
|
-
depth: 0
|
|
2137
|
-
};
|
|
2138
|
-
const result = whereBuilderInstance.build(where, ctx);
|
|
2139
|
-
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2140
|
-
if (!options.isSubquery) {
|
|
2141
|
-
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2142
|
-
(m) => parseInt(m[1], 10)
|
|
2143
|
-
);
|
|
2144
|
-
if (nums.length > 0) {
|
|
2145
|
-
const min = Math.min(...nums);
|
|
2146
|
-
if (min === 1) {
|
|
2147
|
-
validateParamConsistency(publicResult.clause, publicResult.params);
|
|
2148
|
-
} else {
|
|
2149
|
-
validateParamConsistencyFragment(
|
|
2150
|
-
publicResult.clause,
|
|
2151
|
-
publicResult.params
|
|
2152
|
-
);
|
|
2207
|
+
function normalizeLogicalValue(operator, value, ctx) {
|
|
2208
|
+
if (Array.isArray(value)) {
|
|
2209
|
+
const out = [];
|
|
2210
|
+
for (let i = 0; i < value.length; i++) {
|
|
2211
|
+
const v = value[i];
|
|
2212
|
+
if (v === void 0) continue;
|
|
2213
|
+
if (!isPlainObject(v)) {
|
|
2214
|
+
throw createError(`${operator} entries must be objects`, {
|
|
2215
|
+
path: [...ctx.path, operator, String(i)],
|
|
2216
|
+
modelName: ctx.model.name,
|
|
2217
|
+
value: v
|
|
2218
|
+
});
|
|
2153
2219
|
}
|
|
2220
|
+
out.push(v);
|
|
2154
2221
|
}
|
|
2222
|
+
return out;
|
|
2155
2223
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
function normalizeIntLike(name, v, opts = {}) {
|
|
2159
|
-
var _a, _b;
|
|
2160
|
-
if (!isNotNullish(v)) return void 0;
|
|
2161
|
-
if (schemaParser.isDynamicParameter(v)) return v;
|
|
2162
|
-
if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
|
|
2163
|
-
throw new Error(`${name} must be an integer`);
|
|
2164
|
-
}
|
|
2165
|
-
const min = (_a = opts.min) != null ? _a : 0;
|
|
2166
|
-
const allowZero = (_b = opts.allowZero) != null ? _b : true;
|
|
2167
|
-
if (!allowZero && v === 0) {
|
|
2168
|
-
throw new Error(`${name} must be > 0`);
|
|
2169
|
-
}
|
|
2170
|
-
if (v < min) {
|
|
2171
|
-
throw new Error(`${name} must be >= ${min}`);
|
|
2172
|
-
}
|
|
2173
|
-
if (typeof opts.max === "number" && v > opts.max) {
|
|
2174
|
-
throw new Error(`${name} must be <= ${opts.max}`);
|
|
2224
|
+
if (isPlainObject(value)) {
|
|
2225
|
+
return [value];
|
|
2175
2226
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
if (s.length === 0) return dn;
|
|
2182
|
-
return `${s}:${dn}`;
|
|
2227
|
+
throw createError(`${operator} must be an object or array of objects`, {
|
|
2228
|
+
path: [...ctx.path, operator],
|
|
2229
|
+
modelName: ctx.model.name,
|
|
2230
|
+
value
|
|
2231
|
+
});
|
|
2183
2232
|
}
|
|
2184
|
-
function
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2233
|
+
function collectLogicalParts(operator, conditions, ctx, builder) {
|
|
2234
|
+
const allJoins = [];
|
|
2235
|
+
const clauses = [];
|
|
2236
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
2237
|
+
const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
|
|
2238
|
+
path: [...ctx.path, operator, String(i)],
|
|
2239
|
+
depth: ctx.depth + 1
|
|
2240
|
+
}));
|
|
2241
|
+
if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
|
|
2242
|
+
if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
|
|
2243
|
+
clauses.push(`(${result.clause})`);
|
|
2244
|
+
}
|
|
2188
2245
|
}
|
|
2189
|
-
return
|
|
2246
|
+
return {
|
|
2247
|
+
joins: dedupePreserveOrder(allJoins),
|
|
2248
|
+
clauses: Object.freeze(clauses)
|
|
2249
|
+
};
|
|
2190
2250
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
if (s === "last") return "first";
|
|
2197
|
-
return v;
|
|
2198
|
-
};
|
|
2199
|
-
var flipSortString = (v) => {
|
|
2200
|
-
if (typeof v !== "string") return v;
|
|
2201
|
-
const s = v.toLowerCase();
|
|
2202
|
-
if (s === "asc") return "desc";
|
|
2203
|
-
if (s === "desc") return "asc";
|
|
2204
|
-
return v;
|
|
2205
|
-
};
|
|
2206
|
-
var getNextSort = (sortRaw) => {
|
|
2207
|
-
if (typeof sortRaw !== "string") return sortRaw;
|
|
2208
|
-
const s = sortRaw.toLowerCase();
|
|
2209
|
-
if (s === "asc") return "desc";
|
|
2210
|
-
if (s === "desc") return "asc";
|
|
2211
|
-
return sortRaw;
|
|
2212
|
-
};
|
|
2213
|
-
var flipObjectSort = (obj) => {
|
|
2214
|
-
const sortRaw = obj.sort;
|
|
2215
|
-
const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
|
|
2216
|
-
const nullsRaw = obj.nulls;
|
|
2217
|
-
if (typeof nullsRaw === "string") {
|
|
2218
|
-
out.nulls = flipNulls(nullsRaw);
|
|
2219
|
-
}
|
|
2220
|
-
return out;
|
|
2221
|
-
};
|
|
2222
|
-
var flipValue = (v) => {
|
|
2223
|
-
if (typeof v === "string") return flipSortString(v);
|
|
2224
|
-
if (isPlainObject(v)) return flipObjectSort(v);
|
|
2225
|
-
return v;
|
|
2226
|
-
};
|
|
2227
|
-
var assertSingleFieldObject = (item) => {
|
|
2228
|
-
if (!isPlainObject(item)) {
|
|
2229
|
-
throw new Error("orderBy array entries must be objects");
|
|
2230
|
-
}
|
|
2231
|
-
const entries = Object.entries(item);
|
|
2232
|
-
if (entries.length !== 1) {
|
|
2233
|
-
throw new Error("orderBy array entries must have exactly one field");
|
|
2251
|
+
function buildLogicalClause(operator, clauses) {
|
|
2252
|
+
if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
|
|
2253
|
+
if (operator === "NOT") {
|
|
2254
|
+
if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
|
|
2255
|
+
return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
|
|
2234
2256
|
}
|
|
2235
|
-
return
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
};
|
|
2243
|
-
var flipOrderByObject = (orderBy) => {
|
|
2244
|
-
const out = {};
|
|
2245
|
-
for (const [k, v] of Object.entries(orderBy)) {
|
|
2246
|
-
out[k] = flipValue(v);
|
|
2257
|
+
return clauses.join(` ${operator} `);
|
|
2258
|
+
}
|
|
2259
|
+
function buildLogical(operator, value, ctx, builder) {
|
|
2260
|
+
const conditions = normalizeLogicalValue(operator, value, ctx);
|
|
2261
|
+
if (conditions.length === 0) {
|
|
2262
|
+
const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
|
|
2263
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2247
2264
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2265
|
+
const { joins, clauses } = collectLogicalParts(
|
|
2266
|
+
operator,
|
|
2267
|
+
conditions,
|
|
2268
|
+
ctx,
|
|
2269
|
+
builder
|
|
2270
|
+
);
|
|
2271
|
+
const clause = buildLogicalClause(operator, clauses);
|
|
2272
|
+
return freezeResult(clause, joins);
|
|
2273
|
+
}
|
|
2274
|
+
function buildScalarField(fieldName, value, ctx) {
|
|
2275
|
+
const field = assertFieldExists(fieldName, ctx.model, ctx.path);
|
|
2276
|
+
const expr = col(ctx.alias, fieldName, ctx.model);
|
|
2277
|
+
if (value === null) {
|
|
2278
|
+
return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
|
|
2254
2279
|
}
|
|
2255
|
-
if (isPlainObject(
|
|
2256
|
-
|
|
2280
|
+
if (isPlainObject(value)) {
|
|
2281
|
+
const mode = value.mode;
|
|
2282
|
+
const ops = Object.entries(value).filter(
|
|
2283
|
+
([k, v]) => k !== "mode" && v !== void 0
|
|
2284
|
+
);
|
|
2285
|
+
if (ops.length === 0) {
|
|
2286
|
+
return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2287
|
+
}
|
|
2288
|
+
const parts = [];
|
|
2289
|
+
for (const [op, val] of ops) {
|
|
2290
|
+
assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
|
|
2291
|
+
const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
|
|
2292
|
+
if (isValidWhereClause(clause3)) parts.push(clause3);
|
|
2293
|
+
}
|
|
2294
|
+
const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
|
|
2295
|
+
return freezeResult(clause2, EMPTY_JOINS);
|
|
2257
2296
|
}
|
|
2258
|
-
|
|
2297
|
+
const clause = buildOperator(
|
|
2298
|
+
expr,
|
|
2299
|
+
Ops.EQUALS,
|
|
2300
|
+
value,
|
|
2301
|
+
ctx,
|
|
2302
|
+
void 0,
|
|
2303
|
+
field.type
|
|
2304
|
+
);
|
|
2305
|
+
return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
|
|
2259
2306
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
return {
|
|
2264
|
-
[field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
|
|
2265
|
-
};
|
|
2266
|
-
});
|
|
2267
|
-
};
|
|
2268
|
-
function normalizeOrderByInput(orderBy, parseValue) {
|
|
2269
|
-
if (!isNotNullish(orderBy)) return [];
|
|
2270
|
-
if (Array.isArray(orderBy)) {
|
|
2271
|
-
const pairs = orderBy.map(assertSingleFieldObject);
|
|
2272
|
-
return normalizePairs(pairs, parseValue);
|
|
2307
|
+
function buildOperator(expr, op, val, ctx, mode, fieldType) {
|
|
2308
|
+
if (fieldType && isArrayType(fieldType)) {
|
|
2309
|
+
return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
|
|
2273
2310
|
}
|
|
2274
|
-
if (
|
|
2275
|
-
|
|
2311
|
+
if (fieldType && isJsonType(fieldType)) {
|
|
2312
|
+
const JSON_OPS = /* @__PURE__ */ new Set([
|
|
2313
|
+
Ops.PATH,
|
|
2314
|
+
Ops.STRING_CONTAINS,
|
|
2315
|
+
Ops.STRING_STARTS_WITH,
|
|
2316
|
+
Ops.STRING_ENDS_WITH
|
|
2317
|
+
]);
|
|
2318
|
+
if (JSON_OPS.has(op)) {
|
|
2319
|
+
return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
|
|
2320
|
+
}
|
|
2276
2321
|
}
|
|
2277
|
-
|
|
2322
|
+
return buildScalarOperator(
|
|
2323
|
+
expr,
|
|
2324
|
+
op,
|
|
2325
|
+
val,
|
|
2326
|
+
ctx.params,
|
|
2327
|
+
mode,
|
|
2328
|
+
fieldType,
|
|
2329
|
+
ctx.dialect
|
|
2330
|
+
);
|
|
2278
2331
|
}
|
|
2279
2332
|
|
|
2280
|
-
// src/builder/
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
const
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
const s = String(raw).toLowerCase();
|
|
2290
|
-
if (s === "first" || s === "last") return s;
|
|
2291
|
-
throw new Error(`Invalid ${errorLabel}: ${raw}`);
|
|
2292
|
-
}
|
|
2293
|
-
function requireOrderByObject(v, errorPrefix) {
|
|
2294
|
-
if (!isPlainObject(v) || !("sort" in v)) {
|
|
2295
|
-
throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
|
|
2296
|
-
}
|
|
2297
|
-
return v;
|
|
2333
|
+
// src/builder/shared/alias-generator.ts
|
|
2334
|
+
function toSafeSqlIdentifier(input) {
|
|
2335
|
+
const raw = String(input);
|
|
2336
|
+
const cleaned = raw.replace(/\W/g, "_");
|
|
2337
|
+
const startsOk = /^[a-zA-Z_]/.test(cleaned);
|
|
2338
|
+
const base = startsOk ? cleaned : `_${cleaned}`;
|
|
2339
|
+
const fallback = base.length > 0 ? base : "_t";
|
|
2340
|
+
const lowered = fallback.toLowerCase();
|
|
2341
|
+
return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
|
|
2298
2342
|
}
|
|
2299
|
-
function
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2343
|
+
function createAliasGenerator(maxAliases = 1e4) {
|
|
2344
|
+
let counter = 0;
|
|
2345
|
+
const usedAliases = /* @__PURE__ */ new Set();
|
|
2346
|
+
return {
|
|
2347
|
+
next(baseName) {
|
|
2348
|
+
if (usedAliases.size >= maxAliases) {
|
|
2349
|
+
throw new Error(
|
|
2350
|
+
`Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
|
|
2351
|
+
);
|
|
2352
|
+
}
|
|
2353
|
+
const base = toSafeSqlIdentifier(baseName);
|
|
2354
|
+
const suffix = `_${counter}`;
|
|
2355
|
+
const maxLen = 63;
|
|
2356
|
+
const baseMax = Math.max(1, maxLen - suffix.length);
|
|
2357
|
+
const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
|
|
2358
|
+
const alias = `${trimmedBase}${suffix}`;
|
|
2359
|
+
counter += 1;
|
|
2360
|
+
if (usedAliases.has(alias)) {
|
|
2361
|
+
throw new Error(
|
|
2362
|
+
`CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
usedAliases.add(alias);
|
|
2366
|
+
return alias;
|
|
2306
2367
|
}
|
|
2307
|
-
}
|
|
2368
|
+
};
|
|
2308
2369
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
if (
|
|
2312
|
-
|
|
2370
|
+
var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
|
|
2371
|
+
function assertSameLength(params, mappings) {
|
|
2372
|
+
if (params.length !== mappings.length) {
|
|
2373
|
+
throw new Error(
|
|
2374
|
+
`CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
|
|
2375
|
+
);
|
|
2313
2376
|
}
|
|
2314
|
-
const obj = requireOrderByObject(v, errorPrefix);
|
|
2315
|
-
const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
|
|
2316
|
-
const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
|
|
2317
|
-
assertAllowedOrderByKeys(obj, fieldName);
|
|
2318
|
-
return { direction, nulls };
|
|
2319
2377
|
}
|
|
2320
|
-
function
|
|
2321
|
-
if (
|
|
2322
|
-
throw new Error(
|
|
2378
|
+
function assertValidNextIndex(index) {
|
|
2379
|
+
if (!Number.isInteger(index) || index < 1) {
|
|
2380
|
+
throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
|
|
2323
2381
|
}
|
|
2324
|
-
return v;
|
|
2325
2382
|
}
|
|
2326
|
-
function
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
if (n > MAX_LIMIT_OFFSET) {
|
|
2333
|
-
throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
|
|
2383
|
+
function assertNextIndexMatches(mappingsLength, nextIndex) {
|
|
2384
|
+
const expected = mappingsLength + 1;
|
|
2385
|
+
if (nextIndex !== expected) {
|
|
2386
|
+
throw new Error(
|
|
2387
|
+
`CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
|
|
2388
|
+
);
|
|
2334
2389
|
}
|
|
2335
|
-
return n;
|
|
2336
|
-
}
|
|
2337
|
-
function hasNonNullishProp(v, key) {
|
|
2338
|
-
return isPlainObject(v) && key in v && isNotNullish(v[key]);
|
|
2339
|
-
}
|
|
2340
|
-
function normalizeIntegerOrDynamic(name, v) {
|
|
2341
|
-
if (schemaParser.isDynamicParameter(v)) return v;
|
|
2342
|
-
return normalizeFiniteInteger(name, v);
|
|
2343
2390
|
}
|
|
2344
|
-
function
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
hasSkip: false,
|
|
2350
|
-
hasTake: false,
|
|
2351
|
-
skipVal: void 0,
|
|
2352
|
-
takeVal: void 0
|
|
2353
|
-
};
|
|
2391
|
+
function assertSequentialIndex(actual, expected) {
|
|
2392
|
+
if (actual !== expected) {
|
|
2393
|
+
throw new Error(
|
|
2394
|
+
`CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
|
|
2395
|
+
);
|
|
2354
2396
|
}
|
|
2355
|
-
const obj = relArgs;
|
|
2356
|
-
const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
|
|
2357
|
-
const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
|
|
2358
|
-
return { hasSkip, hasTake, skipVal, takeVal };
|
|
2359
2397
|
}
|
|
2360
|
-
function
|
|
2361
|
-
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
|
|
2368
|
-
out.push(`${c} ${dir}${nulls}`);
|
|
2369
|
-
continue;
|
|
2370
|
-
}
|
|
2371
|
-
if (isNotNullish(e.nulls)) {
|
|
2372
|
-
const isNullExpr = `(${c} IS NULL)`;
|
|
2373
|
-
const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
|
|
2374
|
-
out.push(`${isNullExpr} ${nullRankDir}`);
|
|
2375
|
-
out.push(`${c} ${dir}`);
|
|
2376
|
-
continue;
|
|
2377
|
-
}
|
|
2378
|
-
out.push(`${c} ${dir}`);
|
|
2398
|
+
function assertExactlyOneOfDynamicOrValue(m) {
|
|
2399
|
+
const hasDynamic = typeof m.dynamicName === "string";
|
|
2400
|
+
const hasStatic = m.value !== void 0;
|
|
2401
|
+
if (hasDynamic === hasStatic) {
|
|
2402
|
+
throw new Error(
|
|
2403
|
+
`CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
|
|
2404
|
+
);
|
|
2379
2405
|
}
|
|
2380
|
-
return out.join(SQL_SEPARATORS.ORDER_BY);
|
|
2381
2406
|
}
|
|
2382
|
-
function
|
|
2383
|
-
|
|
2384
|
-
|
|
2407
|
+
function normalizeDynamicNameOrThrow(dynamicName, index) {
|
|
2408
|
+
const dn = dynamicName.trim();
|
|
2409
|
+
if (dn.length === 0) {
|
|
2410
|
+
throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
|
|
2385
2411
|
}
|
|
2386
|
-
return
|
|
2412
|
+
return dn;
|
|
2387
2413
|
}
|
|
2388
|
-
function
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
const out = [...orderEntries];
|
|
2392
|
-
for (const [field] of cursorEntries) {
|
|
2393
|
-
if (!existing.has(field)) {
|
|
2394
|
-
out.push({ field, direction: "asc" });
|
|
2395
|
-
existing.set(field, out[out.length - 1]);
|
|
2396
|
-
}
|
|
2414
|
+
function assertUniqueDynamicName(dn, seen) {
|
|
2415
|
+
if (seen.has(dn)) {
|
|
2416
|
+
throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
|
|
2397
2417
|
}
|
|
2398
|
-
|
|
2418
|
+
seen.add(dn);
|
|
2399
2419
|
}
|
|
2400
|
-
function
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
const parts = [];
|
|
2407
|
-
for (const [field, value] of entries) {
|
|
2408
|
-
const c = `${cursorAlias}.${quote(field)}`;
|
|
2409
|
-
if (value === null) {
|
|
2410
|
-
parts.push(`${c} IS NULL`);
|
|
2411
|
-
continue;
|
|
2412
|
-
}
|
|
2413
|
-
const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
|
|
2414
|
-
placeholdersByField.set(field, ph);
|
|
2415
|
-
parts.push(`${c} = ${ph}`);
|
|
2420
|
+
function validateMappingEntry(m, expectedIndex, seenDynamic) {
|
|
2421
|
+
assertSequentialIndex(m.index, expectedIndex);
|
|
2422
|
+
assertExactlyOneOfDynamicOrValue(m);
|
|
2423
|
+
if (typeof m.dynamicName === "string") {
|
|
2424
|
+
const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
|
|
2425
|
+
assertUniqueDynamicName(dn, seenDynamic);
|
|
2416
2426
|
}
|
|
2417
|
-
return {
|
|
2418
|
-
whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
|
|
2419
|
-
placeholdersByField
|
|
2420
|
-
};
|
|
2421
|
-
}
|
|
2422
|
-
function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
|
|
2423
|
-
const colName = quote(field);
|
|
2424
|
-
return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
|
|
2425
2427
|
}
|
|
2426
|
-
function
|
|
2427
|
-
|
|
2428
|
+
function validateMappings(mappings) {
|
|
2429
|
+
const seenDynamic = /* @__PURE__ */ new Set();
|
|
2430
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
2431
|
+
validateMappingEntry(mappings[i], i + 1, seenDynamic);
|
|
2432
|
+
}
|
|
2428
2433
|
}
|
|
2429
|
-
function
|
|
2430
|
-
|
|
2434
|
+
function validateState(params, mappings, index) {
|
|
2435
|
+
assertSameLength(params, mappings);
|
|
2436
|
+
assertValidNextIndex(index);
|
|
2437
|
+
if (mappings.length === 0) return;
|
|
2438
|
+
validateMappings(mappings);
|
|
2439
|
+
assertNextIndexMatches(mappings.length, index);
|
|
2431
2440
|
}
|
|
2432
|
-
function
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
|
|
2441
|
+
function normalizeValue2(value) {
|
|
2442
|
+
if (value instanceof Date) {
|
|
2443
|
+
return value.toISOString();
|
|
2436
2444
|
}
|
|
2437
|
-
return
|
|
2445
|
+
return value;
|
|
2438
2446
|
}
|
|
2439
|
-
function
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2447
|
+
function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
|
|
2448
|
+
let index = startIndex;
|
|
2449
|
+
const params = [...initialParams];
|
|
2450
|
+
const mappings = [...initialMappings];
|
|
2451
|
+
const dynamicNameToIndex = /* @__PURE__ */ new Map();
|
|
2452
|
+
for (const m of initialMappings) {
|
|
2453
|
+
if (typeof m.dynamicName === "string") {
|
|
2454
|
+
dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
|
|
2446
2455
|
}
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2456
|
+
}
|
|
2457
|
+
function assertCanAdd() {
|
|
2458
|
+
if (index > MAX_PARAM_INDEX) {
|
|
2459
|
+
throw new Error(
|
|
2460
|
+
`CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
|
|
2461
|
+
);
|
|
2451
2462
|
}
|
|
2452
|
-
const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
|
|
2453
|
-
parts.push(`${c} = ${ph}`);
|
|
2454
2463
|
}
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
const entries = [];
|
|
2460
|
-
for (const item of normalized) {
|
|
2461
|
-
for (const [field, value] of Object.entries(item)) {
|
|
2462
|
-
if (typeof value === "string") {
|
|
2463
|
-
entries.push({ field, direction: value });
|
|
2464
|
-
} else {
|
|
2465
|
-
entries.push({
|
|
2466
|
-
field,
|
|
2467
|
-
direction: value.sort,
|
|
2468
|
-
nulls: value.nulls
|
|
2469
|
-
});
|
|
2470
|
-
}
|
|
2464
|
+
function normalizeDynamicName(dynamicName) {
|
|
2465
|
+
const dn = dynamicName.trim();
|
|
2466
|
+
if (dn.length === 0) {
|
|
2467
|
+
throw new Error("CRITICAL: dynamicName cannot be empty");
|
|
2471
2468
|
}
|
|
2469
|
+
return dn;
|
|
2472
2470
|
}
|
|
2473
|
-
|
|
2474
|
-
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2471
|
+
function format(position) {
|
|
2472
|
+
return `$${position}`;
|
|
2473
|
+
}
|
|
2474
|
+
function addDynamic(dynamicName) {
|
|
2475
|
+
const dn = normalizeDynamicName(dynamicName);
|
|
2476
|
+
const existing = dynamicNameToIndex.get(dn);
|
|
2477
|
+
if (existing !== void 0) {
|
|
2478
|
+
return format(existing);
|
|
2479
|
+
}
|
|
2480
|
+
const position = index;
|
|
2481
|
+
dynamicNameToIndex.set(dn, position);
|
|
2482
|
+
params.push(void 0);
|
|
2483
|
+
mappings.push({ index: position, dynamicName: dn });
|
|
2484
|
+
index++;
|
|
2485
|
+
return format(position);
|
|
2486
|
+
}
|
|
2487
|
+
function addStatic(value) {
|
|
2488
|
+
const position = index;
|
|
2489
|
+
const normalizedValue = normalizeValue2(value);
|
|
2490
|
+
params.push(normalizedValue);
|
|
2491
|
+
mappings.push({ index: position, value: normalizedValue });
|
|
2492
|
+
index++;
|
|
2493
|
+
return format(position);
|
|
2481
2494
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
if (orderEntries.length === 0) {
|
|
2486
|
-
orderEntries = cursorEntries.map(([field]) => ({
|
|
2487
|
-
field,
|
|
2488
|
-
direction: "asc"
|
|
2489
|
-
}));
|
|
2490
|
-
} else {
|
|
2491
|
-
orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
|
|
2495
|
+
function add(value, dynamicName) {
|
|
2496
|
+
assertCanAdd();
|
|
2497
|
+
return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
|
|
2492
2498
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
);
|
|
2498
|
-
const outerCursorMatch = buildOuterCursorMatch(
|
|
2499
|
-
cursor,
|
|
2500
|
-
alias,
|
|
2501
|
-
placeholdersByField,
|
|
2502
|
-
params,
|
|
2503
|
-
model
|
|
2504
|
-
);
|
|
2505
|
-
const orClauses = [];
|
|
2506
|
-
for (let level = 0; level < orderEntries.length; level++) {
|
|
2507
|
-
const andParts = [];
|
|
2508
|
-
for (let i = 0; i < level; i++) {
|
|
2509
|
-
const e2 = orderEntries[i];
|
|
2510
|
-
const c2 = col(alias, e2.field, model);
|
|
2511
|
-
const v2 = cursorValueExpr(
|
|
2512
|
-
tableName,
|
|
2513
|
-
cursorAlias,
|
|
2514
|
-
cursorWhereSql,
|
|
2515
|
-
e2.field);
|
|
2516
|
-
andParts.push(buildCursorEqualityExpr(c2, v2));
|
|
2499
|
+
function addAuto(value) {
|
|
2500
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
2501
|
+
const dynamicName = schemaParser.extractDynamicName(value);
|
|
2502
|
+
return add(void 0, dynamicName);
|
|
2517
2503
|
}
|
|
2518
|
-
|
|
2519
|
-
const c = col(alias, e.field, model);
|
|
2520
|
-
const v = cursorValueExpr(
|
|
2521
|
-
tableName,
|
|
2522
|
-
cursorAlias,
|
|
2523
|
-
cursorWhereSql,
|
|
2524
|
-
e.field);
|
|
2525
|
-
const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
|
|
2526
|
-
andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
|
|
2527
|
-
orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
|
|
2504
|
+
return add(value);
|
|
2528
2505
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
return
|
|
2506
|
+
function snapshot() {
|
|
2507
|
+
return Object.freeze({
|
|
2508
|
+
index,
|
|
2509
|
+
params: Object.freeze([...params]),
|
|
2510
|
+
mappings: Object.freeze([...mappings])
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
return {
|
|
2514
|
+
add,
|
|
2515
|
+
addAuto,
|
|
2516
|
+
snapshot,
|
|
2517
|
+
get index() {
|
|
2518
|
+
return index;
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2537
2521
|
}
|
|
2538
|
-
function
|
|
2539
|
-
if (!
|
|
2540
|
-
|
|
2541
|
-
|
|
2522
|
+
function createParamStore(startIndex = 1) {
|
|
2523
|
+
if (!Number.isInteger(startIndex) || startIndex < 1) {
|
|
2524
|
+
throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
|
|
2525
|
+
}
|
|
2526
|
+
if (startIndex > MAX_PARAM_INDEX) {
|
|
2542
2527
|
throw new Error(
|
|
2543
|
-
|
|
2528
|
+
`Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
|
|
2544
2529
|
);
|
|
2545
2530
|
}
|
|
2546
|
-
return
|
|
2531
|
+
return createStoreInternal(startIndex);
|
|
2547
2532
|
}
|
|
2548
|
-
function
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
if (n === 0) return 0;
|
|
2556
|
-
}
|
|
2557
|
-
return n;
|
|
2533
|
+
function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
|
|
2534
|
+
validateState([...existingParams], [...existingMappings], nextIndex);
|
|
2535
|
+
return createStoreInternal(
|
|
2536
|
+
nextIndex,
|
|
2537
|
+
[...existingParams],
|
|
2538
|
+
[...existingMappings]
|
|
2539
|
+
);
|
|
2558
2540
|
}
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2541
|
+
|
|
2542
|
+
// src/builder/shared/state.ts
|
|
2543
|
+
function toPublicResult(clause, joins, params) {
|
|
2544
|
+
const snapshot = params.snapshot();
|
|
2545
|
+
return Object.freeze({
|
|
2546
|
+
clause: clause || DEFAULT_WHERE_CLAUSE,
|
|
2547
|
+
joins: Object.freeze([...joins]),
|
|
2548
|
+
params: snapshot.params,
|
|
2549
|
+
paramMappings: snapshot.mappings,
|
|
2550
|
+
nextParamIndex: snapshot.index
|
|
2564
2551
|
});
|
|
2565
2552
|
}
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2553
|
+
|
|
2554
|
+
// src/builder/where.ts
|
|
2555
|
+
function buildWhereClause(where, options) {
|
|
2556
|
+
var _a, _b, _c, _d, _e;
|
|
2557
|
+
const dialect = options.dialect || getGlobalDialect();
|
|
2558
|
+
const params = (_a = options.params) != null ? _a : createParamStore();
|
|
2559
|
+
const ctx = {
|
|
2560
|
+
alias: options.alias,
|
|
2561
|
+
model: options.model,
|
|
2562
|
+
schemaModels: (_b = options.schemaModels) != null ? _b : [],
|
|
2563
|
+
path: (_c = options.path) != null ? _c : [],
|
|
2564
|
+
isSubquery: (_d = options.isSubquery) != null ? _d : false,
|
|
2565
|
+
aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
|
|
2566
|
+
dialect,
|
|
2567
|
+
params,
|
|
2568
|
+
depth: 0
|
|
2569
|
+
};
|
|
2570
|
+
const result = whereBuilderInstance.build(where, ctx);
|
|
2571
|
+
const publicResult = toPublicResult(result.clause, result.joins, params);
|
|
2572
|
+
if (!options.isSubquery) {
|
|
2573
|
+
const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
|
|
2574
|
+
(m) => parseInt(m[1], 10)
|
|
2575
|
+
);
|
|
2576
|
+
if (nums.length > 0) {
|
|
2577
|
+
const min = Math.min(...nums);
|
|
2578
|
+
if (min === 1) {
|
|
2579
|
+
validateParamConsistency(publicResult.clause, publicResult.params);
|
|
2580
|
+
} else {
|
|
2581
|
+
validateParamConsistencyFragment(
|
|
2582
|
+
publicResult.clause,
|
|
2583
|
+
publicResult.params
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2580
2587
|
}
|
|
2581
|
-
return
|
|
2588
|
+
return publicResult;
|
|
2582
2589
|
}
|
|
2583
2590
|
|
|
2584
2591
|
// src/builder/select/fields.ts
|
|
@@ -4512,35 +4519,301 @@ function transformQueryResults(method, results) {
|
|
|
4512
4519
|
const transformer = RESULT_TRANSFORMERS[method];
|
|
4513
4520
|
return transformer ? transformer(results) : results;
|
|
4514
4521
|
}
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4522
|
+
|
|
4523
|
+
// src/utils/s3-fifo.ts
|
|
4524
|
+
function withDispose(it) {
|
|
4525
|
+
const anyIt = it;
|
|
4526
|
+
if (anyIt[Symbol.dispose] === void 0) {
|
|
4527
|
+
anyIt[Symbol.dispose] = () => {
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
return it;
|
|
4531
|
+
}
|
|
4532
|
+
var BoundedCache = class {
|
|
4533
|
+
constructor(maxSize) {
|
|
4534
|
+
this.map = /* @__PURE__ */ new Map();
|
|
4535
|
+
this.ghost = /* @__PURE__ */ new Set();
|
|
4536
|
+
this.smallHead = null;
|
|
4537
|
+
this.smallTail = null;
|
|
4538
|
+
this.smallSize = 0;
|
|
4539
|
+
this.mainHead = null;
|
|
4540
|
+
this.mainTail = null;
|
|
4541
|
+
this.mainSize = 0;
|
|
4542
|
+
this.maxSize = maxSize;
|
|
4543
|
+
this.smallLimit = Math.max(1, Math.floor(maxSize * 0.1));
|
|
4544
|
+
this.mainLimit = maxSize - this.smallLimit;
|
|
4545
|
+
this.ghostLimit = this.mainLimit;
|
|
4546
|
+
}
|
|
4547
|
+
get size() {
|
|
4548
|
+
return this.map.size;
|
|
4549
|
+
}
|
|
4550
|
+
get(key) {
|
|
4551
|
+
const node = this.map.get(key);
|
|
4552
|
+
if (!node) return void 0;
|
|
4553
|
+
node.freq = Math.min(node.freq + 1, 3);
|
|
4554
|
+
return node.value;
|
|
4555
|
+
}
|
|
4556
|
+
set(key, value) {
|
|
4557
|
+
const existing = this.map.get(key);
|
|
4558
|
+
if (existing) {
|
|
4559
|
+
existing.value = value;
|
|
4560
|
+
return this;
|
|
4561
|
+
}
|
|
4562
|
+
if (this.ghost.has(key)) {
|
|
4563
|
+
this.ghost.delete(key);
|
|
4564
|
+
const node2 = this.createNode(key, value, "main");
|
|
4565
|
+
this.map.set(key, node2);
|
|
4566
|
+
this.pushMain(node2);
|
|
4567
|
+
if (this.mainSize > this.mainLimit) this.evictMain();
|
|
4568
|
+
return this;
|
|
4569
|
+
}
|
|
4570
|
+
const node = this.createNode(key, value, "small");
|
|
4571
|
+
this.map.set(key, node);
|
|
4572
|
+
this.pushSmall(node);
|
|
4573
|
+
if (this.size > this.maxSize) {
|
|
4574
|
+
if (this.smallSize > this.smallLimit) this.evictSmall();
|
|
4575
|
+
else this.evictMain();
|
|
4576
|
+
}
|
|
4577
|
+
return this;
|
|
4578
|
+
}
|
|
4579
|
+
has(key) {
|
|
4580
|
+
return this.map.has(key);
|
|
4581
|
+
}
|
|
4582
|
+
delete(key) {
|
|
4583
|
+
const node = this.map.get(key);
|
|
4584
|
+
if (!node) return false;
|
|
4585
|
+
this.map.delete(key);
|
|
4586
|
+
this.removeNode(node);
|
|
4587
|
+
return true;
|
|
4588
|
+
}
|
|
4589
|
+
clear() {
|
|
4590
|
+
this.map.clear();
|
|
4591
|
+
this.ghost.clear();
|
|
4592
|
+
this.smallHead = this.smallTail = null;
|
|
4593
|
+
this.mainHead = this.mainTail = null;
|
|
4594
|
+
this.smallSize = this.mainSize = 0;
|
|
4595
|
+
}
|
|
4596
|
+
keys() {
|
|
4597
|
+
return withDispose(
|
|
4598
|
+
(function* (self) {
|
|
4599
|
+
for (const key of self.map.keys()) yield key;
|
|
4600
|
+
})(this)
|
|
4601
|
+
);
|
|
4602
|
+
}
|
|
4603
|
+
values() {
|
|
4604
|
+
return withDispose(
|
|
4605
|
+
(function* (self) {
|
|
4606
|
+
for (const node of self.map.values()) yield node.value;
|
|
4607
|
+
})(this)
|
|
4608
|
+
);
|
|
4609
|
+
}
|
|
4610
|
+
entries() {
|
|
4611
|
+
return withDispose(
|
|
4612
|
+
(function* (self) {
|
|
4613
|
+
for (const [key, node] of self.map.entries())
|
|
4614
|
+
yield [key, node.value];
|
|
4615
|
+
})(this)
|
|
4616
|
+
);
|
|
4617
|
+
}
|
|
4618
|
+
forEach(callbackfn, thisArg) {
|
|
4619
|
+
for (const [key, node] of this.map.entries()) {
|
|
4620
|
+
callbackfn.call(thisArg, node.value, key, this);
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
[Symbol.iterator]() {
|
|
4624
|
+
return this.entries();
|
|
4625
|
+
}
|
|
4626
|
+
get [Symbol.toStringTag]() {
|
|
4627
|
+
return "BoundedCache";
|
|
4628
|
+
}
|
|
4629
|
+
createNode(key, value, queue) {
|
|
4630
|
+
return { key, value, freq: 0, queue, prev: null, next: null };
|
|
4631
|
+
}
|
|
4632
|
+
pushSmall(node) {
|
|
4633
|
+
node.next = this.smallHead;
|
|
4634
|
+
node.prev = null;
|
|
4635
|
+
if (this.smallHead) this.smallHead.prev = node;
|
|
4636
|
+
else this.smallTail = node;
|
|
4637
|
+
this.smallHead = node;
|
|
4638
|
+
this.smallSize++;
|
|
4639
|
+
}
|
|
4640
|
+
pushMain(node) {
|
|
4641
|
+
node.next = this.mainHead;
|
|
4642
|
+
node.prev = null;
|
|
4643
|
+
if (this.mainHead) this.mainHead.prev = node;
|
|
4644
|
+
else this.mainTail = node;
|
|
4645
|
+
this.mainHead = node;
|
|
4646
|
+
this.mainSize++;
|
|
4647
|
+
}
|
|
4648
|
+
popSmall() {
|
|
4649
|
+
if (!this.smallTail) return null;
|
|
4650
|
+
const node = this.smallTail;
|
|
4651
|
+
this.smallTail = node.prev;
|
|
4652
|
+
if (this.smallTail) this.smallTail.next = null;
|
|
4653
|
+
else this.smallHead = null;
|
|
4654
|
+
node.prev = null;
|
|
4655
|
+
node.next = null;
|
|
4656
|
+
this.smallSize--;
|
|
4657
|
+
return node;
|
|
4658
|
+
}
|
|
4659
|
+
popMain() {
|
|
4660
|
+
if (!this.mainTail) return null;
|
|
4661
|
+
const node = this.mainTail;
|
|
4662
|
+
this.mainTail = node.prev;
|
|
4663
|
+
if (this.mainTail) this.mainTail.next = null;
|
|
4664
|
+
else this.mainHead = null;
|
|
4665
|
+
node.prev = null;
|
|
4666
|
+
node.next = null;
|
|
4667
|
+
this.mainSize--;
|
|
4668
|
+
return node;
|
|
4669
|
+
}
|
|
4670
|
+
removeNode(node) {
|
|
4671
|
+
this.unlinkNode(node);
|
|
4672
|
+
if (node.queue === "small") {
|
|
4673
|
+
if (node === this.smallHead) this.smallHead = node.next;
|
|
4674
|
+
if (node === this.smallTail) this.smallTail = node.prev;
|
|
4675
|
+
this.smallSize--;
|
|
4676
|
+
} else {
|
|
4677
|
+
if (node === this.mainHead) this.mainHead = node.next;
|
|
4678
|
+
if (node === this.mainTail) this.mainTail = node.prev;
|
|
4679
|
+
this.mainSize--;
|
|
4680
|
+
}
|
|
4681
|
+
node.prev = null;
|
|
4682
|
+
node.next = null;
|
|
4683
|
+
}
|
|
4684
|
+
unlinkNode(node) {
|
|
4685
|
+
if (node.prev) node.prev.next = node.next;
|
|
4686
|
+
if (node.next) node.next.prev = node.prev;
|
|
4687
|
+
}
|
|
4688
|
+
shouldPromoteFromSmall(node) {
|
|
4689
|
+
return node.freq > 1;
|
|
4690
|
+
}
|
|
4691
|
+
shouldRetryInMain(node) {
|
|
4692
|
+
return node.freq >= 1;
|
|
4693
|
+
}
|
|
4694
|
+
promoteToMain(node) {
|
|
4695
|
+
node.queue = "main";
|
|
4696
|
+
this.pushMain(node);
|
|
4697
|
+
}
|
|
4698
|
+
addToGhost(key) {
|
|
4699
|
+
this.ghost.add(key);
|
|
4700
|
+
if (this.ghost.size <= this.ghostLimit) return;
|
|
4701
|
+
const firstGhost = this.ghost.values().next().value;
|
|
4702
|
+
if (firstGhost !== void 0) this.ghost.delete(firstGhost);
|
|
4703
|
+
}
|
|
4704
|
+
evictFromCache(node) {
|
|
4705
|
+
this.map.delete(node.key);
|
|
4706
|
+
}
|
|
4707
|
+
evictSmall() {
|
|
4708
|
+
while (this.smallSize > 0) {
|
|
4709
|
+
const node = this.popSmall();
|
|
4710
|
+
if (!node) return;
|
|
4711
|
+
if (this.shouldPromoteFromSmall(node)) {
|
|
4712
|
+
this.promoteToMain(node);
|
|
4713
|
+
if (this.mainSize > this.mainLimit) {
|
|
4714
|
+
this.evictMain();
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
this.evictFromCache(node);
|
|
4720
|
+
this.addToGhost(node.key);
|
|
4721
|
+
return;
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
evictMain() {
|
|
4725
|
+
while (this.mainSize > 0) {
|
|
4726
|
+
const node = this.popMain();
|
|
4727
|
+
if (!node) return;
|
|
4728
|
+
if (this.shouldRetryInMain(node)) {
|
|
4729
|
+
node.freq--;
|
|
4730
|
+
this.pushMain(node);
|
|
4731
|
+
continue;
|
|
4732
|
+
}
|
|
4733
|
+
this.evictFromCache(node);
|
|
4734
|
+
return;
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
};
|
|
4738
|
+
function createBoundedCache(maxSize) {
|
|
4739
|
+
return new BoundedCache(maxSize);
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
// src/query-cache.ts
|
|
4743
|
+
var queryCache = createBoundedCache(1e3);
|
|
4523
4744
|
function makeAlias(name) {
|
|
4524
4745
|
const base = name.toLowerCase().replace(/[^a-z0-9_]/g, "_").slice(0, 50);
|
|
4525
4746
|
const safe = /^[a-z_]/.test(base) ? base : `_${base}`;
|
|
4526
4747
|
return SQL_RESERVED_WORDS.has(safe) ? `${safe}_t` : safe;
|
|
4527
4748
|
}
|
|
4528
4749
|
function toSqliteParams(sql, params) {
|
|
4529
|
-
const
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4750
|
+
const reorderedParams = [];
|
|
4751
|
+
let lastIndex = 0;
|
|
4752
|
+
const parts = [];
|
|
4753
|
+
for (let i = 0; i < sql.length; i++) {
|
|
4754
|
+
if (sql[i] === "$" && i + 1 < sql.length) {
|
|
4755
|
+
let num = 0;
|
|
4756
|
+
let j = i + 1;
|
|
4757
|
+
while (j < sql.length && sql[j] >= "0" && sql[j] <= "9") {
|
|
4758
|
+
num = num * 10 + (sql.charCodeAt(j) - 48);
|
|
4759
|
+
j++;
|
|
4760
|
+
}
|
|
4761
|
+
if (j > i + 1) {
|
|
4762
|
+
parts.push(sql.substring(lastIndex, i));
|
|
4763
|
+
parts.push("?");
|
|
4764
|
+
reorderedParams.push(params[num - 1]);
|
|
4765
|
+
i = j - 1;
|
|
4766
|
+
lastIndex = j;
|
|
4767
|
+
}
|
|
4538
4768
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
return { sql:
|
|
4769
|
+
}
|
|
4770
|
+
parts.push(sql.substring(lastIndex));
|
|
4771
|
+
return { sql: parts.join(""), params: reorderedParams };
|
|
4772
|
+
}
|
|
4773
|
+
function canonicalizeQuery(modelName, method, args) {
|
|
4774
|
+
function normalize(obj, path = []) {
|
|
4775
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
4776
|
+
if (Array.isArray(obj)) return obj.map((v, i) => normalize(v, [...path, String(i)]));
|
|
4777
|
+
const sorted = {};
|
|
4778
|
+
const keys = Object.keys(obj).sort();
|
|
4779
|
+
for (const key of keys) {
|
|
4780
|
+
const fullPath = [...path, key].join(".");
|
|
4781
|
+
const value = obj[key];
|
|
4782
|
+
if (schemaParser.isDynamicParameter(value)) {
|
|
4783
|
+
sorted[key] = `__DYN:${fullPath}__`;
|
|
4784
|
+
} else {
|
|
4785
|
+
sorted[key] = normalize(value, [...path, key]);
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
return sorted;
|
|
4789
|
+
}
|
|
4790
|
+
const canonical = normalize(args);
|
|
4791
|
+
return `${modelName}:${method}:${JSON.stringify(canonical)}`;
|
|
4542
4792
|
}
|
|
4543
|
-
function
|
|
4793
|
+
function extractValueFromPath(obj, path) {
|
|
4794
|
+
const parts = path.split(".");
|
|
4795
|
+
let current = obj;
|
|
4796
|
+
for (const part of parts) {
|
|
4797
|
+
if (current === void 0 || current === null) return void 0;
|
|
4798
|
+
current = current[part];
|
|
4799
|
+
}
|
|
4800
|
+
return current;
|
|
4801
|
+
}
|
|
4802
|
+
function rebuildParams(mappings, args) {
|
|
4803
|
+
const params = new Array(mappings.length);
|
|
4804
|
+
for (let i = 0; i < mappings.length; i++) {
|
|
4805
|
+
const mapping = mappings[i];
|
|
4806
|
+
if (mapping.dynamicName !== void 0) {
|
|
4807
|
+
const parts = mapping.dynamicName.split(":");
|
|
4808
|
+
const path = parts.length === 2 ? parts[1] : mapping.dynamicName;
|
|
4809
|
+
params[i] = extractValueFromPath(args, path);
|
|
4810
|
+
} else {
|
|
4811
|
+
params[i] = mapping.value;
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4814
|
+
return params;
|
|
4815
|
+
}
|
|
4816
|
+
function buildSQLFull(model, models, method, args, dialect) {
|
|
4544
4817
|
const tableName = buildTableReference(
|
|
4545
4818
|
SQL_TEMPLATES.PUBLIC_SCHEMA,
|
|
4546
4819
|
model.tableName,
|
|
@@ -4562,32 +4835,13 @@ function buildSQL(model, models, method, args, dialect) {
|
|
|
4562
4835
|
let result;
|
|
4563
4836
|
switch (method) {
|
|
4564
4837
|
case "aggregate":
|
|
4565
|
-
result = buildAggregateSql(
|
|
4566
|
-
withMethod,
|
|
4567
|
-
whereResult,
|
|
4568
|
-
tableName,
|
|
4569
|
-
alias,
|
|
4570
|
-
model
|
|
4571
|
-
);
|
|
4838
|
+
result = buildAggregateSql(withMethod, whereResult, tableName, alias, model);
|
|
4572
4839
|
break;
|
|
4573
4840
|
case "groupBy":
|
|
4574
|
-
result = buildGroupBySql(
|
|
4575
|
-
withMethod,
|
|
4576
|
-
whereResult,
|
|
4577
|
-
tableName,
|
|
4578
|
-
alias,
|
|
4579
|
-
model,
|
|
4580
|
-
dialect
|
|
4581
|
-
);
|
|
4841
|
+
result = buildGroupBySql(withMethod, whereResult, tableName, alias, model, dialect);
|
|
4582
4842
|
break;
|
|
4583
4843
|
case "count":
|
|
4584
|
-
result = buildCountSql(
|
|
4585
|
-
whereResult,
|
|
4586
|
-
tableName,
|
|
4587
|
-
alias,
|
|
4588
|
-
args.skip,
|
|
4589
|
-
dialect
|
|
4590
|
-
);
|
|
4844
|
+
result = buildCountSql(whereResult, tableName, alias, args.skip, dialect);
|
|
4591
4845
|
break;
|
|
4592
4846
|
default:
|
|
4593
4847
|
result = buildSelectSql({
|
|
@@ -4600,7 +4854,35 @@ function buildSQL(model, models, method, args, dialect) {
|
|
|
4600
4854
|
dialect
|
|
4601
4855
|
});
|
|
4602
4856
|
}
|
|
4603
|
-
|
|
4857
|
+
const finalResult = dialect === "sqlite" ? toSqliteParams(result.sql, result.params) : { sql: result.sql, params: [...result.params] };
|
|
4858
|
+
return __spreadProps(__spreadValues({}, finalResult), {
|
|
4859
|
+
paramMappings: result.paramMappings
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
function buildSQLWithCache(model, models, method, args, dialect) {
|
|
4863
|
+
const cacheKey = canonicalizeQuery(model.name, method, args);
|
|
4864
|
+
const cached = queryCache.get(cacheKey);
|
|
4865
|
+
if (cached) {
|
|
4866
|
+
const params = rebuildParams(cached.paramMappings, args);
|
|
4867
|
+
return { sql: cached.sql, params };
|
|
4868
|
+
}
|
|
4869
|
+
const result = buildSQLFull(model, models, method, args, dialect);
|
|
4870
|
+
queryCache.set(cacheKey, {
|
|
4871
|
+
sql: result.sql,
|
|
4872
|
+
paramMappings: result.paramMappings
|
|
4873
|
+
});
|
|
4874
|
+
return { sql: result.sql, params: result.params };
|
|
4875
|
+
}
|
|
4876
|
+
var ACCELERATED_METHODS = /* @__PURE__ */ new Set([
|
|
4877
|
+
"findMany",
|
|
4878
|
+
"findFirst",
|
|
4879
|
+
"findUnique",
|
|
4880
|
+
"count",
|
|
4881
|
+
"aggregate",
|
|
4882
|
+
"groupBy"
|
|
4883
|
+
]);
|
|
4884
|
+
function buildSQL(model, models, method, args, dialect) {
|
|
4885
|
+
return buildSQLWithCache(model, models, method, args, dialect);
|
|
4604
4886
|
}
|
|
4605
4887
|
function executePostgres(client, sql, params) {
|
|
4606
4888
|
return __async(this, null, function* () {
|