@uwdata/mosaic-sql 0.12.2 → 0.13.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/README.md CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@uwdata/mosaic-sql.svg)](https://www.npmjs.com/package/@uwdata/mosaic-sql)
4
4
 
5
- An API for convenient construction and analysis of SQL queries. Query objects then coerce to SQL query strings.
5
+ An API for convenient construction and analysis of SQL queries. Constructed `Query` objects coerce to SQL query strings.
6
6
 
7
7
  Many `mosaic-sql` utilities are included as part of the [vgplot](https://github.com/uwdata/mosaic/tree/main/packages/vgplot) API.
@@ -17,6 +17,12 @@ export function isSelectQuery(value: any): value is SelectQuery;
17
17
  */
18
18
  export function isDescribeQuery(value: any): value is DescribeQuery;
19
19
  export class Query extends ExprNode {
20
+ /**
21
+ * Create a new WITH clause with the given CTE queries.
22
+ * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
23
+ * @returns {WithClause}
24
+ */
25
+ static with(...expr: import("../types.js").WithExpr[]): WithClause;
20
26
  /**
21
27
  * Create a new select query with the given SELECT expressions.
22
28
  * @param {...import('../types.js').SelectExpr} expr The SELECT expressions.
@@ -29,12 +35,6 @@ export class Query extends ExprNode {
29
35
  * @returns {SelectQuery}
30
36
  */
31
37
  static from(...expr: import("../types.js").FromExpr[]): SelectQuery;
32
- /**
33
- * Create a new select query with the given WITH CTE queries.
34
- * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
35
- * @returns {SelectQuery}
36
- */
37
- static with(...expr: import("../types.js").WithExpr[]): SelectQuery;
38
38
  /**
39
39
  * Create a new UNION set operation over the given queries.
40
40
  * @param {...Query} queries The queries.
@@ -69,6 +69,8 @@ export class Query extends ExprNode {
69
69
  * Instantiate a new query.
70
70
  */
71
71
  constructor(type: any);
72
+ /** @type {WithClauseNode[]} */
73
+ _with: WithClauseNode[];
72
74
  /** @type {ExprNode[]} */
73
75
  _orderby: ExprNode[];
74
76
  /** @type {number} */
@@ -87,6 +89,12 @@ export class Query extends ExprNode {
87
89
  * @returns {Query}
88
90
  */
89
91
  clone(): Query;
92
+ /**
93
+ * Add WITH common table expressions (CTEs).
94
+ * @param {...import('../types.js').WithExpr} expr Expressions to add.
95
+ * @returns {this}
96
+ */
97
+ with(...expr: import("../types.js").WithExpr[]): this;
90
98
  /**
91
99
  * Add ORDER BY expressions.
92
100
  * @param {...import('../types.js').OrderByExpr} expr Expressions to add.
@@ -111,8 +119,6 @@ export class SelectQuery extends Query {
111
119
  * Instantiate a new select query.
112
120
  */
113
121
  constructor();
114
- /** @type {WithClauseNode[]} */
115
- _with: WithClauseNode[];
116
122
  /** @type {SelectClauseNode[]} */
117
123
  _select: SelectClauseNode[];
118
124
  /** @type {FromClauseNode[]} */
@@ -134,12 +140,6 @@ export class SelectQuery extends Query {
134
140
  * @returns {SelectQuery}
135
141
  */
136
142
  clone(): SelectQuery;
137
- /**
138
- * Add WITH common table expressions (CTEs).
139
- * @param {...import('../types.js').WithExpr} expr Expressions to add.
140
- * @returns {this}
141
- */
142
- with(...expr: import("../types.js").WithExpr[]): this;
143
143
  /**
144
144
  * Add SELECT expressions.
145
145
  * @param {...import('../types.js').SelectExpr} expr Expressions to add.
@@ -261,8 +261,53 @@ export class SetOperation extends Query {
261
261
  }
262
262
  import { ExprNode } from './node.js';
263
263
  import { WithClauseNode } from './with.js';
264
+ declare class WithClause {
265
+ /**
266
+ * Instantiate a new WITH clause instance.
267
+ * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
268
+ */
269
+ constructor(...expr: import("../types.js").WithExpr[]);
270
+ _with: import("../types.js").WithExpr[];
271
+ /**
272
+ * Create a new select query with the given SELECT expressions.
273
+ * @param {...import('../types.js').SelectExpr} expr The SELECT expressions.
274
+ * @returns {SelectQuery}
275
+ */
276
+ select(...expr: import("../types.js").SelectExpr[]): SelectQuery;
277
+ /**
278
+ * Create a new select query with the given FROM expressions.
279
+ * @param {...import('../types.js').FromExpr} expr The FROM expressions.
280
+ * @returns {SelectQuery}
281
+ */
282
+ from(...expr: import("../types.js").FromExpr[]): SelectQuery;
283
+ /**
284
+ * Create a new UNION set operation over the given queries.
285
+ * @param {...Query} queries The queries.
286
+ * @returns {SetOperation}
287
+ */
288
+ union(...queries: Query[]): SetOperation;
289
+ /**
290
+ * Create a new UNION ALL set operation over the given queries.
291
+ * @param {...Query} queries The queries.
292
+ * @returns {SetOperation}
293
+ */
294
+ unionAll(...queries: Query[]): SetOperation;
295
+ /**
296
+ * Create a new INTERSECT set operation over the given queries.
297
+ * @param {...Query} queries The queries.
298
+ * @returns {SetOperation}
299
+ */
300
+ intersect(...queries: Query[]): SetOperation;
301
+ /**
302
+ * Create a new EXCEPT set operation over the given queries.
303
+ * @param {...Query} queries The queries.
304
+ * @returns {SetOperation}
305
+ */
306
+ except(...queries: Query[]): SetOperation;
307
+ }
264
308
  import { SelectClauseNode } from './select.js';
265
309
  import { FromClauseNode } from './from.js';
266
310
  import { SampleClauseNode } from './sample.js';
267
311
  import { WindowClauseNode } from './window.js';
268
312
  import { SQLNode } from './node.js';
313
+ export {};
@@ -3,8 +3,12 @@ export class WithClauseNode extends SQLNode {
3
3
  * Instantiate a with clause node for a common table expression (CTE).
4
4
  * @param {string} name The common table expression (CTE) name.
5
5
  * @param {Query} query The common table expression (CTE) query.
6
+ * @param {boolean | null} [materialized] The common table expression (CTE)
7
+ * materialization flag. If `true`, forces materialization of the CTE.
8
+ * If `false`, materialization is not performed. Otherwise (for example, if
9
+ * `undefined` or `null`), materialization is decided by the database.
6
10
  */
7
- constructor(name: string, query: Query);
11
+ constructor(name: string, query: Query, materialized?: boolean | null);
8
12
  /**
9
13
  * The common table expression (CTE) name.
10
14
  * @type {string}
@@ -17,6 +21,12 @@ export class WithClauseNode extends SQLNode {
17
21
  * @readonly
18
22
  */
19
23
  readonly query: Query;
24
+ /**
25
+ * The common table expression (CTE) materialization flag.
26
+ * @type {boolean | null}
27
+ * @readonly
28
+ */
29
+ readonly materialized: boolean | null;
20
30
  }
21
31
  import { SQLNode } from './node.js';
22
32
  import { Query } from './query.js';
@@ -65,6 +65,12 @@ export function entropy(expr: import("../types.js").ExprValue): AggregateNode;
65
65
  * @returns {AggregateNode} A SQL aggregate function call.
66
66
  */
67
67
  export function first(expr: import("../types.js").ExprValue): AggregateNode;
68
+ /**
69
+ * Compute a geomean aggregate.
70
+ * @param {import('../types.js').ExprValue} expr The expression to aggregate.
71
+ * @returns {AggregateNode} A SQL aggregate function call.
72
+ */
73
+ export function geomean(expr: import("../types.js").ExprValue): AggregateNode;
68
74
  /**
69
75
  * Compute a sample kurtosis aggregate.
70
76
  * @param {import('../types.js').ExprValue} expr The expression to aggregate.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Create a common table expression (CTE) to include within a WITH clause.
3
+ * @param {string} name The common table expression (CTE) name.
4
+ * @param {Query} query The common table expression (CTE) query.
5
+ * @param {boolean | null} [materialized] The common table expression (CTE)
6
+ * materialization flag. If `true`, forces materialization of the CTE.
7
+ * If `false`, materialization is not performed. Otherwise (for example, if
8
+ * `undefined` or `null`), materialization is decided by the database.
9
+ * @returns {WithClauseNode}
10
+ */
11
+ export function cte(name: string, query: Query, materialized?: boolean | null): WithClauseNode;
12
+ import { Query } from '../ast/query.js';
13
+ import { WithClauseNode } from '../ast/with.js';
@@ -15,6 +15,7 @@ export { VerbatimNode } from "./ast/verbatim.js";
15
15
  export { WithClauseNode } from "./ast/with.js";
16
16
  export { cond } from "./functions/case.js";
17
17
  export { column } from "./functions/column.js";
18
+ export { cte } from "./functions/cte.js";
18
19
  export { literal } from "./functions/literal.js";
19
20
  export { sql } from "./functions/sql-template-tag.js";
20
21
  export { rewrite } from "./visit/rewrite.js";
@@ -38,7 +39,7 @@ export { DescribeQuery, Query, SelectQuery, SetOperation, isDescribeQuery, isQue
38
39
  export { TableRefNode, isTableRef } from "./ast/table-ref.js";
39
40
  export { UnaryOpNode, UnaryPosftixOpNode } from "./ast/unary-op.js";
40
41
  export { WindowClauseNode, WindowDefNode, WindowFrameNode, WindowFunctionNode, WindowNode } from "./ast/window.js";
41
- export { argmax, argmin, arrayAgg, avg, corr, count, covariance, covarPop, entropy, first, kurtosis, mad, max, median, min, mode, last, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from "./functions/aggregate.js";
42
+ export { argmax, argmin, arrayAgg, avg, corr, count, covariance, covarPop, entropy, first, geomean, kurtosis, mad, max, median, min, mode, last, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from "./functions/aggregate.js";
42
43
  export { cast, float32, float64, int32 } from "./functions/cast.js";
43
44
  export { dateBin, dateMonth, dateMonthDay, dateDay, epoch_ms, interval } from "./functions/datetime.js";
44
45
  export { abs, ceil, exp, floor, greatest, isFinite, isInfinite, isNaN, least, ln, log, round, sign, sqrt, trunc } from "./functions/numeric.js";
@@ -2,6 +2,7 @@ import { ColumnRefNode } from './ast/column-ref.js';
2
2
  import { ExprNode, SQLNode } from './ast/node.js';
3
3
  import { TableRefNode } from './ast/table-ref.js';
4
4
  import { Query } from './ast/query.js';
5
+ import { WithClauseNode } from './ast/with.js';
5
6
  /**
6
7
  * Interface representing a dynamic parameter value.
7
8
  */
@@ -51,7 +52,8 @@ export type WindowFunctionName = 'cume_dist' | 'dense_rank' | 'first_value' | 'l
51
52
  export type MaybeArray<T> = T | T[];
52
53
  export type SelectEntry = string | ColumnRefNode | [string, ExprNode] | Record<string, ExprValue>;
53
54
  export type SelectExpr = MaybeArray<SelectEntry>;
54
- export type WithExpr = MaybeArray<Record<string, Query>>;
55
+ export type WithEntry = WithClauseNode | Record<string, Query>;
56
+ export type WithExpr = MaybeArray<WithEntry>;
55
57
  export type FromEntry = string | TableRefNode | SQLNode | [string, SQLNode] | Record<string, string | SQLNode>;
56
58
  export type FromExpr = MaybeArray<FromEntry>;
57
59
  export type FilterExpr = MaybeArray<string | ExprNode>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-sql",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "SQL query construction and analysis.",
5
5
  "keywords": [
6
6
  "sql",
@@ -10,22 +10,22 @@
10
10
  "license": "BSD-3-Clause",
11
11
  "author": "Jeffrey Heer (https://idl.uw.edu)",
12
12
  "type": "module",
13
- "main": "src/index.js",
14
- "module": "src/index.js",
15
- "jsdelivr": "dist/mosaic-sql.min.js",
16
- "unpkg": "dist/mosaic-sql.min.js",
17
- "types": "dist/types/index-types.d.ts",
13
+ "exports": {
14
+ "types": "./dist/types/index-types.d.ts",
15
+ "default": "./src/index.js"
16
+ },
18
17
  "repository": {
19
18
  "type": "git",
20
19
  "url": "https://github.com/uwdata/mosaic.git"
21
20
  },
22
21
  "scripts": {
23
22
  "prebuild": "rimraf dist && mkdir dist",
24
- "build": "npm run types && node ../../esbuild.js mosaic-sql",
23
+ "build": "npm run types",
25
24
  "types": "tsc",
26
25
  "lint": "eslint src test",
27
- "test": "vitest run && tsc -p jsconfig.json",
26
+ "test": "vitest run && npm run tsc",
27
+ "tsc": "tsc -p jsconfig.json",
28
28
  "prepublishOnly": "npm run test && npm run lint && npm run build"
29
29
  },
30
- "gitHead": "0ca741d840b98039255f26a5ceedf10be66f790e"
30
+ "gitHead": "b5a0e03e200c0f04c46562a288f084ffc9f6ad55"
31
31
  }
@@ -30,7 +30,7 @@ export function literalToSQL(value) {
30
30
  case 'number':
31
31
  return Number.isFinite(value) ? `${value}` : 'NULL';
32
32
  case 'string':
33
- return `'${value.replace(`'`, `''`)}'`;
33
+ return `'${value.replaceAll(`'`, `''`)}'`;
34
34
  case 'boolean':
35
35
  return value ? 'TRUE' : 'FALSE';
36
36
  default:
package/src/ast/query.js CHANGED
@@ -40,6 +40,15 @@ export function isDescribeQuery(value) {
40
40
  }
41
41
 
42
42
  export class Query extends ExprNode {
43
+ /**
44
+ * Create a new WITH clause with the given CTE queries.
45
+ * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
46
+ * @returns {WithClause}
47
+ */
48
+ static with(...expr) {
49
+ return new WithClause(...expr);
50
+ }
51
+
43
52
  /**
44
53
  * Create a new select query with the given SELECT expressions.
45
54
  * @param {...import('../types.js').SelectExpr} expr The SELECT expressions.
@@ -58,15 +67,6 @@ export class Query extends ExprNode {
58
67
  return new SelectQuery().from(...expr);
59
68
  }
60
69
 
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
70
  /**
71
71
  * Create a new UNION set operation over the given queries.
72
72
  * @param {...Query} queries The queries.
@@ -117,6 +117,8 @@ export class Query extends ExprNode {
117
117
  */
118
118
  constructor(type) {
119
119
  super(type);
120
+ /** @type {WithClauseNode[]} */
121
+ this._with = [];
120
122
  /** @type {ExprNode[]} */
121
123
  this._orderby = [];
122
124
  /** @type {number} */
@@ -143,6 +145,27 @@ export class Query extends ExprNode {
143
145
  return this;
144
146
  }
145
147
 
148
+ /**
149
+ * Add WITH common table expressions (CTEs).
150
+ * @param {...import('../types.js').WithExpr} expr Expressions to add.
151
+ * @returns {this}
152
+ */
153
+ with(...expr) {
154
+ /** @type {WithClauseNode[]} */
155
+ const list = [];
156
+ const add = (name, q) => {
157
+ const query = q.clone();
158
+ query.cteFor = this;
159
+ list.push(new WithClauseNode(name, query));
160
+ };
161
+ expr.flat().forEach(e => {
162
+ if (e instanceof WithClauseNode) list.push(e);
163
+ else if (e != null) for (const name in e) add(name, e[name]);
164
+ });
165
+ this._with = this._with.concat(list);
166
+ return this;
167
+ }
168
+
146
169
  /**
147
170
  * Add ORDER BY expressions.
148
171
  * @param {...import('../types.js').OrderByExpr} expr Expressions to add.
@@ -180,8 +203,6 @@ export class SelectQuery extends Query {
180
203
  */
181
204
  constructor() {
182
205
  super(SELECT_QUERY);
183
- /** @type {WithClauseNode[]} */
184
- this._with = [];
185
206
  /** @type {SelectClauseNode[]} */
186
207
  this._select = [];
187
208
  /** @type {FromClauseNode[]} */
@@ -232,26 +253,6 @@ export class SelectQuery extends Query {
232
253
  return Object.assign(new SelectQuery(), this);
233
254
  }
234
255
 
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
  /**
256
257
  * Add SELECT expressions.
257
258
  * @param {...import('../types.js').SelectExpr} expr Expressions to add.
@@ -559,10 +560,14 @@ export class SetOperation extends Query {
559
560
  * @returns {string}
560
561
  */
561
562
  toString() {
562
- const { op, queries, _orderby, _limit, _offset } = this;
563
+ const { op, queries, _with, _orderby, _limit, _offset } = this;
564
+ const sql = [];
565
+
566
+ // WITH
567
+ if (_with.length) sql.push(`WITH ${_with.join(', ')}`);
563
568
 
564
569
  // SUBQUERIES
565
- const sql = [ queries.join(` ${op} `) ];
570
+ sql.push(queries.join(` ${op} `));
566
571
 
567
572
  // ORDER BY
568
573
  if (_orderby.length) sql.push(`ORDER BY ${_orderby.join(', ')}`);
@@ -576,3 +581,67 @@ export class SetOperation extends Query {
576
581
  return sql.join(' ');
577
582
  }
578
583
  }
584
+
585
+ class WithClause {
586
+ /**
587
+ * Instantiate a new WITH clause instance.
588
+ * @param {...import('../types.js').WithExpr} expr The WITH CTE queries.
589
+ */
590
+ constructor(...expr) {
591
+ this._with = expr;
592
+ }
593
+
594
+ /**
595
+ * Create a new select query with the given SELECT expressions.
596
+ * @param {...import('../types.js').SelectExpr} expr The SELECT expressions.
597
+ * @returns {SelectQuery}
598
+ */
599
+ select(...expr) {
600
+ return Query.select(...expr).with(...this._with);
601
+ }
602
+
603
+ /**
604
+ * Create a new select query with the given FROM expressions.
605
+ * @param {...import('../types.js').FromExpr} expr The FROM expressions.
606
+ * @returns {SelectQuery}
607
+ */
608
+ from(...expr) {
609
+ return Query.from(...expr).with(...this._with);
610
+ }
611
+
612
+ /**
613
+ * Create a new UNION set operation over the given queries.
614
+ * @param {...Query} queries The queries.
615
+ * @returns {SetOperation}
616
+ */
617
+ union(...queries) {
618
+ return Query.union(...queries).with(...this._with);
619
+ }
620
+
621
+ /**
622
+ * Create a new UNION ALL set operation over the given queries.
623
+ * @param {...Query} queries The queries.
624
+ * @returns {SetOperation}
625
+ */
626
+ unionAll(...queries) {
627
+ return Query.unionAll(...queries).with(...this._with);
628
+ }
629
+
630
+ /**
631
+ * Create a new INTERSECT set operation over the given queries.
632
+ * @param {...Query} queries The queries.
633
+ * @returns {SetOperation}
634
+ */
635
+ intersect(...queries) {
636
+ return Query.intersect(...queries).with(...this._with);
637
+ }
638
+
639
+ /**
640
+ * Create a new EXCEPT set operation over the given queries.
641
+ * @param {...Query} queries The queries.
642
+ * @returns {SetOperation}
643
+ */
644
+ except(...queries) {
645
+ return Query.except(...queries).with(...this._with);
646
+ }
647
+ }
package/src/ast/with.js CHANGED
@@ -7,8 +7,12 @@ export class WithClauseNode extends SQLNode {
7
7
  * Instantiate a with clause node for a common table expression (CTE).
8
8
  * @param {string} name The common table expression (CTE) name.
9
9
  * @param {Query} query The common table expression (CTE) query.
10
+ * @param {boolean | null} [materialized] The common table expression (CTE)
11
+ * materialization flag. If `true`, forces materialization of the CTE.
12
+ * If `false`, materialization is not performed. Otherwise (for example, if
13
+ * `undefined` or `null`), materialization is decided by the database.
10
14
  */
11
- constructor(name, query) {
15
+ constructor(name, query, materialized = undefined) {
12
16
  super(WITH_CLAUSE);
13
17
  /**
14
18
  * The common table expression (CTE) name.
@@ -22,9 +26,19 @@ export class WithClauseNode extends SQLNode {
22
26
  * @readonly
23
27
  */
24
28
  this.query = query;
29
+ /**
30
+ * The common table expression (CTE) materialization flag.
31
+ * @type {boolean | null}
32
+ * @readonly
33
+ */
34
+ this.materialized = materialized;
25
35
  }
26
36
 
27
37
  toString() {
28
- return `"${this.name}" AS (${this.query})`;
38
+ const flag = this.materialized;
39
+ const mat = flag === true ? ' MATERIALIZED'
40
+ : flag === false ? ' NOT MATERIALIZED'
41
+ : '';
42
+ return `"${this.name}" AS${mat} (${this.query})`;
29
43
  }
30
44
  }
@@ -98,6 +98,15 @@ export function first(expr) {
98
98
  return aggFn('first', expr);
99
99
  }
100
100
 
101
+ /**
102
+ * Compute a geomean aggregate.
103
+ * @param {import('../types.js').ExprValue} expr The expression to aggregate.
104
+ * @returns {AggregateNode} A SQL aggregate function call.
105
+ */
106
+ export function geomean(expr) {
107
+ return aggFn('geomean', expr);
108
+ }
109
+
101
110
  /**
102
111
  * Compute a sample kurtosis aggregate.
103
112
  * @param {import('../types.js').ExprValue} expr The expression to aggregate.
@@ -0,0 +1,16 @@
1
+ import { Query } from '../ast/query.js';
2
+ import { WithClauseNode } from '../ast/with.js';
3
+
4
+ /**
5
+ * Create a common table expression (CTE) to include within a WITH clause.
6
+ * @param {string} name The common table expression (CTE) name.
7
+ * @param {Query} query The common table expression (CTE) query.
8
+ * @param {boolean | null} [materialized] The common table expression (CTE)
9
+ * materialization flag. If `true`, forces materialization of the CTE.
10
+ * If `false`, materialization is not performed. Otherwise (for example, if
11
+ * `undefined` or `null`), materialization is decided by the database.
12
+ * @returns {WithClauseNode}
13
+ */
14
+ export function cte(name, query, materialized) {
15
+ return new WithClauseNode(name, query, materialized);
16
+ }
package/src/index.js CHANGED
@@ -24,10 +24,11 @@ export { VerbatimNode } from './ast/verbatim.js';
24
24
  export { WindowClauseNode, WindowDefNode, WindowFrameNode, WindowFunctionNode, WindowNode } from './ast/window.js';
25
25
  export { WithClauseNode } from './ast/with.js';
26
26
 
27
- export { argmax, argmin, arrayAgg, avg, corr, count, covariance, covarPop, entropy, first, kurtosis, mad, max, median, min, mode, last, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from './functions/aggregate.js';
27
+ export { argmax, argmin, arrayAgg, avg, corr, count, covariance, covarPop, entropy, first, geomean, kurtosis, mad, max, median, min, mode, last, product, quantile, regrAvgX, regrAvgY, regrCount, regrIntercept, regrR2, regrSXX, regrSXY, regrSYY, regrSlope, skewness, stddev, stddevPop, stringAgg, sum, variance, varPop } from './functions/aggregate.js';
28
28
  export { cond } from './functions/case.js';
29
29
  export { cast, float32, float64, int32 } from './functions/cast.js';
30
30
  export { column } from './functions/column.js';
31
+ export { cte } from './functions/cte.js';
31
32
  export { dateBin, dateMonth, dateMonthDay, dateDay, epoch_ms, interval } from './functions/datetime.js';
32
33
  export { literal } from './functions/literal.js';
33
34
  export { abs, ceil, exp, floor, greatest, isFinite, isInfinite, isNaN, least, ln, log, round, sign, sqrt, trunc } from './functions/numeric.js';
@@ -1,6 +1,7 @@
1
- import { Query } from '../ast/query.js';
1
+ import { Query, isQuery } from '../ast/query.js';
2
2
  import { argmax, argmin, max, min } from '../functions/aggregate.js';
3
3
  import { int32 } from '../functions/cast.js';
4
+ import { cte } from '../functions/cte.js';
4
5
  import { floor } from '../functions/numeric.js';
5
6
 
6
7
  /**
@@ -22,12 +23,20 @@ import { floor } from '../functions/numeric.js';
22
23
  export function m4(input, bin, x, y, groups = []) {
23
24
  const pixel = int32(floor(bin));
24
25
 
26
+ // Below, we treat input as a CTE when it is a query. In this case,
27
+ // we also request that the CTE be explicitly materialized.
28
+ const useCTE = isQuery(input);
29
+ const from = useCTE ? 'input' : input;
30
+ const query = useCTE
31
+ ? Query.with(cte(/** @type {string} */(from), input, true))
32
+ : Query;
33
+
25
34
  const q = (sel) => Query
26
- .from(input)
35
+ .from(from)
27
36
  .select(sel)
28
37
  .groupby(pixel, groups);
29
38
 
30
- return Query
39
+ return query
31
40
  .union(
32
41
  q([{ [x]: min(x), [y]: argmin(y, x) }, ...groups]),
33
42
  q([{ [x]: max(x), [y]: argmax(y, x) }, ...groups]),
package/src/types.ts CHANGED
@@ -2,6 +2,7 @@ import { ColumnRefNode } from './ast/column-ref.js';
2
2
  import { ExprNode, SQLNode } from './ast/node.js';
3
3
  import { TableRefNode } from './ast/table-ref.js';
4
4
  import { Query } from './ast/query.js';
5
+ import { WithClauseNode } from './ast/with.js';
5
6
 
6
7
  /**
7
8
  * Interface representing a dynamic parameter value.
@@ -80,7 +81,11 @@ export type SelectEntry =
80
81
 
81
82
  export type SelectExpr = MaybeArray<SelectEntry>;
82
83
 
83
- export type WithExpr = MaybeArray<Record<string, Query>>;
84
+ export type WithEntry =
85
+ | WithClauseNode
86
+ | Record<string, Query>;
87
+
88
+ export type WithExpr = MaybeArray<WithEntry>;
84
89
 
85
90
  export type FromEntry =
86
91
  | string
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ export default defineConfig({});