@uwdata/mosaic-sql 0.10.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.
- package/dist/mosaic-sql.js +2242 -1064
- package/dist/mosaic-sql.min.js +1 -1
- package/dist/types/ast/aggregate.d.ts +70 -0
- package/dist/types/ast/between-op.d.ts +46 -0
- package/dist/types/ast/binary-op.d.ts +28 -0
- package/dist/types/ast/case.d.ts +68 -0
- package/dist/types/ast/cast.d.ts +21 -0
- package/dist/types/ast/column-param.d.ts +17 -0
- package/dist/types/ast/column-ref.d.ts +39 -0
- package/dist/types/ast/fragment.d.ts +14 -0
- package/dist/types/ast/from.d.ts +21 -0
- package/dist/types/ast/function.d.ts +21 -0
- package/dist/types/ast/in-op.d.ts +21 -0
- package/dist/types/ast/interval.d.ts +21 -0
- package/dist/types/ast/literal.d.ts +15 -0
- package/dist/types/ast/logical-op.d.ts +46 -0
- package/dist/types/ast/node.d.ts +24 -0
- package/dist/types/ast/order-by.d.ts +29 -0
- package/dist/types/ast/param.d.ts +19 -0
- package/dist/types/ast/query.d.ts +268 -0
- package/dist/types/ast/sample.d.ts +42 -0
- package/dist/types/ast/select.d.ts +22 -0
- package/dist/types/ast/table-ref.d.ts +25 -0
- package/dist/types/ast/unary-op.d.ts +39 -0
- package/dist/types/ast/verbatim.d.ts +9 -0
- package/dist/types/ast/window.d.ts +177 -0
- package/dist/types/ast/with.d.ts +22 -0
- package/dist/types/constants.d.ts +38 -0
- package/dist/types/functions/aggregate.d.ts +229 -0
- package/dist/types/functions/case.d.ts +15 -0
- package/dist/types/functions/cast.d.ts +26 -0
- package/dist/types/functions/column.d.ts +9 -0
- package/dist/types/functions/datetime.d.ts +44 -0
- package/dist/types/functions/literal.d.ts +16 -0
- package/dist/types/functions/numeric.d.ts +93 -0
- package/dist/types/functions/operators.d.ts +198 -0
- package/dist/types/functions/order-by.d.ts +17 -0
- package/dist/types/functions/spatial.d.ts +37 -0
- package/dist/types/functions/sql-template-tag.d.ts +16 -0
- package/dist/types/functions/string.d.ts +55 -0
- package/dist/types/functions/table-ref.d.ts +9 -0
- package/dist/types/functions/window.d.ts +87 -0
- package/dist/types/index-types.d.ts +2 -0
- package/dist/types/index.d.ts +53 -0
- package/dist/types/load/create.d.ts +8 -0
- package/dist/types/load/extension.d.ts +1 -0
- package/dist/types/load/load.d.ts +12 -0
- package/dist/types/load/sql-from.d.ts +11 -0
- package/dist/types/transforms/bin-1d.d.ts +14 -0
- package/dist/types/transforms/bin-2d.d.ts +18 -0
- package/dist/types/transforms/bin-linear-1d.d.ts +9 -0
- package/dist/types/transforms/bin-linear-2d.d.ts +18 -0
- package/dist/types/transforms/line-density.d.ts +23 -0
- package/dist/types/transforms/m4.d.ts +18 -0
- package/dist/types/transforms/scales.d.ts +1 -0
- package/dist/types/types.d.ts +59 -0
- package/dist/types/util/ast.d.ts +60 -0
- package/dist/types/util/function.d.ts +54 -0
- package/dist/types/util/string.d.ts +3 -0
- package/dist/types/util/type-check.d.ts +18 -0
- package/dist/types/visit/recurse.d.ts +28 -0
- package/dist/types/visit/rewrite.d.ts +10 -0
- package/dist/types/visit/visitors.d.ts +33 -0
- package/dist/types/visit/walk.d.ts +7 -0
- package/jsconfig.json +11 -0
- package/package.json +6 -4
- package/src/ast/aggregate.js +164 -0
- package/src/ast/between-op.js +75 -0
- package/src/ast/binary-op.js +40 -0
- package/src/ast/case.js +105 -0
- package/src/ast/cast.js +34 -0
- package/src/ast/column-param.js +29 -0
- package/src/ast/column-ref.js +72 -0
- package/src/ast/fragment.js +26 -0
- package/src/ast/from.js +40 -0
- package/src/ast/function.js +34 -0
- package/src/ast/in-op.js +33 -0
- package/src/ast/interval.js +33 -0
- package/src/ast/literal.js +55 -0
- package/src/ast/logical-op.js +67 -0
- package/src/ast/node.js +29 -0
- package/src/ast/order-by.js +48 -0
- package/src/ast/param.js +35 -0
- package/src/ast/query.js +578 -0
- package/src/ast/sample.js +53 -0
- package/src/ast/select.js +44 -0
- package/src/ast/table-ref.js +44 -0
- package/src/ast/unary-op.js +64 -0
- package/src/ast/verbatim.js +26 -0
- package/src/ast/window.js +290 -0
- package/src/ast/with.js +30 -0
- package/src/constants.js +44 -0
- package/src/functions/aggregate.js +335 -0
- package/src/functions/case.js +21 -0
- package/src/functions/cast.js +39 -0
- package/src/functions/column.js +20 -0
- package/src/functions/datetime.js +65 -0
- package/src/functions/literal.js +22 -0
- package/src/functions/numeric.js +139 -0
- package/src/functions/operators.js +298 -0
- package/src/functions/order-by.js +24 -0
- package/src/functions/spatial.js +56 -0
- package/src/functions/sql-template-tag.js +51 -0
- package/src/functions/string.js +82 -0
- package/src/functions/table-ref.js +14 -0
- package/src/functions/window.js +121 -0
- package/src/index-types.ts +2 -0
- package/src/index.js +57 -155
- package/src/load/create.js +10 -2
- package/src/load/load.js +4 -4
- package/src/load/sql-from.js +7 -6
- package/src/transforms/bin-1d.js +21 -0
- package/src/transforms/bin-2d.js +29 -0
- package/src/transforms/bin-linear-1d.js +26 -0
- package/src/transforms/bin-linear-2d.js +71 -0
- package/src/transforms/line-density.js +113 -0
- package/src/transforms/m4.js +38 -0
- package/src/{scales.js → transforms/scales.js} +31 -17
- package/src/types.ts +96 -0
- package/src/util/ast.js +96 -0
- package/src/util/function.js +78 -0
- package/src/util/string.js +16 -0
- package/src/util/type-check.js +29 -0
- package/src/visit/recurse.js +57 -0
- package/src/visit/rewrite.js +32 -0
- package/src/visit/visitors.js +108 -0
- package/src/visit/walk.js +30 -0
- package/tsconfig.json +12 -0
- package/src/Query.js +0 -593
- package/src/aggregates.js +0 -185
- package/src/cast.js +0 -19
- package/src/datetime.js +0 -31
- package/src/desc.js +0 -13
- package/src/expression.js +0 -170
- package/src/functions.js +0 -25
- package/src/literal.js +0 -6
- package/src/operators.js +0 -54
- package/src/ref.js +0 -109
- package/src/repeat.js +0 -3
- package/src/spatial.js +0 -10
- package/src/to-sql.js +0 -52
- package/src/windows.js +0 -239
package/src/ast/query.js
ADDED
|
@@ -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
|
+
}
|