xcraft-core-pickaxe 0.1.4 → 0.1.6
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/lib/operator-to-sql.js +49 -1
- package/lib/operators.js +104 -0
- package/lib/picks.js +29 -2
- package/lib/query-builder.js +112 -12
- package/lib/query-to-sql.js +26 -6
- package/package.json +1 -1
package/lib/operator-to-sql.js
CHANGED
|
@@ -21,6 +21,9 @@ const operators = {
|
|
|
21
21
|
if (typeof value === 'boolean') {
|
|
22
22
|
return value ? 'TRUE' : 'FALSE';
|
|
23
23
|
}
|
|
24
|
+
if (!values) {
|
|
25
|
+
return escape(value);
|
|
26
|
+
}
|
|
24
27
|
values.push(value);
|
|
25
28
|
return '?';
|
|
26
29
|
},
|
|
@@ -121,6 +124,24 @@ const operators = {
|
|
|
121
124
|
return `(${sqlConditions.join(' OR ')})`;
|
|
122
125
|
},
|
|
123
126
|
|
|
127
|
+
abs({value}, values) {
|
|
128
|
+
return `ABS(${sql(value, values)})`;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
plus({values: list}, values) {
|
|
132
|
+
return `(${list
|
|
133
|
+
.map((value) => sql(value, values))
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join(' + ')})`;
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
minus({values: list}, values) {
|
|
139
|
+
return `(${list
|
|
140
|
+
.map((value) => sql(value, values))
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
.join(' - ')})`;
|
|
143
|
+
},
|
|
144
|
+
|
|
124
145
|
length({list}, values) {
|
|
125
146
|
return `json_array_length(${sql(list, values)})`;
|
|
126
147
|
},
|
|
@@ -174,6 +195,33 @@ const operators = {
|
|
|
174
195
|
desc({value}, values) {
|
|
175
196
|
return `${sql(value, values)} DESC`;
|
|
176
197
|
},
|
|
198
|
+
|
|
199
|
+
count({field, distinct}, values) {
|
|
200
|
+
if (!field) {
|
|
201
|
+
return `COUNT(*)`;
|
|
202
|
+
}
|
|
203
|
+
return `COUNT(${distinct ? 'DISTINCT ' : ''}${sql(field, values)})`;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
avg({field}, values) {
|
|
207
|
+
return `AVG(${sql(field, values)})`;
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
max({field}, values) {
|
|
211
|
+
return `MAX(${sql(field, values)})`;
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
min({field}, values) {
|
|
215
|
+
return `MIN(${sql(field, values)})`;
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
sum({field, distinct}, values) {
|
|
219
|
+
return `SUM(${distinct ? 'DISTINCT ' : ''}${sql(field, values)})`;
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
groupArray({field}, values) {
|
|
223
|
+
return `json_group_array(${sql(field, values)})`;
|
|
224
|
+
},
|
|
177
225
|
};
|
|
178
226
|
|
|
179
227
|
/**
|
|
@@ -189,7 +237,7 @@ function getOperator(operatorOrPick) {
|
|
|
189
237
|
|
|
190
238
|
/**
|
|
191
239
|
* @param {Operator | AnyPick} operatorOrPick
|
|
192
|
-
* @param {any[]} values
|
|
240
|
+
* @param {any[] | null} values
|
|
193
241
|
* @returns {string}
|
|
194
242
|
*/
|
|
195
243
|
function sql(operatorOrPick, values) {
|
package/lib/operators.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
/* eslint-disable jsdoc/require-returns */
|
|
1
2
|
// @ts-check
|
|
2
3
|
|
|
4
|
+
const {number, array} = require('xcraft-core-stones');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @template {AnyTypeOrShape} T
|
|
8
|
+
* @typedef {import("./picks.js").ValuePick<T>} ValuePick
|
|
9
|
+
*/
|
|
10
|
+
|
|
3
11
|
function op(value) {
|
|
4
12
|
if (value === null) {
|
|
5
13
|
return operators.null();
|
|
@@ -160,6 +168,33 @@ const operators = {
|
|
|
160
168
|
});
|
|
161
169
|
},
|
|
162
170
|
|
|
171
|
+
abs(value) {
|
|
172
|
+
value = op(value);
|
|
173
|
+
return /** @type {const} */ ({
|
|
174
|
+
operator: 'abs',
|
|
175
|
+
type: number,
|
|
176
|
+
value,
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
plus(...values) {
|
|
181
|
+
values = values.map(op);
|
|
182
|
+
return /** @type {const} */ ({
|
|
183
|
+
operator: 'plus',
|
|
184
|
+
type: number,
|
|
185
|
+
values,
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
minus(...values) {
|
|
190
|
+
values = values.map(op);
|
|
191
|
+
return /** @type {const} */ ({
|
|
192
|
+
operator: 'minus',
|
|
193
|
+
type: number,
|
|
194
|
+
values,
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
|
|
163
198
|
length(list) {
|
|
164
199
|
return /** @type {const} */ ({
|
|
165
200
|
operator: 'length',
|
|
@@ -223,6 +258,75 @@ const operators = {
|
|
|
223
258
|
value,
|
|
224
259
|
});
|
|
225
260
|
},
|
|
261
|
+
|
|
262
|
+
count(field, distinct) {
|
|
263
|
+
return /** @type {const} */ ({
|
|
264
|
+
operator: 'count',
|
|
265
|
+
type: number,
|
|
266
|
+
field,
|
|
267
|
+
distinct,
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @template {AnyTypeOrShape} T
|
|
273
|
+
* @param {ValuePick<T>} field
|
|
274
|
+
*/
|
|
275
|
+
avg(field) {
|
|
276
|
+
return /** @type {const} */ ({
|
|
277
|
+
operator: 'avg',
|
|
278
|
+
type: field.type,
|
|
279
|
+
field: field.value,
|
|
280
|
+
});
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @template {AnyTypeOrShape} T
|
|
285
|
+
* @param {ValuePick<T>} field
|
|
286
|
+
*/
|
|
287
|
+
max(field) {
|
|
288
|
+
return /** @type {const} */ ({
|
|
289
|
+
operator: 'max',
|
|
290
|
+
type: field.type,
|
|
291
|
+
field: field.value,
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* @template {AnyTypeOrShape} T
|
|
297
|
+
* @param {ValuePick<T>} field
|
|
298
|
+
*/
|
|
299
|
+
min(field) {
|
|
300
|
+
return /** @type {const} */ ({
|
|
301
|
+
operator: 'min',
|
|
302
|
+
type: field.type,
|
|
303
|
+
field: field.value,
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @template {AnyTypeOrShape} T
|
|
309
|
+
* @param {ValuePick<T>} field
|
|
310
|
+
*/
|
|
311
|
+
sum(field) {
|
|
312
|
+
return /** @type {const} */ ({
|
|
313
|
+
operator: 'sum',
|
|
314
|
+
type: field.type,
|
|
315
|
+
field: field.value,
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @template {AnyTypeOrShape} T
|
|
321
|
+
* @param {ValuePick<T>} field
|
|
322
|
+
*/
|
|
323
|
+
groupArray(field) {
|
|
324
|
+
return /** @type {const} */ ({
|
|
325
|
+
operator: 'groupArray',
|
|
326
|
+
type: array(field.type),
|
|
327
|
+
field: field.value,
|
|
328
|
+
});
|
|
329
|
+
},
|
|
226
330
|
};
|
|
227
331
|
|
|
228
332
|
/**
|
package/lib/picks.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
number,
|
|
13
13
|
StringType,
|
|
14
14
|
string,
|
|
15
|
+
NumberType,
|
|
15
16
|
} = require('xcraft-core-stones');
|
|
16
17
|
|
|
17
18
|
const $o = require('./operators.js');
|
|
@@ -49,7 +50,7 @@ const $o = require('./operators.js');
|
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
52
|
* @template {Type} T
|
|
52
|
-
* @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : ValuePick<T>} PickOfType
|
|
53
|
+
* @typedef { [T] extends [ArrayType<infer V>] ? ArrayPick<V> : [T] extends [ObjectType<infer S>] ? ObjectPick<S> : [T] extends [ObjectMapType<infer V>] ? RecordPick<StringType,V> : [T] extends [RecordType<infer K,infer V>] ? RecordPick<K,V> : [T] extends [NumberType] ? NumberPick : ValuePick<T>} PickOfType
|
|
53
54
|
*/
|
|
54
55
|
/**
|
|
55
56
|
* @template {AnyTypeOrShape} T
|
|
@@ -155,6 +156,29 @@ class ValuePick {
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
/**
|
|
160
|
+
* @extends {ValuePick<NumberType>}
|
|
161
|
+
*/
|
|
162
|
+
class NumberPick extends ValuePick {
|
|
163
|
+
abs() {
|
|
164
|
+
return $o.abs(this.value);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {number | ValuePick<NumberType>} value
|
|
169
|
+
*/
|
|
170
|
+
plus(value) {
|
|
171
|
+
return $o.plus(this.value, value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {number | ValuePick<NumberType>} value
|
|
176
|
+
*/
|
|
177
|
+
minus(value) {
|
|
178
|
+
return $o.minus(this.value, value);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
158
182
|
/**
|
|
159
183
|
* @template {ObjectShape} T
|
|
160
184
|
*/
|
|
@@ -340,7 +364,7 @@ class ArrayPick {
|
|
|
340
364
|
}
|
|
341
365
|
|
|
342
366
|
get length() {
|
|
343
|
-
return new
|
|
367
|
+
return new NumberPick(number, {
|
|
344
368
|
field: $o.length(this.value),
|
|
345
369
|
path: [],
|
|
346
370
|
});
|
|
@@ -437,6 +461,9 @@ function makeTypePick(type, context) {
|
|
|
437
461
|
if (type instanceof RecordType) {
|
|
438
462
|
return /** @type {any} */ (new RecordPick(type, context));
|
|
439
463
|
}
|
|
464
|
+
if (type instanceof NumberType) {
|
|
465
|
+
return /** @type {any} */ (new NumberPick(type, context));
|
|
466
|
+
}
|
|
440
467
|
return /** @type {any} */ (new ValuePick(type, context));
|
|
441
468
|
}
|
|
442
469
|
|
package/lib/query-builder.js
CHANGED
|
@@ -28,6 +28,28 @@ const {queryToSql} = require('./query-to-sql.js');
|
|
|
28
28
|
* @typedef {import("./picks.js").Path} Path
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Operators["count"] | Operators["avg"] | Operators["max"] | Operators["min"] | Operators["sum"] | Operators["groupArray"]} Aggregator
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Operators["abs"] | Operators["plus"] | Operators["minus"]} MathOperator
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {AnyPick | Aggregator | MathOperator} SelectValue
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* @template T
|
|
43
|
+
* @typedef {T extends SelectValue ? T["type"]: never} SelectValueType
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Record<string, SelectValue> | [SelectValue, ...SelectValue[]]} SelectResult
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* @template {SelectResult} T
|
|
50
|
+
* @typedef {{[K in keyof T]: t<SelectValueType<T[K]>>}} QueryResultOf
|
|
51
|
+
*/
|
|
52
|
+
|
|
31
53
|
/**
|
|
32
54
|
* @typedef {ValuePick<any> | Operators["desc"] | Operators["asc"]} OrderByValue
|
|
33
55
|
*/
|
|
@@ -35,15 +57,21 @@ const {queryToSql} = require('./query-to-sql.js');
|
|
|
35
57
|
* @typedef {OrderByValue | OrderByValue[]} OrderByResult
|
|
36
58
|
*/
|
|
37
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @typedef {ValuePick<any> | ValuePick<any>[]} GroupByResult
|
|
62
|
+
*/
|
|
63
|
+
|
|
38
64
|
/**
|
|
39
65
|
* @typedef {{
|
|
40
66
|
* db: string,
|
|
41
67
|
* from: string,
|
|
42
68
|
* scope?: ObjectPick<any>
|
|
43
|
-
* select:
|
|
69
|
+
* select: SelectResult,
|
|
44
70
|
* selectOneField?: boolean,
|
|
71
|
+
* distinct?: boolean,
|
|
45
72
|
* where?: Operator,
|
|
46
|
-
* orderBy?: OrderByResult
|
|
73
|
+
* orderBy?: OrderByResult,
|
|
74
|
+
* groupBy?: GroupByResult
|
|
47
75
|
* }} QueryObj
|
|
48
76
|
*/
|
|
49
77
|
|
|
@@ -76,7 +104,10 @@ class FinalQuery {
|
|
|
76
104
|
}
|
|
77
105
|
|
|
78
106
|
#useRaw() {
|
|
79
|
-
return
|
|
107
|
+
return (
|
|
108
|
+
Array.isArray(this.#queryParts.select) ||
|
|
109
|
+
Boolean(this.#queryParts.selectOneField)
|
|
110
|
+
);
|
|
80
111
|
}
|
|
81
112
|
|
|
82
113
|
#getStatement() {
|
|
@@ -156,6 +187,10 @@ class FinalQuery {
|
|
|
156
187
|
return this.#queryParts;
|
|
157
188
|
}
|
|
158
189
|
|
|
190
|
+
sql() {
|
|
191
|
+
return queryToSql(this.#queryParts, false).sql;
|
|
192
|
+
}
|
|
193
|
+
|
|
159
194
|
/**
|
|
160
195
|
* @returns {R | undefined}
|
|
161
196
|
*/
|
|
@@ -173,6 +208,24 @@ class FinalQuery {
|
|
|
173
208
|
return Array.from(this.#getStatement().iterate(), mapRow);
|
|
174
209
|
}
|
|
175
210
|
|
|
211
|
+
#resultIsEntry() {
|
|
212
|
+
return (
|
|
213
|
+
Array.isArray(this.#queryParts.select) &&
|
|
214
|
+
this.#queryParts.select.length === 2
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @returns {R extends [infer K, infer V] ? Record<K,V> : never}
|
|
220
|
+
*/
|
|
221
|
+
toObject() {
|
|
222
|
+
if (!this.#resultIsEntry()) {
|
|
223
|
+
throw new Error('Select result must be a [key,value] entry');
|
|
224
|
+
}
|
|
225
|
+
const entries = /** @type {[any, any]} */ (this.all());
|
|
226
|
+
return /** @type {any} */ (Object.fromEntries(entries));
|
|
227
|
+
}
|
|
228
|
+
|
|
176
229
|
/**
|
|
177
230
|
* @returns {Generator<R>}
|
|
178
231
|
*/
|
|
@@ -206,6 +259,17 @@ class ScopedSelectQuery extends FinalQuery {
|
|
|
206
259
|
this.#queryParts = queryParts;
|
|
207
260
|
}
|
|
208
261
|
|
|
262
|
+
/**
|
|
263
|
+
* @returns {ScopedSelectQuery<T,R>}
|
|
264
|
+
*/
|
|
265
|
+
distinct() {
|
|
266
|
+
const queryParts = {
|
|
267
|
+
...this.#queryParts,
|
|
268
|
+
distinct: true,
|
|
269
|
+
};
|
|
270
|
+
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
271
|
+
}
|
|
272
|
+
|
|
209
273
|
/**
|
|
210
274
|
* @param {(obj: ObjectPick<T>, $: typeof operators) => Operator} fct
|
|
211
275
|
* @returns {ScopedSelectQuery<T,R>}
|
|
@@ -231,6 +295,19 @@ class ScopedSelectQuery extends FinalQuery {
|
|
|
231
295
|
};
|
|
232
296
|
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
233
297
|
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* @param {(obj: ObjectPick<T>, $: typeof operators) => GroupByResult} fct
|
|
301
|
+
* @returns {ScopedSelectQuery<T,R>}
|
|
302
|
+
*/
|
|
303
|
+
groupBy(fct) {
|
|
304
|
+
const scope = this.#queryParts.scope;
|
|
305
|
+
const queryParts = {
|
|
306
|
+
...this.#queryParts,
|
|
307
|
+
groupBy: fct(scope, operators),
|
|
308
|
+
};
|
|
309
|
+
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
310
|
+
}
|
|
234
311
|
}
|
|
235
312
|
|
|
236
313
|
/**
|
|
@@ -255,6 +332,17 @@ class SelectQuery extends FinalQuery {
|
|
|
255
332
|
this.#queryParts = queryParts;
|
|
256
333
|
}
|
|
257
334
|
|
|
335
|
+
/**
|
|
336
|
+
* @returns {SelectQuery<T,R>}
|
|
337
|
+
*/
|
|
338
|
+
distinct() {
|
|
339
|
+
const queryParts = {
|
|
340
|
+
...this.#queryParts,
|
|
341
|
+
distinct: true,
|
|
342
|
+
};
|
|
343
|
+
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
344
|
+
}
|
|
345
|
+
|
|
258
346
|
/**
|
|
259
347
|
* @param {(obj: RowPick<T>, $: typeof operators) => Operator} fct
|
|
260
348
|
* @returns {SelectQuery<T,R>}
|
|
@@ -271,7 +359,7 @@ class SelectQuery extends FinalQuery {
|
|
|
271
359
|
}
|
|
272
360
|
|
|
273
361
|
/**
|
|
274
|
-
* @param {(obj: RowPick<T>, $: typeof operators) =>
|
|
362
|
+
* @param {(obj: RowPick<T>, $: typeof operators) => OrderByResult} fct
|
|
275
363
|
* @returns {SelectQuery<T,R>}
|
|
276
364
|
*/
|
|
277
365
|
orderBy(fct) {
|
|
@@ -281,6 +369,18 @@ class SelectQuery extends FinalQuery {
|
|
|
281
369
|
};
|
|
282
370
|
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
283
371
|
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @param {(obj: RowPick<T>, $: typeof operators) => GroupByResult} fct
|
|
375
|
+
* @returns {SelectQuery<T,R>}
|
|
376
|
+
*/
|
|
377
|
+
groupBy(fct) {
|
|
378
|
+
const queryParts = {
|
|
379
|
+
...this.#queryParts,
|
|
380
|
+
groupBy: fct(rowPick(this.#type), operators),
|
|
381
|
+
};
|
|
382
|
+
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
383
|
+
}
|
|
284
384
|
}
|
|
285
385
|
|
|
286
386
|
/**
|
|
@@ -336,15 +436,15 @@ class ScopedFromQuery {
|
|
|
336
436
|
}
|
|
337
437
|
|
|
338
438
|
/**
|
|
339
|
-
* @template {
|
|
340
|
-
* @param {(obj: ObjectPick<T
|
|
341
|
-
* @returns {ScopedSelectQuery<T,
|
|
439
|
+
* @template {SelectResult} R
|
|
440
|
+
* @param {(obj: ObjectPick<T>, $: typeof operators) => R} fct
|
|
441
|
+
* @returns {ScopedSelectQuery<T, QueryResultOf<R>>}
|
|
342
442
|
*/
|
|
343
443
|
select(fct) {
|
|
344
444
|
const scope = this.#queryParts.scope;
|
|
345
445
|
const queryParts = {
|
|
346
446
|
...this.#queryParts,
|
|
347
|
-
select: fct(scope),
|
|
447
|
+
select: fct(scope, operators),
|
|
348
448
|
};
|
|
349
449
|
return new ScopedSelectQuery(this.#database, this.#type, queryParts);
|
|
350
450
|
}
|
|
@@ -414,14 +514,14 @@ class FromQuery {
|
|
|
414
514
|
}
|
|
415
515
|
|
|
416
516
|
/**
|
|
417
|
-
* @template {
|
|
418
|
-
* @param {(obj: RowPick<T
|
|
419
|
-
* @returns {SelectQuery<T,
|
|
517
|
+
* @template {SelectResult} R
|
|
518
|
+
* @param {(obj: RowPick<T>, $: typeof operators) => R} fct
|
|
519
|
+
* @returns {SelectQuery<T, QueryResultOf<R>>}
|
|
420
520
|
*/
|
|
421
521
|
select(fct) {
|
|
422
522
|
const queryParts = {
|
|
423
523
|
...this.#queryParts,
|
|
424
|
-
select: fct(rowPick(this.#type)),
|
|
524
|
+
select: fct(rowPick(this.#type), operators),
|
|
425
525
|
};
|
|
426
526
|
return new SelectQuery(this.#database, this.#type, queryParts);
|
|
427
527
|
}
|
package/lib/query-to-sql.js
CHANGED
|
@@ -7,10 +7,13 @@ const {sql} = require('./operator-to-sql.js');
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @param {QueryObj["select"]} select
|
|
10
|
-
* @param {any[]} values
|
|
10
|
+
* @param {any[] | null} values
|
|
11
11
|
* @returns {string}
|
|
12
12
|
*/
|
|
13
13
|
function selectFields(select, values) {
|
|
14
|
+
if (Array.isArray(select)) {
|
|
15
|
+
return select.map((value) => sql(value, values)).join(', ');
|
|
16
|
+
}
|
|
14
17
|
return Object.entries(select)
|
|
15
18
|
.map(([name, value]) => `${sql(value, values)} AS ${name}`)
|
|
16
19
|
.join(', ');
|
|
@@ -18,7 +21,7 @@ function selectFields(select, values) {
|
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* @param {NonNullable<QueryObj["orderBy"]>} orderBy
|
|
21
|
-
* @param {any[]} values
|
|
24
|
+
* @param {any[] | null} values
|
|
22
25
|
* @returns {string}
|
|
23
26
|
*/
|
|
24
27
|
function orderByFields(orderBy, values) {
|
|
@@ -28,13 +31,27 @@ function orderByFields(orderBy, values) {
|
|
|
28
31
|
return sql(orderBy, values);
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @param {NonNullable<QueryObj["groupBy"]>} groupBy
|
|
36
|
+
* @param {any[] | null} values
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function groupByFields(groupBy, values) {
|
|
40
|
+
if (Array.isArray(groupBy)) {
|
|
41
|
+
return groupBy.map((value) => sql(value, values)).join(', ');
|
|
42
|
+
}
|
|
43
|
+
return sql(groupBy, values);
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
/**
|
|
32
47
|
* @param {QueryObj} query
|
|
33
|
-
* @
|
|
48
|
+
* @param {boolean} [useBindedValues=true]
|
|
49
|
+
* @returns {{sql: string, values: any[] | null}}
|
|
34
50
|
*/
|
|
35
|
-
function queryToSql(query) {
|
|
36
|
-
const values = [];
|
|
37
|
-
|
|
51
|
+
function queryToSql(query, useBindedValues = true) {
|
|
52
|
+
const values = useBindedValues ? [] : null;
|
|
53
|
+
const distinct = query.distinct ? 'DISTINCT ' : '';
|
|
54
|
+
let result = `SELECT ${distinct}${selectFields(query.select, values)}`;
|
|
38
55
|
result += '\n' + `FROM ${query.from}`;
|
|
39
56
|
if (query.where) {
|
|
40
57
|
result += '\n' + `WHERE ${sql(query.where, values)}`;
|
|
@@ -42,6 +59,9 @@ function queryToSql(query) {
|
|
|
42
59
|
if (query.orderBy) {
|
|
43
60
|
result += '\n' + `ORDER BY ${orderByFields(query.orderBy, values)}`;
|
|
44
61
|
}
|
|
62
|
+
if (query.groupBy) {
|
|
63
|
+
result += '\n' + `GROUP BY ${groupByFields(query.groupBy, values)}`;
|
|
64
|
+
}
|
|
45
65
|
return {sql: result, values};
|
|
46
66
|
}
|
|
47
67
|
|