sql-love 1.0.3 → 1.0.4

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.
Files changed (56) hide show
  1. package/README.md +52 -32
  2. package/dist/index.cjs +16 -0
  3. package/dist/index.d.ts +732 -9
  4. package/dist/index.mjs +16 -0
  5. package/package.json +27 -10
  6. package/.github/workflows/node.js.yml +0 -37
  7. package/.prettierrc.json +0 -13
  8. package/CHANGELOG.md +0 -39
  9. package/CONTRIBUTING.md +0 -85
  10. package/assets/sql-love-logo.png +0 -0
  11. package/codecov.yml +0 -7
  12. package/dist/index.js +0 -9
  13. package/dist/src/Parser/Parser.d.ts +0 -157
  14. package/dist/src/Parser/Parser.js +0 -304
  15. package/dist/src/SelectBuilder/SelectBuilder.d.ts +0 -379
  16. package/dist/src/SelectBuilder/SelectBuilder.js +0 -810
  17. package/dist/src/extractGrouped/extractGrouped.d.ts +0 -17
  18. package/dist/src/extractGrouped/extractGrouped.js +0 -31
  19. package/dist/src/extractIndexed/extractIndexed.d.ts +0 -12
  20. package/dist/src/extractIndexed/extractIndexed.js +0 -23
  21. package/dist/src/extractLookup/extractLookup.d.ts +0 -9
  22. package/dist/src/extractLookup/extractLookup.js +0 -21
  23. package/dist/src/getPagination/getPagination.d.ts +0 -16
  24. package/dist/src/getPagination/getPagination.js +0 -40
  25. package/dist/src/runMysql/runMysql.d.ts +0 -58
  26. package/dist/src/runMysql/runMysql.js +0 -61
  27. package/dist/src/runPrisma/runPrisma.d.ts +0 -41
  28. package/dist/src/runPrisma/runPrisma.js +0 -34
  29. package/dist/src/substrCount/substrCount.d.ts +0 -6
  30. package/dist/src/substrCount/substrCount.js +0 -14
  31. package/dist/src/toSafeJson/toSafeJson.d.ts +0 -25
  32. package/dist/src/toSafeJson/toSafeJson.js +0 -45
  33. package/index.ts +0 -19
  34. package/src/Parser/Parser.spec.ts +0 -226
  35. package/src/Parser/Parser.ts +0 -325
  36. package/src/SelectBuilder/SelectBuilder.spec.ts +0 -785
  37. package/src/SelectBuilder/SelectBuilder.ts +0 -914
  38. package/src/extractGrouped/extractGrouped.spec.ts +0 -43
  39. package/src/extractGrouped/extractGrouped.ts +0 -36
  40. package/src/extractIndexed/extractIndexed.spec.ts +0 -40
  41. package/src/extractIndexed/extractIndexed.ts +0 -28
  42. package/src/extractLookup/extractLookup.spec.ts +0 -38
  43. package/src/extractLookup/extractLookup.ts +0 -33
  44. package/src/getPagination/getPagination.spec.ts +0 -75
  45. package/src/getPagination/getPagination.ts +0 -42
  46. package/src/runMysql/runMysql.spec.ts +0 -286
  47. package/src/runMysql/runMysql.ts +0 -106
  48. package/src/runPrisma/runPrisma.spec.ts +0 -99
  49. package/src/runPrisma/runPrisma.ts +0 -75
  50. package/src/substrCount/substrCount.spec.ts +0 -29
  51. package/src/substrCount/substrCount.ts +0 -14
  52. package/src/toSafeJson/toSafeJson.spec.ts +0 -28
  53. package/src/toSafeJson/toSafeJson.ts +0 -56
  54. package/tsconfig.build.json +0 -10
  55. package/tsconfig.json +0 -22
  56. package/vitest.config.ts +0 -11
@@ -1,226 +0,0 @@
1
- import SelectBuilder from '../SelectBuilder/SelectBuilder';
2
-
3
- const trim = s => s.replace(/\s+/g, ' ').trim();
4
-
5
- describe('column handler', () => {
6
- it('should recognize asterisk', () => {
7
- const query = new SelectBuilder('SELECT * FROM mytable');
8
- expect(query._columns).toEqual(['*']);
9
- });
10
- });
11
- describe('comment handler', () => {
12
- it('should strip single-line comments -- dashes', () => {
13
- const query = new SelectBuilder(
14
- `
15
- SELECT
16
- -- stuff
17
- a,
18
- -- more stuff
19
- b -- and even more stuff
20
- FROM mytable
21
- `
22
- );
23
- expect(trim(query.compile().sql)).toBe('SELECT a, b FROM mytable');
24
- });
25
- it('should strip single-line comments #hash', () => {
26
- const query = new SelectBuilder(
27
- `
28
- SELECT
29
- #stuff
30
- a,
31
- # more stuff
32
- b #and even more stuff
33
- FROM mytable
34
- `
35
- );
36
- expect(trim(query.compile().sql)).toBe('SELECT a, b FROM mytable');
37
- });
38
- it('should strip multi-line comments', () => {
39
- const query = new SelectBuilder(
40
- `
41
- SELECT
42
- /*
43
- stuff
44
- */
45
- a,
46
- /* more stuff */ b
47
- FROM mytable
48
- `
49
- );
50
- expect(trim(query.compile().sql)).toBe('SELECT a, b FROM mytable');
51
- });
52
- });
53
- describe('subquery handler', () => {
54
- it('should handle column subqueries', () => {
55
- const normalized =
56
- 'SELECT a, (SELECT * FROM tbl2) AS b FROM mytable WHERE mycol = 1';
57
- const query = new SelectBuilder(normalized);
58
- expect(trim(query.compile().sql)).toBe(normalized);
59
- });
60
- it('should handle column subqueries with SELECT EXISTS', () => {
61
- const normalized =
62
- 'SELECT a, (SELECT EXISTS (SELECT * FROM tbl2)) AS b FROM mytable WHERE mycol = 1';
63
- const query = new SelectBuilder(normalized);
64
- expect(trim(query.compile().sql)).toBe(normalized);
65
- });
66
- it('should handle IF() column expressions', () => {
67
- const normalized =
68
- 'SELECT a, IF(b > 0) AS is_gt_0, IF(b > 100) AS is_gt_100 FROM mytable WHERE mycol = 1';
69
- const query = new SelectBuilder(normalized);
70
- expect(trim(query.compile().sql)).toBe(normalized);
71
- });
72
- it('should handle IN() expressions in WHERE', () => {
73
- const normalized =
74
- 'SELECT * FROM mytable WHERE mycol IN (SELECT id FROM othertable)';
75
- const query = new SelectBuilder(normalized);
76
- expect(trim(query.compile().sql)).toBe(normalized);
77
- });
78
- it('should handle IN() expressions in JOINs', () => {
79
- const normalized =
80
- 'SELECT * FROM a INNER JOIN b ON b.id = a.b_id AND b.status IN (SELECT id FROM statuses) WHERE a.id > 10';
81
- const query = new SelectBuilder(normalized);
82
- expect(trim(query.compile().sql)).toBe(normalized);
83
- });
84
- });
85
- describe('table handler', () => {
86
- it('should parse single table', () => {
87
- const query = new SelectBuilder('SELECT * FROM a');
88
- expect(query._tables).toEqual(['a']);
89
- });
90
- it('should parse comma-separated tables', () => {
91
- const query = new SelectBuilder('SELECT * FROM a, b');
92
- expect(query._tables).toEqual(['a', 'b']);
93
- });
94
- });
95
- describe('column handler', () => {
96
- it('should parse single column', () => {
97
- const query = new SelectBuilder('SELECT a FROM b');
98
- expect(query._columns).toEqual(['a']);
99
- });
100
- it('should parse comma-separated columns', () => {
101
- const query = new SelectBuilder('SELECT a, b FROM c');
102
- expect(query._columns).toEqual(['a', 'b']);
103
- });
104
- it('should handle expressions with one comma', () => {
105
- const query = new SelectBuilder("SELECT a, CONCAT('b',b) FROM c");
106
- expect(query._columns).toEqual(['a', "CONCAT('b',b)"]);
107
- });
108
- it('should handle expressions with 2 commas', () => {
109
- const query = new SelectBuilder(
110
- "SELECT a, CONCAT(fname, ' ', lname) FROM users"
111
- );
112
- expect(query._columns).toEqual(['a', "CONCAT(fname, ' ', lname)"]);
113
- });
114
- });
115
- describe('option handler', () => {
116
- it('should save SQL_CALC_FOUND_ROWS as an option', () => {
117
- const query = new SelectBuilder('SELECT SQL_CALC_FOUND_ROWS * FROM a');
118
- expect(query._columns).toEqual(['*']);
119
- expect(query._options).toEqual(['SQL_CALC_FOUND_ROWS']);
120
- });
121
- });
122
- describe('JOIN handler', () => {
123
- const joins = [
124
- 'JOIN',
125
- 'INNER JOIN',
126
- 'LEFT JOIN',
127
- 'LEFT OUTER JOIN',
128
- 'OUTER JOIN',
129
- 'RIGHT JOIN',
130
- 'RIGHT OUTER JOIN',
131
- 'CROSS JOIN',
132
- 'FULL JOIN',
133
- 'FULL OUTER JOIN',
134
- ];
135
- joins.forEach(join => {
136
- it(`should handle ${join}`, () => {
137
- const sql = `SELECT * FROM a ${join} b ON b.id = a.b_id`;
138
- const query = new SelectBuilder(sql);
139
- expect(trim(query.compile().sql)).toBe(sql);
140
- });
141
- });
142
- });
143
- describe('conditions handler', () => {
144
- it('should parse WHERE 1 = 1', () => {
145
- const query = new SelectBuilder('SELECT * FROM mytable WHERE 1 = 1');
146
- expect(query._wheres).toEqual(['1 = 1']);
147
- });
148
- it("should parse WHERE '1'", () => {
149
- const query = new SelectBuilder("SELECT * FROM mytable WHERE '1'");
150
- expect(query._wheres).toEqual(["'1'"]);
151
- });
152
- it('should parse WHERE true', () => {
153
- const query = new SelectBuilder('SELECT * FROM mytable WHERE true');
154
- expect(query._wheres).toEqual(['true']);
155
- });
156
- it('should parse single WHERE', () => {
157
- const query = new SelectBuilder('SELECT * FROM mytable WHERE mycol = 1');
158
- expect(query._wheres).toEqual(['mycol = 1']);
159
- });
160
- it('should parse two WHERE clauses joined by AND', () => {
161
- const query = new SelectBuilder(
162
- 'SELECT * FROM mytable WHERE mycol = 1 AND scheduled < NOW()'
163
- );
164
- expect(query._wheres).toEqual(['mycol = 1', 'scheduled < NOW()']);
165
- });
166
- it('should parse two WHERE clauses joined by OR', () => {
167
- const query = new SelectBuilder(
168
- 'SELECT * FROM mytable WHERE mycol = 1 OR scheduled < NOW()'
169
- );
170
- expect(query._wheres).toEqual(['(mycol = 1 OR scheduled < NOW())']);
171
- });
172
- it('should parse OR then AND', () => {
173
- const query = new SelectBuilder(
174
- 'SELECT * FROM mytable WHERE a = 1 OR b = 2 AND c = 3'
175
- );
176
- expect(query._wheres).toEqual(['(a = 1 OR b = 2)', 'c = 3']);
177
- });
178
- it('should parse OR then AND with parens', () => {
179
- const query = new SelectBuilder(
180
- 'SELECT * FROM mytable WHERE (a = 1 OR b = 2) AND c = 3'
181
- );
182
- expect(query._wheres).toEqual(['(a = 1 OR b = 2)', 'c = 3']);
183
- });
184
- it('should parse AND then OR', () => {
185
- const query = new SelectBuilder(
186
- 'SELECT * FROM mytable WHERE a = 1 AND b = 2 OR c = 3'
187
- );
188
- expect(query._wheres).toEqual(['a = 1', '(b = 2 OR c = 3)']);
189
- });
190
- });
191
- it('should parse GROUP BY and HAVING', () => {
192
- const query = new SelectBuilder(
193
- 'SELECT name, COUNT(*) FROM mytable GROUP BY name HAVING COUNT(*) > 1'
194
- );
195
- expect(query._groupBys).toEqual(['name']);
196
- expect(query._havings).toEqual(['COUNT(*) > 1']);
197
- });
198
- it('should parse ORDER BY', () => {
199
- const query = new SelectBuilder('SELECT * FROM users ORDER BY id DESC');
200
- expect(query._orderBys).toEqual(['id DESC']);
201
- });
202
- describe('pagination', () => {
203
- it('should parse LIMIT with number', () => {
204
- const query = new SelectBuilder('SELECT * FROM mytable LIMIT 1');
205
- expect(query._limit).toBe(1);
206
- });
207
- it('should parse OFFSET with number', () => {
208
- const query = new SelectBuilder('SELECT * FROM mytable LIMIT 5 OFFSET 10');
209
- expect(query._offset).toBe(10);
210
- });
211
- it('should parse OFFSET, LIMIT with numbers', () => {
212
- const query = new SelectBuilder('SELECT * FROM mytable LIMIT 10, 5');
213
- expect(query._limit).toBe(5);
214
- expect(query._offset).toBe(10);
215
- });
216
- it('should parse OFFSET # ROWS', () => {
217
- const query = new SelectBuilder('SELECT * FROM mytable OFFSET 10 ROWS');
218
- expect(query._offset).toBe(10);
219
- });
220
- it('should parse FETCH NEXT # ROWS ONLY', () => {
221
- const query = new SelectBuilder(
222
- 'SELECT * FROM mytable FETCH NEXT 10 ROWS ONLY'
223
- );
224
- expect(query._limit).toBe(10);
225
- });
226
- });
@@ -1,325 +0,0 @@
1
- import type Select from '../SelectBuilder/SelectBuilder';
2
-
3
- /**
4
- * Parse SQL and populate onto a Select query object
5
- */
6
- export default class Parser {
7
- query: Select;
8
-
9
- /**
10
- * Create a new instance
11
- * @param query A Select object on which to build parsed conditions
12
- */
13
- constructor(query: Select) {
14
- this.query = query;
15
- }
16
-
17
- /**
18
- * Strip single-line and multi-line comment blocks
19
- * @param sql The SQL string
20
- * @return The SQL string without comments
21
- * @private
22
- */
23
- _stripComments(sql: string) {
24
- // multiline comments
25
- sql = sql.replace(/\/\*[\s\S]*?\*\//g, '');
26
- // single line comments -- dashes
27
- sql = sql.replace(/--([\r\n]|\s+[^\r\n]+[\r\n])/g, '');
28
- // single line comments #hashes
29
- sql = sql.replace(/#[^\r\n]+[\r\n]/g, '');
30
- return sql;
31
- }
32
-
33
- /**
34
- * Before splitting into SQL clauses, extract some regex-able subQueries
35
- * @param sql The unparsed sql string
36
- * @return An array with new sql and subQueries
37
- * @private
38
- */
39
- _extractSubQueries(sql: string) {
40
- const subQueries = {};
41
- let i = 0;
42
- const extractor = $0 => {
43
- const placeholder = `~~SUBQUERY_${i++}~~`;
44
- subQueries[placeholder] = $0;
45
- return placeholder;
46
- };
47
- // subselect in FROM clause
48
- sql = sql.replace(/\(\s*SELECT\s+.+\)\s+AS\s+[^\s,]+/, extractor);
49
- // IF() in FROM clause
50
- sql = sql.replace(/\bIF\s*\(.+\)\s+AS\s+[^\s,]+/, extractor);
51
- // IN (SELECT *) in JOINs, WHERE or HAVING
52
- sql = sql.replace(/\bIN\s*\(\s*SELECT\s.+?\)/, extractor);
53
- return { sql, subQueries };
54
- }
55
-
56
- /**
57
- * Inject column subQueries back into this object
58
- * @param subQueries The lookup of extracted subQueries
59
- * @private
60
- */
61
- _injectSubQueries(subQueries: Record<string, string>) {
62
- const replacer = $0 => {
63
- return subQueries[$0] || $0;
64
- };
65
- const mapper = clause => {
66
- return clause.replace(/~~SUBQUERY_\d+~~/g, replacer);
67
- };
68
- this.query._columns = this.query._columns.map(replacer);
69
- this.query._joins = this.query._joins.map(mapper);
70
- this.query._wheres = this.query._wheres.map(mapper);
71
- this.query._havings = this.query._havings.map(mapper);
72
- }
73
-
74
- /**
75
- * Split SQL into clauses (used by parse())
76
- * @param sql The SQL to split
77
- * @return
78
- * @private
79
- */
80
- _split(sql: string) {
81
- const splitter =
82
- /(?:^|\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;
83
- return sql.split(splitter);
84
- }
85
-
86
- /**
87
- * Get a QuickSelect object representing the given SQL SELECT statement
88
- * @param rawSql The raw SQL for the SELECT statement
89
- * @return True if the SQL was parsed successfully
90
- */
91
- parse(rawSql: string) {
92
- const stripped = this._stripComments(rawSql);
93
- const { sql, subQueries } = this._extractSubQueries(stripped);
94
- const expressions = this._split(sql);
95
- let i = 1;
96
- while (i < expressions.length) {
97
- const rawKeyword = expressions[i++].trim();
98
- const keyword = rawKeyword.toUpperCase().replace(/\s+/g, '_');
99
- const clause = expressions[i++].trim();
100
- const handler = `_handle${keyword}`;
101
- this[handler](clause);
102
- }
103
- this._injectSubQueries(subQueries);
104
- return true;
105
- }
106
-
107
- /**
108
- * Handle SQL_CALC_FOUND_ROWS and column names
109
- * @param clause The clause after the SELECT
110
- * @private
111
- */
112
- _handleSELECT(clause: string) {
113
- let columns = clause.split(/s*,s*/);
114
- // now handle parenthesis expressions that contain commas
115
- let buffer = '';
116
- columns.forEach((column, i) => {
117
- if (i === 0) {
118
- const optionRegex = /^(SQL_CALC_FOUND_ROWS)\s+/i;
119
- const match = column.match(optionRegex);
120
- if (match) {
121
- this.query.option(match[1]);
122
- column = column.replace(optionRegex, '');
123
- }
124
- }
125
- if (buffer.length) {
126
- // we are in the middle of an expression containing parenthesis
127
- if (column.indexOf(')') > 0) {
128
- // we have an end parenthesis
129
- buffer += column;
130
- this.query.column(buffer.trim());
131
- buffer = '';
132
- } else {
133
- buffer += column + ',';
134
- }
135
- } else if (column.match(/\([^)]+$/)) {
136
- buffer = column + ',';
137
- } else {
138
- this.query.column(column.trim());
139
- }
140
- });
141
- }
142
-
143
- /**
144
- * Handle table names
145
- * @param clause The clause after the FROM
146
- * @private
147
- */
148
- _handleFROM(clause: string) {
149
- const tables = clause.split(/\s*,\s*/);
150
- tables.forEach(table => this.query.table(table));
151
- }
152
-
153
- /**
154
- * Handle INNER JOIN statements
155
- * @param clause The clause after the INNER JOIN
156
- * @private
157
- */
158
- _handleINNER_JOIN(clause: string) {
159
- this.query.innerJoin(clause);
160
- }
161
-
162
- /**
163
- * Handle LEFT JOIN statements
164
- * @param clause The clause after the LEFT JOIN
165
- * @private
166
- */
167
- _handleLEFT_JOIN(clause: string) {
168
- this.query.leftJoin(clause);
169
- }
170
-
171
- /**
172
- * Handle LEFT OUTER JOIN statements
173
- * @param clause The clause after the LEFT OUTER JOIN
174
- * @private
175
- */
176
- _handleLEFT_OUTER_JOIN(clause: string) {
177
- this.query.leftOuterJoin(clause);
178
- }
179
-
180
- /**
181
- * Handle RIGHT JOIN statements
182
- * @param clause The clause after the RIGHT JOIN
183
- * @private
184
- */
185
- _handleRIGHT_JOIN(clause: string) {
186
- this.query.rightJoin(clause);
187
- }
188
-
189
- /**
190
- * Handle RIGHT OUTER JOIN statements
191
- * @param clause The clause after the RIGHT OUTER JOIN
192
- * @private
193
- */
194
- _handleRIGHT_OUTER_JOIN(clause: string) {
195
- this.query.rightOuterJoin(clause);
196
- }
197
-
198
- /**
199
- * Handle CROSS JOIN statements
200
- * @param clause The clause after the CROSS JOIN
201
- * @private
202
- */
203
- _handleCROSS_JOIN(clause: string) {
204
- this.query.crossJoin(clause);
205
- }
206
-
207
- /**
208
- * Handle FULL JOIN statements
209
- * @param clause The clause after the FULL JOIN
210
- * @private
211
- */
212
- _handleFULL_JOIN(clause: string) {
213
- this.query.fullJoin(clause);
214
- }
215
-
216
- /**
217
- * Handle FULL OUTER JOIN statements
218
- * @param clause The clause after the FULL OUTER JOIN
219
- * @private
220
- */
221
- _handleFULL_OUTER_JOIN(clause: string) {
222
- this.query.fullOuterJoin(clause);
223
- }
224
-
225
- /**
226
- * Handle WHERE conditions
227
- * @param clause All the conditions after WHERE
228
- * @private
229
- */
230
- _handleWHERE(clause: string) {
231
- if (/^(1|'1'|true)$/i.test(clause)) {
232
- this.query._wheres.push(clause);
233
- } else {
234
- this._handleConditions('where', clause);
235
- }
236
- }
237
-
238
- /**
239
- * Handle HAVING statements
240
- * @param clause All the conditions after HAVING
241
- * @private
242
- */
243
- _handleHAVING(clause: string) {
244
- this._handleConditions('having', clause);
245
- }
246
-
247
- /**
248
- * Build a conditions list
249
- * @param type Either where or having
250
- * @param clause The expressions following the type keyword
251
- * @private
252
- */
253
- _handleConditions(type: 'where' | 'having', clause: string) {
254
- const andGroups = clause.split(/\bAND\b/i);
255
- andGroups.forEach(andGroup => {
256
- const orPieces = andGroup.split(/\bOR\b/i).map(str => str.trim());
257
- if (orPieces.length === 1) {
258
- // no OR operators
259
- const fn = type; // either where or having
260
- this.query[fn](orPieces[0]);
261
- } else {
262
- // some OR operators
263
- // either orWhere or orHaving
264
- const orFn = type === 'where' ? 'orWhere' : 'orHaving';
265
- this.query[orFn](orPieces.map(p => [p]));
266
- }
267
- });
268
- }
269
-
270
- /**
271
- * Handle GROUP BY statements
272
- * @param clause The clauses after the GROUP BY
273
- * @private
274
- */
275
- _handleGROUP_BY(clause: string) {
276
- const columns = clause.split(/\s*,\s*/);
277
- columns.forEach(column => this.query.groupBy(column));
278
- }
279
-
280
- /**
281
- * Handle ORDER BY statements
282
- * @param clause The clause after the ORDER BY
283
- * @private
284
- */
285
- _handleORDER_BY(clause: string) {
286
- const columns = clause.split(/\s*,\s*/);
287
- columns.forEach(column => this.query.orderBy(column));
288
- }
289
-
290
- /**
291
- * Handle LIMIT statements including "LIMIT #" and "LIMIT #, #"
292
- * @param clause The clause after the LIMIT
293
- * @private
294
- */
295
- _handleLIMIT(clause: string) {
296
- const offsetLimit = clause.match(/^(\d+|\?|:\w+)\s*,\s*(\d+|\?|:\w+)$/);
297
- if (offsetLimit) {
298
- this.query.offset(offsetLimit[1]);
299
- this.query.limit(offsetLimit[2]);
300
- } else {
301
- clause = clause.replace(/ ROWS$/i, '');
302
- this.query.limit(clause);
303
- }
304
- }
305
-
306
- /**
307
- * Handle OFFSET statements
308
- * @param clause The number after the OFFSET
309
- * @private
310
- */
311
- _handleOFFSET(clause: string) {
312
- clause = clause.replace(/^(\d+) ROWS$/i, '$1');
313
- this.query.offset(clause);
314
- }
315
-
316
- /**
317
- * Handle OFFSET statements
318
- * @param clause The number after the OFFSET
319
- * @private
320
- */
321
- _handleFETCH_NEXT(clause: string) {
322
- clause = clause.replace(/^(\d+) ROWS ONLY$/i, '$1');
323
- this.query.limit(clause);
324
- }
325
- }