@stamhoofd/sql 2.17.0 → 2.18.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/src/SQL.d.ts +3 -0
- package/dist/src/SQL.d.ts.map +1 -1
- package/dist/src/SQL.js +7 -0
- package/dist/src/SQL.js.map +1 -1
- package/dist/src/SQLDelete.d.ts +1 -1
- package/dist/src/SQLJoin.d.ts +1 -1
- package/dist/src/SQLJsonExpressions.d.ts +3 -2
- package/dist/src/SQLJsonExpressions.d.ts.map +1 -1
- package/dist/src/SQLJsonExpressions.js +5 -2
- package/dist/src/SQLJsonExpressions.js.map +1 -1
- package/dist/src/SQLSelect.d.ts +1 -1
- package/dist/src/SQLSelect.js +1 -1
- package/dist/src/SQLSelect.js.map +1 -1
- package/dist/src/SQLWhere.d.ts +13 -3
- package/dist/src/SQLWhere.d.ts.map +1 -1
- package/dist/src/SQLWhere.js +57 -42
- package/dist/src/SQLWhere.js.map +1 -1
- package/dist/src/filters/SQLFilter.d.ts.map +1 -1
- package/dist/src/filters/SQLFilter.js +65 -22
- package/dist/src/filters/SQLFilter.js.map +1 -1
- package/dist/src/filters/SQLSorter.d.ts.map +1 -1
- package/dist/src/filters/SQLSorter.js +7 -0
- package/dist/src/filters/SQLSorter.js.map +1 -1
- package/package.json +2 -2
- package/src/SQL.ts +9 -0
- package/src/SQLJsonExpressions.ts +5 -2
- package/src/SQLSelect.ts +1 -1
- package/src/SQLWhere.ts +82 -53
- package/src/filters/SQLFilter.ts +88 -51
- package/src/filters/SQLSorter.ts +8 -0
package/src/filters/SQLFilter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SimpleError } from "@simonbackx/simple-errors";
|
|
2
|
-
import { StamhoofdFilter } from "@stamhoofd/structures";
|
|
2
|
+
import { StamhoofdCompareValue, StamhoofdFilter } from "@stamhoofd/structures";
|
|
3
3
|
import { SQL } from "../SQL";
|
|
4
4
|
import { SQLExpression } from "../SQLExpression";
|
|
5
5
|
import { SQLArray, SQLCast, SQLColumnExpression, SQLNull, SQLSafeValue, SQLScalarValue, scalarToSQLExpression, scalarToSQLJSONExpression } from "../SQLExpressions";
|
|
@@ -25,17 +25,72 @@ export function notSQLFilterCompiler(filter: StamhoofdFilter, filters: SQLFilter
|
|
|
25
25
|
return new SQLWhereNot(andRunner)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function
|
|
29
|
-
if (
|
|
30
|
-
|
|
28
|
+
function guardFilterCompareValue(val: any): StamhoofdCompareValue {
|
|
29
|
+
if (val instanceof Date) {
|
|
30
|
+
return val
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof val === 'string') {
|
|
34
|
+
return val
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof val === 'number') {
|
|
38
|
+
return val
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (typeof val === 'boolean') {
|
|
42
|
+
return val;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (val === null) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof val === 'object' && "$" in val) {
|
|
50
|
+
if (val["$"] === '$now') {
|
|
51
|
+
return val;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
throw new Error('Invalid compare value. Expected a string, number, boolean, date or null.')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function doNormalizeValue(val: StamhoofdCompareValue): string|number|Date|null {
|
|
59
|
+
if (val instanceof Date) {
|
|
60
|
+
return val
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeof val === 'string') {
|
|
64
|
+
return val.toLocaleLowerCase()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof val === 'boolean') {
|
|
68
|
+
return val === true ? 1 : 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (val === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof val === 'object' && "$" in val) {
|
|
76
|
+
const specialValue = val["$"];
|
|
77
|
+
|
|
78
|
+
switch (specialValue) {
|
|
79
|
+
case '$now':
|
|
80
|
+
return doNormalizeValue(new Date())
|
|
81
|
+
default:
|
|
82
|
+
throw new Error('Unsupported magic value ' + specialValue)
|
|
83
|
+
}
|
|
31
84
|
}
|
|
32
85
|
|
|
86
|
+
return val;
|
|
33
87
|
}
|
|
34
88
|
|
|
35
|
-
function
|
|
36
|
-
if (typeof s !== 'string' && typeof s !== 'number' && typeof s !== 'boolean' && !(s instanceof Date)) {
|
|
89
|
+
function guardScalar(s: any): asserts s is SQLScalarValue|null {
|
|
90
|
+
if (typeof s !== 'string' && typeof s !== 'number' && typeof s !== 'boolean' && !(s instanceof Date) && s !== null) {
|
|
37
91
|
throw new Error('Invalid scalar value')
|
|
38
92
|
}
|
|
93
|
+
|
|
39
94
|
}
|
|
40
95
|
|
|
41
96
|
function guardString(s: any): asserts s is string {
|
|
@@ -68,7 +123,11 @@ export function createSQLFilterNamespace(definitions: SQLFilterDefinitions): SQL
|
|
|
68
123
|
type SQLExpressionFilterOptions = {normalizeValue?: (v: SQLScalarValue|null) => SQLScalarValue|null, isJSONValue?: boolean, isJSONObject?: boolean, nullable?: boolean}
|
|
69
124
|
|
|
70
125
|
export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression, {normalizeValue, isJSONObject = false, isJSONValue = false, nullable = false}: SQLExpressionFilterOptions = {}): SQLFilterCompiler {
|
|
71
|
-
|
|
126
|
+
normalizeValue = normalizeValue ?? ((v) => v);
|
|
127
|
+
const norm = (val: any) => {
|
|
128
|
+
const n = doNormalizeValue(guardFilterCompareValue(val));
|
|
129
|
+
return normalizeValue(n);
|
|
130
|
+
}
|
|
72
131
|
const convertToExpression = isJSONValue ? scalarToSQLJSONExpression : scalarToSQLExpression
|
|
73
132
|
|
|
74
133
|
return (filter: StamhoofdFilter, filters: SQLFilterDefinitions) => {
|
|
@@ -83,11 +142,9 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
83
142
|
}
|
|
84
143
|
|
|
85
144
|
|
|
86
|
-
const f = filter
|
|
145
|
+
const f = filter;
|
|
87
146
|
|
|
88
147
|
if ('$eq' in f) {
|
|
89
|
-
guardScalar(f.$eq);
|
|
90
|
-
|
|
91
148
|
if (isJSONObject) {
|
|
92
149
|
const v = norm(f.$eq);
|
|
93
150
|
|
|
@@ -100,13 +157,9 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
100
157
|
// }
|
|
101
158
|
|
|
102
159
|
// else
|
|
103
|
-
return new
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
convertToExpression(JSON.stringify(v))
|
|
107
|
-
),
|
|
108
|
-
SQLWhereSign.Equal,
|
|
109
|
-
new SQLSafeValue(1)
|
|
160
|
+
return new SQLJsonContains(
|
|
161
|
+
sqlExpression,
|
|
162
|
+
convertToExpression(JSON.stringify(v))
|
|
110
163
|
);
|
|
111
164
|
}
|
|
112
165
|
return new SQLWhereEqual(sqlExpression, SQLWhereSign.Equal, convertToExpression(norm(f.$eq)));
|
|
@@ -133,25 +186,17 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
133
186
|
// that makes comparing more difficult, to combat this, we still need to use SQLJsonOverlaps with the JSON null value
|
|
134
187
|
return new SQLWhereOr([
|
|
135
188
|
new SQLWhereEqual(sqlExpression, SQLWhereSign.Equal, new SQLNull()), // checks path not exists (= mysql null)
|
|
136
|
-
new
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
convertToExpression(JSON.stringify(v)) // contains json null
|
|
140
|
-
),
|
|
141
|
-
SQLWhereSign.Equal,
|
|
142
|
-
new SQLSafeValue(1)
|
|
189
|
+
new SQLJsonOverlaps(
|
|
190
|
+
sqlExpression,
|
|
191
|
+
convertToExpression(JSON.stringify(v)) // contains json null
|
|
143
192
|
)
|
|
144
193
|
]);
|
|
145
194
|
}
|
|
146
195
|
|
|
147
196
|
// else
|
|
148
|
-
return new
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
convertToExpression(JSON.stringify(v))
|
|
152
|
-
),
|
|
153
|
-
SQLWhereSign.Equal,
|
|
154
|
-
new SQLSafeValue(1)
|
|
197
|
+
return new SQLJsonOverlaps(
|
|
198
|
+
sqlExpression,
|
|
199
|
+
convertToExpression(JSON.stringify(v))
|
|
155
200
|
);
|
|
156
201
|
}
|
|
157
202
|
|
|
@@ -165,30 +210,24 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
165
210
|
new SQLWhereEqual(sqlExpression, SQLWhereSign.Equal, new SQLArray(remaining))
|
|
166
211
|
]);
|
|
167
212
|
}
|
|
168
|
-
return new SQLWhereEqual(sqlExpression, SQLWhereSign.Equal, new SQLArray(v));
|
|
213
|
+
return new SQLWhereEqual(sqlExpression, SQLWhereSign.Equal, new SQLArray(v as SQLScalarValue[]));
|
|
169
214
|
}
|
|
170
215
|
|
|
171
216
|
if ('$neq' in f) {
|
|
172
|
-
guardScalar(f.$neq);
|
|
173
|
-
|
|
174
217
|
if (isJSONObject) {
|
|
175
|
-
const v = norm(f.$
|
|
218
|
+
const v = norm(f.$neq);
|
|
176
219
|
|
|
177
|
-
return new
|
|
220
|
+
return new SQLWhereNot(
|
|
178
221
|
new SQLJsonContains(
|
|
179
222
|
sqlExpression,
|
|
180
223
|
convertToExpression(JSON.stringify(v))
|
|
181
|
-
)
|
|
182
|
-
SQLWhereSign.Equal,
|
|
183
|
-
new SQLSafeValue(0)
|
|
224
|
+
)
|
|
184
225
|
);
|
|
185
226
|
}
|
|
186
227
|
return new SQLWhereEqual(sqlExpression, SQLWhereSign.NotEqual, convertToExpression(norm(f.$neq)));
|
|
187
228
|
}
|
|
188
229
|
|
|
189
230
|
if ('$gt' in f) {
|
|
190
|
-
guardScalar(f.$gt);
|
|
191
|
-
|
|
192
231
|
if (isJSONObject) {
|
|
193
232
|
throw new Error('Greater than is not supported in this place')
|
|
194
233
|
}
|
|
@@ -203,8 +242,6 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
203
242
|
}
|
|
204
243
|
|
|
205
244
|
if ('$gte' in f) {
|
|
206
|
-
guardScalar(f.$gte);
|
|
207
|
-
|
|
208
245
|
if (isJSONObject) {
|
|
209
246
|
throw new Error('Greater than is not supported in this place')
|
|
210
247
|
}
|
|
@@ -217,8 +254,6 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
217
254
|
}
|
|
218
255
|
|
|
219
256
|
if ('$lte' in f) {
|
|
220
|
-
guardScalar(f.$lte);
|
|
221
|
-
|
|
222
257
|
if (isJSONObject) {
|
|
223
258
|
throw new Error('Greater than is not supported in this place')
|
|
224
259
|
}
|
|
@@ -243,8 +278,6 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
243
278
|
|
|
244
279
|
|
|
245
280
|
if ('$lt' in f) {
|
|
246
|
-
guardScalar(f.$lt);
|
|
247
|
-
|
|
248
281
|
if (isJSONObject) {
|
|
249
282
|
throw new Error('Less than is not supported in this place')
|
|
250
283
|
}
|
|
@@ -268,7 +301,11 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
268
301
|
}
|
|
269
302
|
|
|
270
303
|
if ('$contains' in f) {
|
|
271
|
-
|
|
304
|
+
const needle = norm(f.$contains);
|
|
305
|
+
|
|
306
|
+
if (typeof needle !== 'string') {
|
|
307
|
+
throw new Error('Invalid needle for contains filter')
|
|
308
|
+
}
|
|
272
309
|
|
|
273
310
|
if (isJSONObject) {
|
|
274
311
|
return new SQLWhereEqual(
|
|
@@ -276,7 +313,7 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
276
313
|
sqlExpression,
|
|
277
314
|
'one',
|
|
278
315
|
convertToExpression(
|
|
279
|
-
'%'+SQLWhereLike.escape(
|
|
316
|
+
'%'+SQLWhereLike.escape(needle)+'%'
|
|
280
317
|
)
|
|
281
318
|
),
|
|
282
319
|
SQLWhereSign.NotEqual,
|
|
@@ -289,7 +326,7 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
289
326
|
return new SQLWhereLike(
|
|
290
327
|
new SQLCast(new SQLJsonUnquote(sqlExpression), 'CHAR'),
|
|
291
328
|
convertToExpression(
|
|
292
|
-
'%'+SQLWhereLike.escape(
|
|
329
|
+
'%'+SQLWhereLike.escape(needle)+'%'
|
|
293
330
|
)
|
|
294
331
|
);
|
|
295
332
|
}
|
|
@@ -297,7 +334,7 @@ export function createSQLExpressionFilterCompiler(sqlExpression: SQLExpression,
|
|
|
297
334
|
return new SQLWhereLike(
|
|
298
335
|
sqlExpression,
|
|
299
336
|
convertToExpression(
|
|
300
|
-
'%'+SQLWhereLike.escape(
|
|
337
|
+
'%'+SQLWhereLike.escape(needle)+'%'
|
|
301
338
|
)
|
|
302
339
|
);
|
|
303
340
|
}
|
package/src/filters/SQLSorter.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { PlainObject } from "@simonbackx/simple-encoding";
|
|
|
2
2
|
import { SortDefinition, SortList } from "@stamhoofd/structures";
|
|
3
3
|
|
|
4
4
|
import { SQLOrderBy, SQLOrderByDirection } from "../SQLOrderBy";
|
|
5
|
+
import { SimpleError } from "@simonbackx/simple-errors";
|
|
5
6
|
|
|
6
7
|
export type SQLSortDefinition<T, B extends PlainObject = PlainObject> = SortDefinition<T, B> & {
|
|
7
8
|
toSQL(direction: SQLOrderByDirection): SQLOrderBy
|
|
@@ -10,6 +11,13 @@ export type SQLSortDefinition<T, B extends PlainObject = PlainObject> = SortDefi
|
|
|
10
11
|
export type SQLSortDefinitions<T = any> = Record<string, SQLSortDefinition<T>>
|
|
11
12
|
|
|
12
13
|
export function compileToSQLSorter(sortBy: SortList, definitions: SQLSortDefinitions): SQLOrderBy {
|
|
14
|
+
if (sortBy.length === 0 ){
|
|
15
|
+
throw new SimpleError({
|
|
16
|
+
code: 'empty_sort',
|
|
17
|
+
message: 'No sort passed'
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
const sorters: SQLOrderBy[] = [];
|
|
14
22
|
|
|
15
23
|
for (const s of sortBy) {
|