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