sql-love 1.0.5 → 1.1.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/index.mjs CHANGED
@@ -1,31 +1,15 @@
1
1
  // src/Parser/Parser.ts
2
- var Parser = class {
2
+ class Parser {
3
3
  query;
4
- /**
5
- * Create a new instance
6
- * @param query A Select object on which to build parsed conditions
7
- */
8
4
  constructor(query) {
9
5
  this.query = query;
10
6
  }
11
- /**
12
- * Strip single-line and multi-line comment blocks
13
- * @param sql The SQL string
14
- * @return The SQL string without comments
15
- * @private
16
- */
17
7
  _stripComments(sql) {
18
8
  sql = sql.replace(/\/\*[\s\S]*?\*\//g, "");
19
9
  sql = sql.replace(/--([\r\n]|\s+[^\r\n]+[\r\n])/g, "");
20
10
  sql = sql.replace(/#[^\r\n]+[\r\n]/g, "");
21
11
  return sql;
22
12
  }
23
- /**
24
- * Before splitting into SQL clauses, extract some regex-able subQueries
25
- * @param sql The unparsed sql string
26
- * @return An array with new sql and subQueries
27
- * @private
28
- */
29
13
  _extractSubQueries(sql) {
30
14
  const subQueries = {};
31
15
  let i = 0;
@@ -39,11 +23,6 @@ var Parser = class {
39
23
  sql = sql.replace(/\bIN\s*\(\s*SELECT\s.+?\)/, extractor);
40
24
  return { sql, subQueries };
41
25
  }
42
- /**
43
- * Inject column subQueries back into this object
44
- * @param subQueries The lookup of extracted subQueries
45
- * @private
46
- */
47
26
  _injectSubQueries(subQueries) {
48
27
  const replacer = ($0) => {
49
28
  return subQueries[$0] || $0;
@@ -56,21 +35,10 @@ var Parser = class {
56
35
  this.query._wheres = this.query._wheres.map(mapper);
57
36
  this.query._havings = this.query._havings.map(mapper);
58
37
  }
59
- /**
60
- * Split SQL into clauses (used by parse())
61
- * @param sql The SQL to split
62
- * @return
63
- * @private
64
- */
65
38
  _split(sql) {
66
39
  const splitter = /(?:^|\s)(SELECT|FROM|(?:INNER|LEFT\s+OUTER|RIGHT\s+OUTER|LEFT|RIGHT|CROSS|FULL|FULL\s+OUTER)\s+JOIN|WHERE|GROUP\s+BY|HAVING|ORDER\s+BY|LIMIT|OFFSET|FETCH\s+NEXT)\b/i;
67
40
  return sql.split(splitter);
68
41
  }
69
- /**
70
- * Get a QuickSelect object representing the given SQL SELECT statement
71
- * @param rawSql The raw SQL for the SELECT statement
72
- * @return True if the SQL was parsed successfully
73
- */
74
42
  parse(rawSql) {
75
43
  const stripped = this._stripComments(rawSql);
76
44
  const { sql, subQueries } = this._extractSubQueries(stripped);
@@ -86,11 +54,6 @@ var Parser = class {
86
54
  this._injectSubQueries(subQueries);
87
55
  return true;
88
56
  }
89
- /**
90
- * Handle SQL_CALC_FOUND_ROWS and column names
91
- * @param clause The clause after the SELECT
92
- * @private
93
- */
94
57
  _handleSELECT(clause) {
95
58
  let columns = clause.split(/s*,s*/);
96
59
  let buffer = "";
@@ -118,84 +81,34 @@ var Parser = class {
118
81
  }
119
82
  });
120
83
  }
121
- /**
122
- * Handle table names
123
- * @param clause The clause after the FROM
124
- * @private
125
- */
126
84
  _handleFROM(clause) {
127
85
  const tables = clause.split(/\s*,\s*/);
128
86
  tables.forEach((table) => this.query.table(table));
129
87
  }
130
- /**
131
- * Handle INNER JOIN statements
132
- * @param clause The clause after the INNER JOIN
133
- * @private
134
- */
135
88
  _handleINNER_JOIN(clause) {
136
89
  this.query.innerJoin(clause);
137
90
  }
138
- /**
139
- * Handle LEFT JOIN statements
140
- * @param clause The clause after the LEFT JOIN
141
- * @private
142
- */
143
91
  _handleLEFT_JOIN(clause) {
144
92
  this.query.leftJoin(clause);
145
93
  }
146
- /**
147
- * Handle LEFT OUTER JOIN statements
148
- * @param clause The clause after the LEFT OUTER JOIN
149
- * @private
150
- */
151
94
  _handleLEFT_OUTER_JOIN(clause) {
152
95
  this.query.leftOuterJoin(clause);
153
96
  }
154
- /**
155
- * Handle RIGHT JOIN statements
156
- * @param clause The clause after the RIGHT JOIN
157
- * @private
158
- */
159
97
  _handleRIGHT_JOIN(clause) {
160
98
  this.query.rightJoin(clause);
161
99
  }
162
- /**
163
- * Handle RIGHT OUTER JOIN statements
164
- * @param clause The clause after the RIGHT OUTER JOIN
165
- * @private
166
- */
167
100
  _handleRIGHT_OUTER_JOIN(clause) {
168
101
  this.query.rightOuterJoin(clause);
169
102
  }
170
- /**
171
- * Handle CROSS JOIN statements
172
- * @param clause The clause after the CROSS JOIN
173
- * @private
174
- */
175
103
  _handleCROSS_JOIN(clause) {
176
104
  this.query.crossJoin(clause);
177
105
  }
178
- /**
179
- * Handle FULL JOIN statements
180
- * @param clause The clause after the FULL JOIN
181
- * @private
182
- */
183
106
  _handleFULL_JOIN(clause) {
184
107
  this.query.fullJoin(clause);
185
108
  }
186
- /**
187
- * Handle FULL OUTER JOIN statements
188
- * @param clause The clause after the FULL OUTER JOIN
189
- * @private
190
- */
191
109
  _handleFULL_OUTER_JOIN(clause) {
192
110
  this.query.fullOuterJoin(clause);
193
111
  }
194
- /**
195
- * Handle WHERE conditions
196
- * @param clause All the conditions after WHERE
197
- * @private
198
- */
199
112
  _handleWHERE(clause) {
200
113
  if (/^(1|'1'|true)$/i.test(clause)) {
201
114
  this.query._wheres.push(clause);
@@ -203,20 +116,9 @@ var Parser = class {
203
116
  this._handleConditions("where", clause);
204
117
  }
205
118
  }
206
- /**
207
- * Handle HAVING statements
208
- * @param clause All the conditions after HAVING
209
- * @private
210
- */
211
119
  _handleHAVING(clause) {
212
120
  this._handleConditions("having", clause);
213
121
  }
214
- /**
215
- * Build a conditions list
216
- * @param type Either where or having
217
- * @param clause The expressions following the type keyword
218
- * @private
219
- */
220
122
  _handleConditions(type, clause) {
221
123
  const andGroups = clause.split(/\bAND\b/i);
222
124
  andGroups.forEach((andGroup) => {
@@ -230,29 +132,14 @@ var Parser = class {
230
132
  }
231
133
  });
232
134
  }
233
- /**
234
- * Handle GROUP BY statements
235
- * @param clause The clauses after the GROUP BY
236
- * @private
237
- */
238
135
  _handleGROUP_BY(clause) {
239
136
  const columns = clause.split(/\s*,\s*/);
240
137
  columns.forEach((column) => this.query.groupBy(column));
241
138
  }
242
- /**
243
- * Handle ORDER BY statements
244
- * @param clause The clause after the ORDER BY
245
- * @private
246
- */
247
139
  _handleORDER_BY(clause) {
248
140
  const columns = clause.split(/\s*,\s*/);
249
141
  columns.forEach((column) => this.query.orderBy(column));
250
142
  }
251
- /**
252
- * Handle LIMIT statements including "LIMIT #" and "LIMIT #, #"
253
- * @param clause The clause after the LIMIT
254
- * @private
255
- */
256
143
  _handleLIMIT(clause) {
257
144
  const offsetLimit = clause.match(/^(\d+|\?|:\w+)\s*,\s*(\d+|\?|:\w+)$/);
258
145
  if (offsetLimit) {
@@ -263,26 +150,15 @@ var Parser = class {
263
150
  this.query.limit(clause);
264
151
  }
265
152
  }
266
- /**
267
- * Handle OFFSET statements
268
- * @param clause The number after the OFFSET
269
- * @private
270
- */
271
153
  _handleOFFSET(clause) {
272
154
  clause = clause.replace(/^(\d+) ROWS$/i, "$1");
273
155
  this.query.offset(clause);
274
156
  }
275
- /**
276
- * Handle OFFSET statements
277
- * @param clause The number after the OFFSET
278
- * @private
279
- */
280
157
  _handleFETCH_NEXT(clause) {
281
158
  clause = clause.replace(/^(\d+) ROWS ONLY$/i, "$1");
282
159
  this.query.limit(clause);
283
160
  }
284
- };
285
-
161
+ }
286
162
  // src/substrCount/substrCount.ts
287
163
  function substrCount(haystack, needle) {
288
164
  let count = 0;
@@ -295,75 +171,22 @@ function substrCount(haystack, needle) {
295
171
  }
296
172
 
297
173
  // src/SelectBuilder/SelectBuilder.ts
298
- var isNullish = (v) => v === void 0 || v === null || v === false || isNaN(v);
174
+ var isNullish = (v) => v === undefined || v === null || v === false || isNaN(v);
299
175
  var defaultEngine = "mysql";
300
- var SelectBuilder = class _SelectBuilder {
301
- /**
302
- * The list of values to bind to the query
303
- * @private
304
- */
176
+
177
+ class SelectBuilder {
305
178
  _bindings = [];
306
- /**
307
- * The list of strings to come immediately after "SELECT"
308
- * and before column names
309
- * @private
310
- */
311
179
  _options = [];
312
- /**
313
- * The list of column names to select
314
- * @private
315
- */
316
180
  _tables = [];
317
- /**
318
- * The list of tables in the FROM clause
319
- * @private
320
- */
321
181
  _columns = [];
322
- /**
323
- * The list of JOIN strings to add
324
- * @private
325
- */
326
182
  _joins = [];
327
- /**
328
- * The list of WHERE clauses to join with AND
329
- * @private
330
- */
331
183
  _wheres = [];
332
- /**
333
- * The list of GROUP BY clauses to join with commas
334
- * @private
335
- */
336
184
  _groupBys = [];
337
- /**
338
- * The list of HAVING clauses to join with AND
339
- * @private
340
- */
341
185
  _havings = [];
342
- /**
343
- * The list of ORDER BY clauses to join with commas
344
- * @private
345
- */
346
186
  _orderBys = [];
347
- /**
348
- * The LIMIT to use
349
- * @private
350
- */
351
187
  _limit;
352
- /**
353
- * The OFFSET to use
354
- * @private
355
- */
356
188
  _offset;
357
- /**
358
- * The page used to construct an OFFSET based on the LIMIT
359
- * @private
360
- */
361
189
  _page;
362
- /**
363
- * Select constructor
364
- * @param [sql] A base SQL query to parse
365
- * @param [bindings] Placeholders to bind to the query
366
- */
367
190
  constructor(sql = null, bindings = null) {
368
191
  if (sql) {
369
192
  if (bindings) {
@@ -377,33 +200,32 @@ var SelectBuilder = class _SelectBuilder {
377
200
  new Parser(this).parse(sql);
378
201
  }
379
202
  }
380
- /**
381
- * Set the default database engine style (mssql, mysql, oracle, pg)
382
- * @param engine
383
- */
384
203
  static setDefaultEngine(engine) {
385
204
  defaultEngine = engine;
386
- return _SelectBuilder;
205
+ return SelectBuilder;
387
206
  }
388
- /**
389
- * Get the SQL as a pretty-printed string alongside an array of bindings
390
- * @param engine The database engine style (mssql, mysql, oracle, pg)
391
- * @return The SQL query as a string
392
- */
393
207
  compile({
394
208
  engine = defaultEngine,
395
209
  bindingIndex = 1
396
210
  } = {}) {
397
211
  const lines = [
398
212
  "SELECT",
399
- this._options.length ? ` ${this._options.join("\n ")}` : null,
400
- this._columns.length ? ` ${this._columns.join(",\n ")}` : " *\n",
213
+ this._options.length ? ` ${this._options.join(`
214
+ `)}` : null,
215
+ this._columns.length ? ` ${this._columns.join(`,
216
+ `)}` : ` *
217
+ `,
401
218
  `FROM ${this._tables.join(", ")}`,
402
- this._joins.length ? this._joins.join("\n") : null,
403
- this._wheres.length ? `WHERE ${this._wheres.join("\n AND ")}` : null,
404
- this._groupBys.length ? `GROUP BY ${this._groupBys.join(",\n ")}` : null,
405
- this._havings.length ? `HAVING ${this._havings.join("\n AND ")}` : null,
406
- this._orderBys.length ? `ORDER BY ${this._orderBys.join(",\n ")}` : null
219
+ this._joins.length ? this._joins.join(`
220
+ `) : null,
221
+ this._wheres.length ? `WHERE ${this._wheres.join(`
222
+ AND `)}` : null,
223
+ this._groupBys.length ? `GROUP BY ${this._groupBys.join(`,
224
+ `)}` : null,
225
+ this._havings.length ? `HAVING ${this._havings.join(`
226
+ AND `)}` : null,
227
+ this._orderBys.length ? `ORDER BY ${this._orderBys.join(`,
228
+ `)}` : null
407
229
  ];
408
230
  if (this._page > 0) {
409
231
  const offset = (this._page - 1) * this._limit;
@@ -417,7 +239,8 @@ var SelectBuilder = class _SelectBuilder {
417
239
  lines.push(this._compileLIMIT(engine, this._limit));
418
240
  }
419
241
  }
420
- const pretty = lines.filter(Boolean).join("\n").trim();
242
+ const pretty = lines.filter(Boolean).join(`
243
+ `).trim();
421
244
  let replacer;
422
245
  const bindings = [];
423
246
  if (engine === "pg") {
@@ -449,11 +272,6 @@ var SelectBuilder = class _SelectBuilder {
449
272
  return `OFFSET ${offset} ROWS`;
450
273
  }
451
274
  }
452
- /**
453
- * Get SQL needed to return the found rows of this query
454
- * @param engineStyle The database engine style (mssql, mysql, oracle, pg)
455
- * @param countExpr The expression to use inside the COUNT()
456
- */
457
275
  compileCount({
458
276
  countExpr = "*",
459
277
  engine = defaultEngine,
@@ -464,7 +282,8 @@ var SelectBuilder = class _SelectBuilder {
464
282
  if (this._havings.length === 0) {
465
283
  return { sql, bindings };
466
284
  } else {
467
- const subquerySql = sql.replace(/\n/g, "\n ");
285
+ const subquerySql = sql.replace(/\n/g, `
286
+ `);
468
287
  return {
469
288
  sql: `SELECT COUNT(*) AS found_rows
470
289
  FROM (
@@ -474,11 +293,6 @@ ${subquerySql}
474
293
  };
475
294
  }
476
295
  }
477
- /**
478
- * Get SQL needed to union this query with another
479
- * @param query The query to union
480
- * @param engine The database engine style (mssql, mysql, oracle, pg)
481
- */
482
296
  compileUnion(query, {
483
297
  engine = defaultEngine,
484
298
  bindingIndex = 1
@@ -495,11 +309,6 @@ UNION
495
309
  bindings: [...first.bindings, ...second.bindings]
496
310
  };
497
311
  }
498
- /**
499
- * @param [fieldOrFields] If given, reset the given component(s), otherwise reset all query components
500
- * Valid components: option, column, table, where, orWhere, having, groupBy, orderBy, limit, offset, page
501
- * @return This object
502
- */
503
312
  reset(fieldOrFields = null) {
504
313
  if (Array.isArray(fieldOrFields)) {
505
314
  fieldOrFields.forEach((name) => this.reset(name));
@@ -537,11 +346,8 @@ UNION
537
346
  }
538
347
  return this;
539
348
  }
540
- /**
541
- * Clone this builder object
542
- */
543
349
  getClone() {
544
- const copy = new _SelectBuilder();
350
+ const copy = new SelectBuilder;
545
351
  copy._options = [...this._options];
546
352
  copy._columns = [...this._columns];
547
353
  copy._tables = [...this._tables];
@@ -556,10 +362,6 @@ UNION
556
362
  copy._bindings = [...this._bindings];
557
363
  return copy;
558
364
  }
559
- /**
560
- * Build a version of this query that simply returns COUNT(*)
561
- * @param [countExpr="*"] Use to specify `DISTINCT colname` if needed
562
- */
563
365
  getFoundRowsQuery(countExpr = "*") {
564
366
  if (this._havings.length === 0) {
565
367
  const clone = this.getClone();
@@ -579,74 +381,34 @@ UNION
579
381
  return subquery;
580
382
  }
581
383
  }
582
- /**
583
- * Add an array of column names to fetch
584
- * @param columnNames The names of columns
585
- */
586
384
  select(columnNames) {
587
385
  this._columns = this._columns.concat(columnNames);
588
386
  return this;
589
387
  }
590
- /**
591
- * Add an array of column names to fetch
592
- * @param columnNames The names of columns
593
- */
594
388
  columns(columnNames) {
595
389
  this._columns = this._columns.concat(columnNames);
596
390
  return this;
597
391
  }
598
- /**
599
- * Add a column name to fetch
600
- * @param columnName The name of the column
601
- */
602
392
  column(columnName) {
603
393
  this._columns.push(columnName);
604
394
  return this;
605
395
  }
606
- /**
607
- * Add an option expression such as "TOP 10" or "SQL_CALC_FOUND_ROWS"
608
- * @param optionExpression Expression to go after "SELECT" and before column list
609
- */
610
396
  option(optionExpression) {
611
397
  this._options.push(optionExpression);
612
398
  return this;
613
399
  }
614
- /**
615
- * Add a table to the "FROM" clause (same as .from())
616
- * @param {String} tableName The name of the table to query
617
- * @return {Select}
618
- */
619
400
  table(tableName) {
620
401
  this._tables.push(tableName);
621
402
  return this;
622
403
  }
623
- /**
624
- * Add multiple table to the "FROM" clause
625
- * @param {Array} tableNames The names of the tables to query
626
- * @return {Select}
627
- */
628
404
  tables(tableNames) {
629
405
  this._tables.push(...tableNames);
630
406
  return this;
631
407
  }
632
- /**
633
- * Add a table to the "FROM" clause (same as .table())
634
- * @param {String} tableName The name of the table to query
635
- * @return {Select}
636
- */
637
408
  from(tableName) {
638
409
  this._tables.push(tableName);
639
410
  return this;
640
411
  }
641
- /**
642
- * Add the given join type expression
643
- * @param joinType The type of join (e.g. "INNER JOIN", "LEFT JOIN", etc.)
644
- * @param expression The expression following the INNER JOIN keyword
645
- * @param bindings A list of values to bind to ? placeholders in the expression
646
- * @example
647
- * query.join('posts p ON p.id = c.post_id');
648
- * query.join('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
649
- */
650
412
  _addJoin(joinType, expression, bindings = []) {
651
413
  if (bindings.length > 0) {
652
414
  expression = expression.replace(/\?/g, () => {
@@ -656,118 +418,42 @@ UNION
656
418
  }
657
419
  this._joins.push(`${joinType} ${expression}`);
658
420
  }
659
- /**
660
- * Add a JOIN expression
661
- * @param expression The expression following the INNER JOIN keyword
662
- * @param bindings A list of values to bind to ? placeholders in the expression
663
- * @example
664
- * query.join('posts p ON p.id = c.post_id');
665
- * query.join('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
666
- */
667
421
  join(expression, bindings = []) {
668
422
  this._addJoin("JOIN", expression, bindings);
669
423
  return this;
670
424
  }
671
- /**
672
- * Add a LEFT JOIN expression
673
- * @param expression The expression following the LEFT JOIN keyword
674
- * @param bindings A list of values to bind to ? placeholders in the expression
675
- * @example
676
- * query.leftJoin('posts p ON p.id = c.post_id');
677
- * query.leftJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
678
- */
679
425
  leftJoin(expression, bindings = []) {
680
426
  this._addJoin("LEFT JOIN", expression, bindings);
681
427
  return this;
682
428
  }
683
- /**
684
- * Add a FULL JOIN expression
685
- * @param expression The expression following the FULL JOIN keyword
686
- * @param bindings A list of values to bind to ? placeholders in the expression
687
- * @example
688
- * query.fullJoin('posts p ON p.id = c.post_id');
689
- * query.fullJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
690
- */
691
429
  fullJoin(expression, bindings = []) {
692
430
  this._addJoin("FULL JOIN", expression, bindings);
693
431
  return this;
694
432
  }
695
- /**
696
- * Add a RIGHT JOIN expression
697
- * @param expression The expression following the RIGHT JOIN keyword
698
- * @param bindings A list of values to bind to ? placeholders in the expression
699
- * @example
700
- * query.rightJoin('posts p ON p.id = c.post_id');
701
- * query.rightJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
702
- */
703
433
  rightJoin(expression, bindings = []) {
704
434
  this._addJoin("RIGHT JOIN", expression, bindings);
705
435
  return this;
706
436
  }
707
- /**
708
- * Add a CROSS JOIN expression
709
- * @param expression The expression following the CROSS JOIN keyword
710
- * @param bindings A list of values to bind to ? placeholders in the expression
711
- * @example
712
- * query.crossJoin('posts p ON p.id = c.post_id');
713
- * query.crossJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
714
- */
715
437
  crossJoin(expression, bindings = []) {
716
438
  this._addJoin("CROSS JOIN", expression, bindings);
717
439
  return this;
718
440
  }
719
- /**
720
- * Add an INNER JOIN expression (same as ->join())
721
- * @param expression The expression following the INNER JOIN keyword
722
- * @param bindings A list of values to bind to ? placeholders in the expression
723
- * @example
724
- * query.innerJoin('posts p ON p.id = c.post_id');
725
- * query.innerJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
726
- */
727
441
  innerJoin(expression, bindings = []) {
728
442
  this._addJoin("INNER JOIN", expression, bindings);
729
443
  return this;
730
444
  }
731
- /**
732
- * Add a LEFT OUTER JOIN expression
733
- * @param expression The expression following the LEFT OUTER JOIN keyword
734
- * @param bindings A list of values to bind to ? placeholders in the expression
735
- * @example
736
- * query.leftOuterJoin('posts p ON p.id = c.post_id');
737
- * query.leftOuterJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
738
- */
739
445
  leftOuterJoin(expression, bindings = []) {
740
446
  this._addJoin("LEFT OUTER JOIN", expression, bindings);
741
447
  return this;
742
448
  }
743
- /**
744
- * Add a FULL OUTER JOIN expression
745
- * @param expression The expression following the FULL OUTER JOIN keyword
746
- * @param bindings A list of values to bind to ? placeholders in the expression
747
- * @example
748
- * query.fullOuterJoin('posts p ON p.id = c.post_id');
749
- * query.fullOuterJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
750
- */
751
449
  fullOuterJoin(expression, bindings = []) {
752
450
  this._addJoin("FULL OUTER JOIN", expression, bindings);
753
451
  return this;
754
452
  }
755
- /**
756
- * Add a RIGHT OUTER JOIN expression
757
- * @param expression The expression following the RIGHT OUTER JOIN keyword
758
- * @param bindings A list of values to bind to ? placeholders in the expression
759
- * @example
760
- * query.rightOuterJoin('posts p ON p.id = c.post_id');
761
- * query.rightOuterJoin('posts p ON p.id = c.post_id AND p.deleted_at < ?', [myDate]);
762
- */
763
453
  rightOuterJoin(expression, bindings = []) {
764
454
  this._addJoin("RIGHT OUTER JOIN", expression, bindings);
765
455
  return this;
766
456
  }
767
- /**
768
- * Remove a join condition with the specified table
769
- * @param table The name of the table or tables in the first part of the join statement
770
- */
771
457
  unjoin(table) {
772
458
  if (Array.isArray(table)) {
773
459
  table.forEach((t) => this.unjoin(t));
@@ -779,34 +465,6 @@ UNION
779
465
  });
780
466
  return this;
781
467
  }
782
- /**
783
- * Utility function to add conditions for a clause (WHERE, HAVING)
784
- * @param collection The collection to add the clauses to (e.g. this._wheres or this._havings)
785
- * @param criteria Criteria adhering to WhereCriteria type
786
- * @example The following are equivalent
787
- * this._conditions(this._wheres, ['deleted_at IS NULL']);
788
- * this._conditions(this._wheres, ['deleted_at', null]);
789
- * this._conditions(this._wheres, ['deleted_at', '=', null]);
790
- * @example More examples
791
- * this._conditions(this._wheres, ['fname', 'LIKE', 'joe']); // `fname` LIKE 'joe'
792
- * this._conditions(this._wheres, ['fname', 'LIKE ?', 'joe']); // `fname` LIKE 'joe'
793
- * this._conditions(this._wheres, ['fname LIKE %?%', 'joe']); // `fname` LIKE '%joe%'
794
- * this._conditions(this._wheres, ['fname LIKE ?%', 'joe']); // `fname` LIKE 'joe%'
795
- * this._conditions(this._wheres, ['fname', 'LIKE ?%', 'joe']); // `fname` LIKE 'joe%'
796
- * this._conditions(this._wheres, ['price >', 10]); // `price` > 10
797
- * this._conditions(this._wheres, ['price', '>', 10]); // `price` > 10
798
- * this._conditions(this._wheres, ['price =', 10]); // `price` = 10
799
- * this._conditions(this._wheres, ['price !=', 10]); // `price` != 10
800
- * this._conditions(this._wheres, ['price', 10]); // `price` = 10
801
- * this._conditions(this._wheres, ['price', '=', 10]); // `price` = 10
802
- * this._conditions(this._wheres, ['price', '!=', 10]); // `price` != 10
803
- * this._conditions(this._wheres, ['price', 'BETWEEN', [10,20]]); // `price` BETWEEN 10 AND 20
804
- * this._conditions(this._wheres, ['price', 'NOT BETWEEN', [10,20]]); // `price` NOT BETWEEN 10 AND 20
805
- * this._conditions(this._wheres, ['price', [10,20]]); // `price` IN(10,20)
806
- * this._conditions(this._wheres, ['price', '=', [10,20]]); // `price` IN(10,20)
807
- * this._conditions(this._wheres, ['price', 'IN', [10,20]]); // `price` IN(10,20)
808
- * this._conditions(this._wheres, ['price', 'NOT IN', [10,20]]); // `price` NOT IN(10,20)
809
- */
810
468
  _conditions(collection, criteria) {
811
469
  const numArgs = criteria.length;
812
470
  let [column, operator, value] = criteria;
@@ -837,15 +495,11 @@ UNION
837
495
  operator = "=";
838
496
  }
839
497
  operator = operator.toLocaleUpperCase();
840
- const likeMatch = operator.match(
841
- /^(LIKE|NOT LIKE|ILIKE|NOT ILIKE)(?: (\?|\?%|%\?|%\?%))?$/i
842
- );
498
+ const likeMatch = operator.match(/^(LIKE|NOT LIKE|ILIKE|NOT ILIKE)(?: (\?|\?%|%\?|%\?%))?$/i);
843
499
  if (operator === "NOT BETWEEN" || operator === "BETWEEN") {
844
500
  const one = this._bindings.length;
845
501
  const two = this._bindings.length + 1;
846
- collection.push(
847
- `${column} ${operator} __BOUND_${one}__ AND __BOUND_${two}__`
848
- );
502
+ collection.push(`${column} ${operator} __BOUND_${one}__ AND __BOUND_${two}__`);
849
503
  this._bindings.push(value[0], value[1]);
850
504
  } else if (likeMatch) {
851
505
  const like = likeMatch[1].toUpperCase();
@@ -863,52 +517,32 @@ UNION
863
517
  this._bindings.push(infix.replace("?", value || ""));
864
518
  }
865
519
  } else if (value === null) {
866
- collection.push(
867
- operator === "=" ? `${column} IS NULL` : `${column} IS NOT NULL`
868
- );
520
+ collection.push(operator === "=" ? `${column} IS NULL` : `${column} IS NOT NULL`);
869
521
  } else if (Array.isArray(value)) {
870
522
  const inVals = [];
871
- for (let i = 0; i < value.length; i++) {
523
+ for (let i = 0;i < value.length; i++) {
872
524
  inVals.push(`__BOUND_${this._bindings.length}__`);
873
525
  this._bindings.push(value[i]);
874
526
  }
875
527
  const joined = inVals.join(", ");
876
- collection.push(
877
- operator === "=" || operator === "IN" ? `${column} IN(${joined})` : `${column} NOT IN(${joined})`
878
- );
528
+ collection.push(operator === "=" || operator === "IN" ? `${column} IN(${joined})` : `${column} NOT IN(${joined})`);
879
529
  } else if (operator === "IN" || operator === "NOT IN") {
880
- collection.push(
881
- `${column} ${operator}(__BOUND_${this._bindings.length}__)`
882
- );
530
+ collection.push(`${column} ${operator}(__BOUND_${this._bindings.length}__)`);
883
531
  this._bindings.push(value);
884
532
  } else {
885
- collection.push(
886
- `${column} ${operator} __BOUND_${this._bindings.length}__`
887
- );
533
+ collection.push(`${column} ${operator} __BOUND_${this._bindings.length}__`);
888
534
  this._bindings.push(value);
889
535
  }
890
536
  return this;
891
537
  }
892
- /**
893
- * Add a group by column or expression
894
- * @param column The name of a column (or expression) to group by
895
- */
896
538
  groupBy(column) {
897
539
  this._groupBys.push(column);
898
540
  return this;
899
541
  }
900
- /**
901
- * Add WHERE clauses to conditions (See _conditions for usage)
902
- */
903
542
  where(...args) {
904
543
  this._conditions(this._wheres, args);
905
544
  return this;
906
545
  }
907
- /**
908
- * Add a WHERE clause with a BETWEEN condition
909
- * @param column The column name
910
- * @param twoValueArray The two values to be between
911
- */
912
546
  whereBetween(column, twoValueArray) {
913
547
  if (!isNullish(twoValueArray[0]) && !isNullish(twoValueArray[1])) {
914
548
  this.where(column, "BETWEEN", twoValueArray);
@@ -916,14 +550,9 @@ UNION
916
550
  this.where(column, ">=", twoValueArray[0]);
917
551
  } else if (isNullish(twoValueArray[0]) && !isNullish(twoValueArray[1])) {
918
552
  this.where(column, "<=", twoValueArray[1]);
919
- } else {
920
- }
553
+ } else {}
921
554
  return this;
922
555
  }
923
- /**
924
- * Add a series of WHERE clauses joined by OR (See _conditions for usage)
925
- * @param conditions
926
- */
927
556
  _orConditions(conditions) {
928
557
  const items = [];
929
558
  if (Array.isArray(conditions)) {
@@ -948,41 +577,20 @@ UNION
948
577
  return joined;
949
578
  }
950
579
  }
951
- /**
952
- * Add WHERE conditions to place inside an OR block (See _conditions for usage)
953
- * @param conditions A list or object where each item is an array with parameters that would be taken by where()
954
- */
955
580
  orWhere(conditions) {
956
581
  const joined = this._orConditions(conditions);
957
582
  this.where(`(${joined})`);
958
583
  return this;
959
584
  }
960
- /**
961
- * Add an OR with conditions under the HAVING clause
962
- * @param conditions A list or object where each item is an array with parameters that would be taken by having()
963
- */
964
585
  orHaving(conditions) {
965
586
  const joined = this._orConditions(conditions);
966
587
  this.having(`(${joined})`);
967
588
  return this;
968
589
  }
969
- /**
970
- * Add a HAVING condition (See _conditions for usage)
971
- */
972
590
  having(...args) {
973
591
  this._conditions(this._havings, args);
974
592
  return this;
975
593
  }
976
- /**
977
- * Add a column or expression to order by
978
- * @param column The column name or expression to sort by. Include DESC or prefix with - to sort descending
979
- * @param [direction] The direction to sort by (ASC or DESC)
980
- * @example
981
- * query.sortField('modified_at'); // ORDER BY modified_at
982
- * query.sortField('-modified_at'); // ORDER BY modified_at DESC
983
- * query.sortField('COUNT(*)'); // ORDER BY COUNT(*)
984
- * query.sortField('COUNT(*) DESC'); // ORDER BY COUNT(*) DESC
985
- */
986
594
  orderBy(column, direction = null) {
987
595
  if (direction) {
988
596
  this._orderBys.push(`${column} ${direction}`);
@@ -992,44 +600,33 @@ UNION
992
600
  }
993
601
  return this;
994
602
  }
995
- /**
996
- * Sort by the given column, with a map of columns to translate
997
- * @param fieldName The file name such as "created_at" or "-created_at" for descending
998
- * @param [mapNames] Column names to translate from field to column expression
999
- * @example
1000
- * query.sortField('-modified_at'); // ORDER BY modified_at DESC
1001
- * query.sortField('-created', {'created': 'created_at'}); // ORDER BY created_at DESC
1002
- */
1003
- sortField(fieldName, mapNames = {}) {
603
+ sortField(fieldName, mapNames = {}, modifier = "") {
604
+ if (!fieldName) {
605
+ return this;
606
+ }
1004
607
  let direction = "ASC";
1005
608
  if (fieldName.startsWith("-")) {
609
+ if (mapNames[fieldName]) {
610
+ const maybeModifier = modifier ? ` ${modifier}` : "";
611
+ this.orderBy(mapNames[fieldName] + maybeModifier);
612
+ return this;
613
+ }
1006
614
  direction = "DESC";
1007
615
  fieldName = fieldName.slice(1);
1008
616
  }
1009
617
  const column = mapNames[fieldName] || fieldName;
618
+ if (modifier) {
619
+ direction += ` ${modifier}`;
620
+ }
1010
621
  this.orderBy(column, direction);
1011
622
  return this;
1012
623
  }
1013
- /**
1014
- * Check to see if the given string is 0 or all digits not starting with 0
1015
- * @param string The input string
1016
- * @private
1017
- */
1018
624
  _isEntirelyDigits(string) {
1019
625
  return /^(0|[1-9]\d*)$/.test(string);
1020
626
  }
1021
- /**
1022
- * Check to see if the given string is all digits
1023
- * @param string The input string
1024
- * @private
1025
- */
1026
627
  _isEntirelyDigitsNoZeros(string) {
1027
628
  return /^[1-9]\d*$/.test(string);
1028
629
  }
1029
- /**
1030
- * Limit results to the given number
1031
- * @param max The number to limit by (placeholder string or integer greater than 0)
1032
- */
1033
630
  limit(max) {
1034
631
  if (typeof max === "string") {
1035
632
  max = max.trim();
@@ -1039,55 +636,38 @@ UNION
1039
636
  } else if (typeof max === "number" && Number.isInteger(max) && max >= 1) {
1040
637
  this._limit = max;
1041
638
  } else {
1042
- throw new Error(
1043
- `SelectBuilder: "limit(n)" must receive a positive integer (got "${max}")`
1044
- );
639
+ throw new Error(`SelectBuilder: "limit(n)" must receive a positive integer (got "${max}")`);
1045
640
  }
1046
641
  return this;
1047
642
  }
1048
- /**
1049
- * Fetch results from the given offset
1050
- * @param number The offset (placeholder string or integer greater than or equal to 0)
1051
- */
1052
643
  offset(number) {
1053
644
  if (typeof number === "string" && this._isEntirelyDigits(number.trim())) {
1054
645
  this._offset = parseInt(number, 10);
1055
646
  } else if (typeof number === "number" && Number.isInteger(number) && number >= 0) {
1056
647
  this._offset = number;
1057
648
  } else {
1058
- throw new Error(
1059
- `SelectBuilder: "offset(n)" must receive a positive integer (got "${number}")`
1060
- );
649
+ throw new Error(`SelectBuilder: "offset(n)" must receive a positive integer (got "${number}")`);
1061
650
  }
1062
651
  return this;
1063
652
  }
1064
- /**
1065
- * Set the offset based on the limit with the given page number
1066
- * @param number The page number (integer greater than 0)
1067
- */
1068
653
  page(number) {
1069
654
  if (typeof number === "string" && this._isEntirelyDigitsNoZeros(number.trim())) {
1070
655
  this._page = parseInt(number, 10);
1071
656
  } else if (typeof number === "number" && Number.isInteger(number) && number >= 1) {
1072
657
  this._page = number;
1073
658
  } else {
1074
- throw new Error(
1075
- 'SelectBuilder: "page(n)" must receive a positive integer'
1076
- );
659
+ throw new Error('SelectBuilder: "page(n)" must receive a positive integer');
1077
660
  }
1078
661
  return this;
1079
662
  }
1080
- };
1081
-
663
+ }
1082
664
  // src/extractGrouped/extractGrouped.ts
1083
665
  function extractGrouped(field, records) {
1084
666
  let idx = 0;
1085
667
  const grouped = {};
1086
668
  for (const record of records) {
1087
669
  if (!(field in record)) {
1088
- throw new Error(
1089
- `Column "${String(field)}" is not present in record ${idx}`
1090
- );
670
+ throw new Error(`Column "${String(field)}" is not present in record ${idx}`);
1091
671
  }
1092
672
  if (!grouped[record[field]]) {
1093
673
  grouped[record[field]] = [];
@@ -1097,44 +677,35 @@ function extractGrouped(field, records) {
1097
677
  }
1098
678
  return grouped;
1099
679
  }
1100
-
1101
680
  // src/extractIndexed/extractIndexed.ts
1102
681
  function extractIndexed(field, records) {
1103
682
  let idx = 0;
1104
683
  const indexed = {};
1105
684
  for (const record of records) {
1106
685
  if (!(field in record)) {
1107
- throw new Error(
1108
- `Column "${String(field)}" is not present in record ${idx}`
1109
- );
686
+ throw new Error(`Column "${String(field)}" is not present in record ${idx}`);
1110
687
  }
1111
688
  indexed[record[field]] = record;
1112
689
  idx++;
1113
690
  }
1114
691
  return indexed;
1115
692
  }
1116
-
1117
693
  // src/extractLookup/extractLookup.ts
1118
694
  function extractLookup(keyField, valueField, records) {
1119
695
  const lookup = {};
1120
696
  let idx = 0;
1121
697
  for (const record of records) {
1122
698
  if (!(keyField in record)) {
1123
- throw new Error(
1124
- `Column "${String(keyField)}" is not present in record ${idx}`
1125
- );
699
+ throw new Error(`Column "${String(keyField)}" is not present in record ${idx}`);
1126
700
  }
1127
701
  if (!(valueField in record)) {
1128
- throw new Error(
1129
- `Column "${String(valueField)}" is not present in record ${idx}`
1130
- );
702
+ throw new Error(`Column "${String(valueField)}" is not present in record ${idx}`);
1131
703
  }
1132
704
  idx++;
1133
705
  lookup[String(record[keyField])] = record[valueField];
1134
706
  }
1135
707
  return lookup;
1136
708
  }
1137
-
1138
709
  // src/getPagination/getPagination.ts
1139
710
  function getPagination(query, total) {
1140
711
  total = Number(total) || 0;
@@ -1167,7 +738,6 @@ function getPagination(query, total) {
1167
738
  isLast
1168
739
  };
1169
740
  }
1170
-
1171
741
  // src/runMysql/runMysql.ts
1172
742
  function _queryToPromise(mysqlClient, sql, bindings) {
1173
743
  return new Promise((resolve, reject) => {
@@ -1218,7 +788,6 @@ async function runMysqlAsyncWithCount(mysqlClient, query, {
1218
788
  return { records, total: 0, pagination: getPagination(query, 0), fields };
1219
789
  }
1220
790
  }
1221
-
1222
791
  // src/toSafeJson/toSafeJson.ts
1223
792
  function toSafeJsonValue(value) {
1224
793
  if (typeof value === "bigint") {
@@ -1245,19 +814,16 @@ function toSafeJsonString(records) {
1245
814
  }
1246
815
 
1247
816
  // src/runPrisma/runPrisma.ts
1248
- async function runPrisma(prisma, query, { engine = void 0 } = {}) {
817
+ async function runPrisma(prisma, query, { engine = undefined } = {}) {
1249
818
  const { sql, bindings } = query.compile({ engine });
1250
819
  return prisma.$queryRawUnsafe(sql, ...bindings);
1251
820
  }
1252
821
  async function runPrismaWithCount(prisma, query, {
1253
- engine = void 0,
822
+ engine = undefined,
1254
823
  countExpr = "*"
1255
824
  } = {}) {
1256
825
  const { sql, bindings } = query.compile({ engine });
1257
- const records = await prisma.$queryRawUnsafe(
1258
- sql,
1259
- ...bindings
1260
- );
826
+ const records = await prisma.$queryRawUnsafe(sql, ...bindings);
1261
827
  if (records.length > 0) {
1262
828
  const { sql: sql2, bindings: bindings2 } = query.compileCount({ engine, countExpr });
1263
829
  const result = await prisma.$queryRawUnsafe(sql2, ...bindings2);
@@ -1268,20 +834,20 @@ async function runPrismaWithCount(prisma, query, {
1268
834
  }
1269
835
  }
1270
836
  export {
1271
- Parser,
1272
- SelectBuilder,
1273
- extractGrouped,
1274
- extractIndexed,
1275
- extractLookup,
1276
- getPagination,
1277
- runMysql,
1278
- runMysqlAsync,
1279
- runMysqlAsyncWithCount,
1280
- runMysqlWithCount,
1281
- runPrisma,
1282
- runPrismaWithCount,
1283
- toSafeJsonRecord,
1284
- toSafeJsonRecords,
837
+ toSafeJsonValue,
1285
838
  toSafeJsonString,
1286
- toSafeJsonValue
839
+ toSafeJsonRecords,
840
+ toSafeJsonRecord,
841
+ runPrismaWithCount,
842
+ runPrisma,
843
+ runMysqlWithCount,
844
+ runMysqlAsyncWithCount,
845
+ runMysqlAsync,
846
+ runMysql,
847
+ getPagination,
848
+ extractLookup,
849
+ extractIndexed,
850
+ extractGrouped,
851
+ SelectBuilder,
852
+ Parser
1287
853
  };