@uwdata/mosaic-sql 0.11.0 → 0.12.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.
Files changed (142) hide show
  1. package/dist/mosaic-sql.js +2242 -1064
  2. package/dist/mosaic-sql.min.js +1 -1
  3. package/dist/types/ast/aggregate.d.ts +70 -0
  4. package/dist/types/ast/between-op.d.ts +46 -0
  5. package/dist/types/ast/binary-op.d.ts +28 -0
  6. package/dist/types/ast/case.d.ts +68 -0
  7. package/dist/types/ast/cast.d.ts +21 -0
  8. package/dist/types/ast/column-param.d.ts +17 -0
  9. package/dist/types/ast/column-ref.d.ts +39 -0
  10. package/dist/types/ast/fragment.d.ts +14 -0
  11. package/dist/types/ast/from.d.ts +21 -0
  12. package/dist/types/ast/function.d.ts +21 -0
  13. package/dist/types/ast/in-op.d.ts +21 -0
  14. package/dist/types/ast/interval.d.ts +21 -0
  15. package/dist/types/ast/literal.d.ts +15 -0
  16. package/dist/types/ast/logical-op.d.ts +46 -0
  17. package/dist/types/ast/node.d.ts +24 -0
  18. package/dist/types/ast/order-by.d.ts +29 -0
  19. package/dist/types/ast/param.d.ts +19 -0
  20. package/dist/types/ast/query.d.ts +268 -0
  21. package/dist/types/ast/sample.d.ts +42 -0
  22. package/dist/types/ast/select.d.ts +22 -0
  23. package/dist/types/ast/table-ref.d.ts +25 -0
  24. package/dist/types/ast/unary-op.d.ts +39 -0
  25. package/dist/types/ast/verbatim.d.ts +9 -0
  26. package/dist/types/ast/window.d.ts +177 -0
  27. package/dist/types/ast/with.d.ts +22 -0
  28. package/dist/types/constants.d.ts +38 -0
  29. package/dist/types/functions/aggregate.d.ts +229 -0
  30. package/dist/types/functions/case.d.ts +15 -0
  31. package/dist/types/functions/cast.d.ts +26 -0
  32. package/dist/types/functions/column.d.ts +9 -0
  33. package/dist/types/functions/datetime.d.ts +44 -0
  34. package/dist/types/functions/literal.d.ts +16 -0
  35. package/dist/types/functions/numeric.d.ts +93 -0
  36. package/dist/types/functions/operators.d.ts +198 -0
  37. package/dist/types/functions/order-by.d.ts +17 -0
  38. package/dist/types/functions/spatial.d.ts +37 -0
  39. package/dist/types/functions/sql-template-tag.d.ts +16 -0
  40. package/dist/types/functions/string.d.ts +55 -0
  41. package/dist/types/functions/table-ref.d.ts +9 -0
  42. package/dist/types/functions/window.d.ts +87 -0
  43. package/dist/types/index-types.d.ts +2 -0
  44. package/dist/types/index.d.ts +53 -0
  45. package/dist/types/load/create.d.ts +8 -0
  46. package/dist/types/load/extension.d.ts +1 -0
  47. package/dist/types/load/load.d.ts +12 -0
  48. package/dist/types/load/sql-from.d.ts +11 -0
  49. package/dist/types/transforms/bin-1d.d.ts +14 -0
  50. package/dist/types/transforms/bin-2d.d.ts +18 -0
  51. package/dist/types/transforms/bin-linear-1d.d.ts +9 -0
  52. package/dist/types/transforms/bin-linear-2d.d.ts +18 -0
  53. package/dist/types/transforms/line-density.d.ts +23 -0
  54. package/dist/types/transforms/m4.d.ts +18 -0
  55. package/dist/types/transforms/scales.d.ts +1 -0
  56. package/dist/types/types.d.ts +59 -0
  57. package/dist/types/util/ast.d.ts +60 -0
  58. package/dist/types/util/function.d.ts +54 -0
  59. package/dist/types/util/string.d.ts +3 -0
  60. package/dist/types/util/type-check.d.ts +18 -0
  61. package/dist/types/visit/recurse.d.ts +28 -0
  62. package/dist/types/visit/rewrite.d.ts +10 -0
  63. package/dist/types/visit/visitors.d.ts +33 -0
  64. package/dist/types/visit/walk.d.ts +7 -0
  65. package/jsconfig.json +11 -0
  66. package/package.json +6 -4
  67. package/src/ast/aggregate.js +164 -0
  68. package/src/ast/between-op.js +75 -0
  69. package/src/ast/binary-op.js +40 -0
  70. package/src/ast/case.js +105 -0
  71. package/src/ast/cast.js +34 -0
  72. package/src/ast/column-param.js +29 -0
  73. package/src/ast/column-ref.js +72 -0
  74. package/src/ast/fragment.js +26 -0
  75. package/src/ast/from.js +40 -0
  76. package/src/ast/function.js +34 -0
  77. package/src/ast/in-op.js +33 -0
  78. package/src/ast/interval.js +33 -0
  79. package/src/ast/literal.js +55 -0
  80. package/src/ast/logical-op.js +67 -0
  81. package/src/ast/node.js +29 -0
  82. package/src/ast/order-by.js +48 -0
  83. package/src/ast/param.js +35 -0
  84. package/src/ast/query.js +578 -0
  85. package/src/ast/sample.js +53 -0
  86. package/src/ast/select.js +44 -0
  87. package/src/ast/table-ref.js +44 -0
  88. package/src/ast/unary-op.js +64 -0
  89. package/src/ast/verbatim.js +26 -0
  90. package/src/ast/window.js +290 -0
  91. package/src/ast/with.js +30 -0
  92. package/src/constants.js +44 -0
  93. package/src/functions/aggregate.js +335 -0
  94. package/src/functions/case.js +21 -0
  95. package/src/functions/cast.js +39 -0
  96. package/src/functions/column.js +20 -0
  97. package/src/functions/datetime.js +65 -0
  98. package/src/functions/literal.js +22 -0
  99. package/src/functions/numeric.js +139 -0
  100. package/src/functions/operators.js +298 -0
  101. package/src/functions/order-by.js +24 -0
  102. package/src/functions/spatial.js +56 -0
  103. package/src/functions/sql-template-tag.js +51 -0
  104. package/src/functions/string.js +82 -0
  105. package/src/functions/table-ref.js +14 -0
  106. package/src/functions/window.js +121 -0
  107. package/src/index-types.ts +2 -0
  108. package/src/index.js +57 -155
  109. package/src/load/create.js +10 -2
  110. package/src/load/load.js +4 -4
  111. package/src/load/sql-from.js +7 -6
  112. package/src/transforms/bin-1d.js +21 -0
  113. package/src/transforms/bin-2d.js +29 -0
  114. package/src/transforms/bin-linear-1d.js +26 -0
  115. package/src/transforms/bin-linear-2d.js +71 -0
  116. package/src/transforms/line-density.js +113 -0
  117. package/src/transforms/m4.js +38 -0
  118. package/src/{scales.js → transforms/scales.js} +31 -17
  119. package/src/types.ts +96 -0
  120. package/src/util/ast.js +96 -0
  121. package/src/util/function.js +78 -0
  122. package/src/util/string.js +16 -0
  123. package/src/util/type-check.js +29 -0
  124. package/src/visit/recurse.js +57 -0
  125. package/src/visit/rewrite.js +32 -0
  126. package/src/visit/visitors.js +108 -0
  127. package/src/visit/walk.js +30 -0
  128. package/tsconfig.json +12 -0
  129. package/src/Query.js +0 -593
  130. package/src/aggregates.js +0 -185
  131. package/src/cast.js +0 -19
  132. package/src/datetime.js +0 -31
  133. package/src/desc.js +0 -13
  134. package/src/expression.js +0 -170
  135. package/src/functions.js +0 -25
  136. package/src/literal.js +0 -6
  137. package/src/operators.js +0 -54
  138. package/src/ref.js +0 -109
  139. package/src/repeat.js +0 -3
  140. package/src/spatial.js +0 -10
  141. package/src/to-sql.js +0 -52
  142. package/src/windows.js +0 -239
@@ -0,0 +1,578 @@
1
+ import { DESCRIBE_QUERY, SELECT_QUERY, SET_OPERATION } from '../constants.js';
2
+ import { asNode, asTableRef, asVerbatim } from '../util/ast.js';
3
+ import { exprList } from '../util/function.js';
4
+ import { unquote } from '../util/string.js';
5
+ import { isArray, isString } from '../util/type-check.js';
6
+ import { isColumnRef } from './column-ref.js';
7
+ import { FromClauseNode } from './from.js';
8
+ import { ExprNode, SQLNode, isNode } from './node.js';
9
+ import { SampleClauseNode } from './sample.js';
10
+ import { SelectClauseNode } from './select.js';
11
+ import { isTableRef } from './table-ref.js';
12
+ import { WindowClauseNode } from './window.js';
13
+ import { WithClauseNode } from './with.js';
14
+
15
+ /**
16
+ * Check if a value is a selection query or set operation.
17
+ * @param {*} value The value to check.
18
+ * @returns {value is Query}
19
+ */
20
+ export function isQuery(value) {
21
+ return value instanceof Query;
22
+ }
23
+
24
+ /**
25
+ * Check if a value is a selection query.
26
+ * @param {*} value The value to check.
27
+ * @returns {value is SelectQuery}
28
+ */
29
+ export function isSelectQuery(value) {
30
+ return value instanceof SelectQuery;
31
+ }
32
+
33
+ /**
34
+ * Check if a value is a describe query.
35
+ * @param {*} value The value to check.
36
+ * @returns {value is DescribeQuery}
37
+ */
38
+ export function isDescribeQuery(value) {
39
+ return value instanceof DescribeQuery;
40
+ }
41
+
42
+ export class Query extends ExprNode {
43
+ /**
44
+ * Create a new select query with the given SELECT expressions.
45
+ * @param {...import('../types.js').SelectExpr} expr The SELECT expressions.
46
+ * @returns {SelectQuery}
47
+ */
48
+ static select(...expr) {
49
+ return new SelectQuery().select(...expr);
50
+ }
51
+
52
+ /**
53
+ * Create a new select query with the given FROM expressions.
54
+ * @param {...import('../types.js').FromExpr} expr The FROM expressions.
55
+ * @returns {SelectQuery}
56
+ */
57
+ static from(...expr) {
58
+ return new SelectQuery().from(...expr);
59
+ }
60
+
61
+ /**
62
+ * Create a new select query with the given WITH CTE queries.
63
+ * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
64
+ * @returns {SelectQuery}
65
+ */
66
+ static with(...expr) {
67
+ return new SelectQuery().with(...expr);
68
+ }
69
+
70
+ /**
71
+ * Create a new UNION set operation over the given queries.
72
+ * @param {...Query} queries The queries.
73
+ * @returns {SetOperation}
74
+ */
75
+ static union(...queries) {
76
+ return new SetOperation('UNION', queries.flat());
77
+ }
78
+
79
+ /**
80
+ * Create a new UNION ALL set operation over the given queries.
81
+ * @param {...Query} queries The queries.
82
+ * @returns {SetOperation}
83
+ */
84
+ static unionAll(...queries) {
85
+ return new SetOperation('UNION ALL', queries.flat());
86
+ }
87
+
88
+ /**
89
+ * Create a new INTERSECT set operation over the given queries.
90
+ * @param {...Query} queries The queries.
91
+ * @returns {SetOperation}
92
+ */
93
+ static intersect(...queries) {
94
+ return new SetOperation('INTERSECT', queries.flat());
95
+ }
96
+
97
+ /**
98
+ * Create a new EXCEPT set operation over the given queries.
99
+ * @param {...Query} queries The queries.
100
+ * @returns {SetOperation}
101
+ */
102
+ static except(...queries) {
103
+ return new SetOperation('EXCEPT', queries.flat());
104
+ }
105
+
106
+ /**
107
+ * Create a new describe query for the given input query.
108
+ * @param {Query} query The query to describe.
109
+ * @returns {DescribeQuery}
110
+ */
111
+ static describe(query) {
112
+ return new DescribeQuery(query);
113
+ }
114
+
115
+ /**
116
+ * Instantiate a new query.
117
+ */
118
+ constructor(type) {
119
+ super(type);
120
+ /** @type {ExprNode[]} */
121
+ this._orderby = [];
122
+ /** @type {number} */
123
+ this._limit = undefined;
124
+ /** @type {number} */
125
+ this._offset = undefined;
126
+ /** @type {Query | null} */
127
+ this.cteFor = null;
128
+ }
129
+
130
+ /**
131
+ * Return a list of subqueries.
132
+ * @returns {Query[]}
133
+ */
134
+ get subqueries() {
135
+ return [];
136
+ }
137
+
138
+ /**
139
+ * Clone this query.
140
+ * @returns {Query}
141
+ */
142
+ clone() {
143
+ return this;
144
+ }
145
+
146
+ /**
147
+ * Add ORDER BY expressions.
148
+ * @param {...import('../types.js').OrderByExpr} expr Expressions to add.
149
+ * @returns
150
+ */
151
+ orderby(...expr) {
152
+ this._orderby = this._orderby.concat(exprList(expr));
153
+ return this;
154
+ }
155
+
156
+ /**
157
+ * Set the query result LIMIT.
158
+ * @param {number} value The limit value.
159
+ * @returns {this}
160
+ */
161
+ limit(value) {
162
+ this._limit = Number.isFinite(value) ? value : undefined;
163
+ return this;
164
+ }
165
+
166
+ /**
167
+ * Set the query result OFFSET.
168
+ * @param {number} value The offset value.
169
+ * @returns {this}
170
+ */
171
+ offset(value) {
172
+ this._offset = Number.isFinite(value) ? value : undefined;
173
+ return this;
174
+ }
175
+ }
176
+
177
+ export class SelectQuery extends Query {
178
+ /**
179
+ * Instantiate a new select query.
180
+ */
181
+ constructor() {
182
+ super(SELECT_QUERY);
183
+ /** @type {WithClauseNode[]} */
184
+ this._with = [];
185
+ /** @type {SelectClauseNode[]} */
186
+ this._select = [];
187
+ /** @type {FromClauseNode[]} */
188
+ this._from = [];
189
+ /** @type {ExprNode[]} */
190
+ this._where = [];
191
+ /** @type {SampleClauseNode} */
192
+ this._sample = undefined;
193
+ /** @type {ExprNode[]} */
194
+ this._groupby = [];
195
+ /** @type {ExprNode[]} */
196
+ this._having = [];
197
+ /** @type {WindowClauseNode[]} */
198
+ this._window = [];
199
+ /** @type {ExprNode[]} */
200
+ this._qualify = [];
201
+ }
202
+
203
+ /**
204
+ * Return a list of subqueries.
205
+ * @returns {Query[]}
206
+ */
207
+ get subqueries() {
208
+ // build map of ctes within base query WITH clause
209
+ const q = this.cteFor || this;
210
+ const w = q instanceof SelectQuery ? q._with : [];
211
+ const cte = w.reduce((obj, c) => (obj[c.name] = c.query, obj), {});
212
+
213
+ // extract subqueries in FROM clause
214
+ // unused CTEs will be ignored
215
+ const queries = [];
216
+ this._from.forEach(({ expr }) => {
217
+ if (isQuery(expr)) {
218
+ queries.push(expr);
219
+ } else if (isTableRef(expr)) {
220
+ const subq = cte[expr.name];
221
+ if (subq) queries.push(subq);
222
+ }
223
+ });
224
+ return queries;
225
+ }
226
+
227
+ /**
228
+ * Clone this query.
229
+ * @returns {SelectQuery}
230
+ */
231
+ clone() {
232
+ return Object.assign(new SelectQuery(), this);
233
+ }
234
+
235
+ /**
236
+ * Add WITH common table expressions (CTEs).
237
+ * @param {...import('../types.js').WithExpr} expr Expressions to add.
238
+ * @returns {this}
239
+ */
240
+ with(...expr) {
241
+ /** @type {WithClauseNode[]} */
242
+ const list = [];
243
+ const add = (name, q) => {
244
+ const query = q.clone();
245
+ query.cteFor = this;
246
+ list.push(new WithClauseNode(name, query));
247
+ };
248
+ expr.flat().forEach(e => {
249
+ if (e != null) for (const name in e) add(name, e[name]);
250
+ });
251
+ this._with = this._with.concat(list);
252
+ return this;
253
+ }
254
+
255
+ /**
256
+ * Add SELECT expressions.
257
+ * @param {...import('../types.js').SelectExpr} expr Expressions to add.
258
+ * @returns {this}
259
+ */
260
+ select(...expr) {
261
+ /** @type {SelectClauseNode[]} */
262
+ const list = [];
263
+ const add = (v, as) => list.push(
264
+ new SelectClauseNode(v == null ? v : asNode(v), unquote(as))
265
+ );
266
+ expr.flat().forEach(e => {
267
+ if (e == null) return;
268
+ else if (isString(e)) add(e, e);
269
+ else if (isColumnRef(e)) add(e, e.column);
270
+ else if (isArray(e)) add(e[1], e[0]);
271
+ else for (const alias in e) add(e[alias], alias);
272
+ });
273
+
274
+ const keys = new Set(list.map(x => x.alias));
275
+ this._select = this._select
276
+ .filter(x => !keys.has(x.alias))
277
+ .concat(list.filter(x => x.expr));
278
+ return this;
279
+ }
280
+
281
+ /**
282
+ * Set SELECT expressions, replacing any prior expressions.
283
+ * @param {...import('../types.js').SelectExpr} expr Expressions to add.
284
+ * @returns {this}
285
+ */
286
+ setSelect(...expr) {
287
+ this._select = [];
288
+ return this.select(...expr);
289
+ }
290
+
291
+ /**
292
+ * Indicate if this query should retrieve distinct values only.
293
+ * @param {boolean} value The distinct flag
294
+ * @returns {this}
295
+ */
296
+ distinct(value = true) {
297
+ this._distinct = !!value;
298
+ return this;
299
+ }
300
+
301
+ /**
302
+ * Add table FROM expressions.
303
+ * @param {...import('../types.js').FromExpr} expr Expressions to add.
304
+ * @returns {this}
305
+ */
306
+ from(...expr) {
307
+ const list = [];
308
+ const add = (v, as) => list.push(new FromClauseNode(asTableRef(v), unquote(as)));
309
+ expr.flat().forEach(e => {
310
+ if (e == null) return;
311
+ else if (isString(e)) add(e, e);
312
+ else if (isTableRef(e)) add(e, e.name);
313
+ else if (isNode(e)) add(e);
314
+ else if (isArray(e)) add(e[1], e[0]);
315
+ else for (const alias in e) add(e[alias], alias);
316
+ });
317
+ this._from = this._from.concat(list);
318
+ return this;
319
+ }
320
+
321
+ /**
322
+ * Set FROM expressions, replacing any prior expressions.
323
+ * @param {...import('../types.js').FromExpr} expr Expressions to add.
324
+ * @returns {this}
325
+ */
326
+ setFrom(...expr) {
327
+ this._from = [];
328
+ return this.from(...expr);
329
+ }
330
+
331
+ /**
332
+ * Set SAMPLE settings.
333
+ * @param {number | SampleClauseNode} value Either a sample clause node
334
+ * or the sample size as either a row count or percentage.
335
+ * @param {import('./sample.js').SampleMethod} [method] The sampling method
336
+ * to use.
337
+ * @param {number} [seed] The random seed.
338
+ * @returns {this}
339
+ */
340
+ sample(value, method, seed) {
341
+ let clause;
342
+ if (typeof value === 'number') {
343
+ const perc = value > 0 && value < 1;
344
+ const size = perc ? value * 100 : Math.floor(value);
345
+ clause = new SampleClauseNode(size, perc, method, seed);
346
+ } else {
347
+ clause = value;
348
+ }
349
+ this._sample = clause;
350
+ return this;
351
+ }
352
+
353
+ /**
354
+ * Add WHERE expressions.
355
+ * @param {...import('../types.js').FilterExpr} expr Expressions to add.
356
+ * @returns {this}
357
+ */
358
+ where(...expr) {
359
+ this._where = this._where.concat(exprList(expr, asVerbatim));
360
+ return this;
361
+ }
362
+
363
+ /**
364
+ * Set WHERE expressions, replacing any prior expressions.
365
+ * @param {...import('../types.js').FilterExpr} expr Expressions to add.
366
+ * @returns {this}
367
+ */
368
+ setWhere(...expr) {
369
+ this._where = [];
370
+ return this.where(...expr);
371
+ }
372
+
373
+ /**
374
+ * Add GROUP BY expressions.
375
+ * @param {...import('../types.js').GroupByExpr} expr Expressions to add.
376
+ * @returns {this}
377
+ */
378
+ groupby(...expr) {
379
+ this._groupby = this._groupby.concat(exprList(expr));
380
+ return this;
381
+ }
382
+
383
+ /**
384
+ * Set GROUP BY expressions, replacing any prior expressions.
385
+ * @param {...import('../types.js').GroupByExpr} expr Expressions to add.
386
+ * @returns {this}
387
+ */
388
+ setGroupby(...expr) {
389
+ this._groupby = [];
390
+ return this.groupby(...expr);
391
+ }
392
+
393
+ /**
394
+ * Add HAVING expressions.
395
+ * @param {...import('../types.js').FilterExpr} expr Expressions to add.
396
+ * @returns {this}
397
+ */
398
+ having(...expr) {
399
+ this._having = this._having.concat(exprList(expr, asVerbatim));
400
+ return this;
401
+ }
402
+
403
+ /**
404
+ * Add WINDOW definitions.
405
+ * @param {...any} expr Expressions to add.
406
+ * @returns {this}
407
+ */
408
+ window(...expr) {
409
+ const list = [];
410
+ expr.flat().forEach(e => {
411
+ if (e != null) for (const name in e) {
412
+ list.push(new WindowClauseNode(unquote(name), e[name]));
413
+ }
414
+ });
415
+ this._window = this._window.concat(list);
416
+ return this;
417
+ }
418
+
419
+ /**
420
+ * Add QUALIFY expressions.
421
+ * @param {...import('../types.js').FilterExpr} expr Expressions to add.
422
+ * @returns {this}
423
+ */
424
+ qualify(...expr) {
425
+ this._qualify = this._qualify.concat(exprList(expr, asVerbatim));
426
+ return this;
427
+ }
428
+
429
+ /**
430
+ * Generate a SQL query string.
431
+ * @returns {string}
432
+ */
433
+ toString() {
434
+ const {
435
+ _with, _select, _distinct, _from, _sample, _where, _groupby,
436
+ _having, _window, _qualify, _orderby, _limit, _offset
437
+ } = this;
438
+ const sql = [];
439
+
440
+ // WITH
441
+ if (_with.length) sql.push(`WITH ${_with.join(', ')}`);
442
+
443
+ // SELECT
444
+ sql.push(`SELECT${_distinct ? ' DISTINCT' : ''} ${_select.join(', ')}`);
445
+
446
+ // FROM
447
+ if (_from.length) sql.push(`FROM ${_from.join(', ')}`);
448
+
449
+ // WHERE
450
+ if (_where.length) {
451
+ const clauses = _where.map(String).filter(x => x).join(' AND ');
452
+ if (clauses) sql.push(`WHERE ${clauses}`);
453
+ }
454
+
455
+ // SAMPLE
456
+ if (_sample) sql.push(`USING SAMPLE ${_sample}`);
457
+
458
+ // GROUP BY
459
+ if (_groupby.length) {
460
+ sql.push(`GROUP BY ${_groupby.join(', ')}`);
461
+ }
462
+
463
+ // HAVING
464
+ if (_having.length) {
465
+ const clauses = _having.map(String).filter(x => x).join(' AND ');
466
+ if (clauses) sql.push(`HAVING ${clauses}`);
467
+ }
468
+
469
+ // WINDOW
470
+ if (_window.length) sql.push(`WINDOW ${_window.join(', ')}`);
471
+
472
+ // QUALIFY
473
+ if (_qualify.length) {
474
+ const clauses = _qualify.map(String).filter(x => x).join(' AND ');
475
+ if (clauses) sql.push(`QUALIFY ${clauses}`);
476
+ }
477
+
478
+ // ORDER BY
479
+ if (_orderby.length) sql.push(`ORDER BY ${_orderby.join(', ')}`);
480
+
481
+ // LIMIT
482
+ if (Number.isFinite(_limit)) sql.push(`LIMIT ${_limit}`);
483
+
484
+ // OFFSET
485
+ if (Number.isFinite(_offset)) sql.push(`OFFSET ${_offset}`);
486
+
487
+ return sql.join(' ');
488
+ }
489
+ }
490
+
491
+ export class DescribeQuery extends SQLNode {
492
+ /**
493
+ * Instantiate a describe query.
494
+ */
495
+ constructor(query) {
496
+ super(DESCRIBE_QUERY);
497
+ this.query = query;
498
+ }
499
+
500
+ /**
501
+ * Clone this describe query.
502
+ * @returns {DescribeQuery}
503
+ */
504
+ clone() {
505
+ return new DescribeQuery(this.query.clone());
506
+ }
507
+
508
+ /**
509
+ * Generate a SQL query string.
510
+ * @returns {string}
511
+ */
512
+ toString() {
513
+ return `DESCRIBE ${this.query}`;
514
+ }
515
+ }
516
+
517
+ export class SetOperation extends Query {
518
+ /**
519
+ * Instantiate a new set operation instance.
520
+ * @param {string} op The set operation.
521
+ * @param {Query[]} queries The subqueries.
522
+ */
523
+ constructor(op, queries) {
524
+ super(SET_OPERATION);
525
+ /**
526
+ * @type {string}
527
+ * @readonly
528
+ */
529
+ this.op = op;
530
+ /**
531
+ * @type {Query[]}
532
+ * @readonly
533
+ */
534
+ this.queries = queries;
535
+ }
536
+
537
+ /**
538
+ * Return a list of subqueries.
539
+ * @returns {Query[]}
540
+ */
541
+ get subqueries() {
542
+ const { queries, cteFor } = this;
543
+ // TODO: revisit this?
544
+ if (cteFor) queries.forEach(q => q.cteFor = cteFor);
545
+ return queries;
546
+ }
547
+
548
+ /**
549
+ * Clone this set operation.
550
+ * @returns {SetOperation}
551
+ */
552
+ clone() {
553
+ const { op, queries, ...rest } = this;
554
+ return Object.assign(new SetOperation(op, queries), rest);
555
+ }
556
+
557
+ /**
558
+ * Generate a SQL query string.
559
+ * @returns {string}
560
+ */
561
+ toString() {
562
+ const { op, queries, _orderby, _limit, _offset } = this;
563
+
564
+ // SUBQUERIES
565
+ const sql = [ queries.join(` ${op} `) ];
566
+
567
+ // ORDER BY
568
+ if (_orderby.length) sql.push(`ORDER BY ${_orderby.join(', ')}`);
569
+
570
+ // LIMIT
571
+ if (Number.isFinite(_limit)) sql.push(`LIMIT ${_limit}`);
572
+
573
+ // OFFSET
574
+ if (Number.isFinite(_offset)) sql.push(`OFFSET ${_offset}`);
575
+
576
+ return sql.join(' ');
577
+ }
578
+ }
@@ -0,0 +1,53 @@
1
+ import { SAMPLE_CLAUSE } from '../constants.js';
2
+ import { SQLNode } from './node.js';
3
+
4
+ /**
5
+ * @typedef {'reservoir' | 'bernoulli' | 'system'} SampleMethod
6
+ */
7
+
8
+ export class SampleClauseNode extends SQLNode {
9
+ /**
10
+ * Instantiate a sample clause node.
11
+ * @param {number} size The sample size as either a row count or percentage.
12
+ * @param {boolean} [perc=false] Flag indicating if the sampling unit is
13
+ * rows (`false`) or a percentage (`true`).
14
+ * @param {SampleMethod} [method] The sampling method. If unspecified,
15
+ * a default method is applied based on the sampling unit.
16
+ * @param {number} [seed] The random seed.
17
+ */
18
+ constructor(size, perc = false, method = undefined, seed = undefined) {
19
+ super(SAMPLE_CLAUSE);
20
+ /**
21
+ * The sample size as either a row count or percentage.
22
+ * @type {number}
23
+ * @readonly
24
+ */
25
+ this.size = size;
26
+ /**
27
+ * Flag indicating if the sampling unit is rows (`false`) or a
28
+ * percentage (`true`).
29
+ * @type {boolean}
30
+ * @readonly
31
+ */
32
+ this.perc = perc;
33
+ /**
34
+ * The sampling method.
35
+ * @type {SampleMethod}
36
+ * @readonly
37
+ */
38
+ this.method = method;
39
+ /**
40
+ * The random seed.
41
+ * @type {number}
42
+ * @readonly
43
+ */
44
+ this.seed = seed;
45
+ }
46
+
47
+ toString() {
48
+ const { size, perc, method, seed } = this;
49
+ const unit = perc ? '%' : ' ROWS';
50
+ const s = seed != null ? `, ${seed}` : '';
51
+ return `${size}${unit}${method ? ` (${method}${s})` : ''}`;
52
+ }
53
+ }
@@ -0,0 +1,44 @@
1
+ import { SELECT_CLAUSE } from '../constants.js';
2
+ import { quoteIdentifier } from '../util/string.js';
3
+ import { ColumnRefNode } from './column-ref.js';
4
+ import { ExprNode, SQLNode } from './node.js';
5
+
6
+ export class SelectClauseNode extends SQLNode {
7
+ /**
8
+ * Instantiate a select node.
9
+ * @param {ExprNode} expr The select expression.
10
+ * @param {string} alias The output name.
11
+ */
12
+ constructor(expr, alias) {
13
+ super(SELECT_CLAUSE);
14
+ /**
15
+ * The select expression.
16
+ * @type {ExprNode}
17
+ * @readonly
18
+ */
19
+ this.expr = expr;
20
+ /**
21
+ * The output name.
22
+ * @type {string}
23
+ * @readonly
24
+ */
25
+ this.alias = alias;
26
+ }
27
+
28
+ /**
29
+ * Generate a SQL query string for this node.
30
+ * @returns {string}
31
+ */
32
+ toString() {
33
+ const { expr, alias } = this;
34
+ return !alias || isColumnRefFor(expr, alias)
35
+ ? `${expr}`
36
+ : `${expr} AS ${quoteIdentifier(alias)}`;
37
+ }
38
+ }
39
+
40
+ function isColumnRefFor(expr, name) {
41
+ return expr instanceof ColumnRefNode
42
+ && expr.table == null
43
+ && expr.column === name;
44
+ }
@@ -0,0 +1,44 @@
1
+ import { TABLE_REF } from '../constants.js';
2
+ import { quoteIdentifier } from '../util/string.js';
3
+ import { ExprNode } from './node.js';
4
+
5
+ /**
6
+ * Check if a value is a table reference node.
7
+ * @param {*} value The value to check.
8
+ * @returns {value is TableRefNode}
9
+ */
10
+ export function isTableRef(value) {
11
+ return value instanceof TableRefNode;
12
+ }
13
+
14
+ export class TableRefNode extends ExprNode {
15
+ /**
16
+ * Instantiate a table reference node.
17
+ * @param {string | string[]} table The table name.
18
+ */
19
+ constructor(table) {
20
+ super(TABLE_REF);
21
+ /**
22
+ * The table name, including namespaces.
23
+ * @type {string[]}
24
+ * @readonly
25
+ */
26
+ this.table = [table].flat();
27
+ }
28
+
29
+ /**
30
+ * The table name without database or schema namespaces.
31
+ * @returns {string}
32
+ */
33
+ get name() {
34
+ return this.table[this.table.length - 1];
35
+ }
36
+
37
+ /**
38
+ * Generate a SQL query string for this node.
39
+ * @returns {string}
40
+ */
41
+ toString() {
42
+ return this.table.map(t => quoteIdentifier(t)).join('.');
43
+ }
44
+ }