@uwdata/mosaic-sql 0.18.0 → 0.20.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/src/ast/aggregate.d.ts +0 -4
- package/dist/src/ast/aggregate.d.ts.map +1 -1
- package/dist/src/ast/aggregate.js +0 -15
- package/dist/src/ast/aggregate.js.map +1 -1
- package/dist/src/ast/between-op.d.ts +0 -8
- package/dist/src/ast/between-op.d.ts.map +1 -1
- package/dist/src/ast/between-op.js +0 -12
- package/dist/src/ast/between-op.js.map +1 -1
- package/dist/src/ast/binary-op.d.ts +0 -4
- package/dist/src/ast/binary-op.d.ts.map +1 -1
- package/dist/src/ast/binary-op.js +0 -6
- package/dist/src/ast/binary-op.js.map +1 -1
- package/dist/src/ast/case.d.ts +0 -8
- package/dist/src/ast/case.d.ts.map +1 -1
- package/dist/src/ast/case.js +0 -16
- package/dist/src/ast/case.js.map +1 -1
- package/dist/src/ast/cast.d.ts +0 -4
- package/dist/src/ast/cast.d.ts.map +1 -1
- package/dist/src/ast/cast.js +0 -7
- package/dist/src/ast/cast.js.map +1 -1
- package/dist/src/ast/collate.d.ts +0 -4
- package/dist/src/ast/collate.d.ts.map +1 -1
- package/dist/src/ast/collate.js +0 -6
- package/dist/src/ast/collate.js.map +1 -1
- package/dist/src/ast/column-ref.d.ts +0 -4
- package/dist/src/ast/column-ref.d.ts.map +1 -1
- package/dist/src/ast/column-ref.js +0 -10
- package/dist/src/ast/column-ref.js.map +1 -1
- package/dist/src/ast/fragment.d.ts +0 -4
- package/dist/src/ast/fragment.d.ts.map +1 -1
- package/dist/src/ast/fragment.js +0 -6
- package/dist/src/ast/fragment.js.map +1 -1
- package/dist/src/ast/from.d.ts +10 -3
- package/dist/src/ast/from.d.ts.map +1 -1
- package/dist/src/ast/from.js +11 -12
- package/dist/src/ast/from.js.map +1 -1
- package/dist/src/ast/function.d.ts +0 -4
- package/dist/src/ast/function.d.ts.map +1 -1
- package/dist/src/ast/function.js +0 -7
- package/dist/src/ast/function.js.map +1 -1
- package/dist/src/ast/in-op.d.ts +0 -4
- package/dist/src/ast/in-op.d.ts.map +1 -1
- package/dist/src/ast/in-op.js +0 -6
- package/dist/src/ast/in-op.js.map +1 -1
- package/dist/src/ast/interval.d.ts +0 -4
- package/dist/src/ast/interval.d.ts.map +1 -1
- package/dist/src/ast/interval.js +0 -6
- package/dist/src/ast/interval.js.map +1 -1
- package/dist/src/ast/join.d.ts +45 -0
- package/dist/src/ast/join.d.ts.map +1 -0
- package/dist/src/ast/join.js +47 -0
- package/dist/src/ast/join.js.map +1 -0
- package/dist/src/ast/list.d.ts +11 -0
- package/dist/src/ast/list.d.ts.map +1 -0
- package/dist/src/ast/list.js +15 -0
- package/dist/src/ast/list.js.map +1 -0
- package/dist/src/ast/literal.d.ts +0 -4
- package/dist/src/ast/literal.d.ts.map +1 -1
- package/dist/src/ast/literal.js +0 -6
- package/dist/src/ast/literal.js.map +1 -1
- package/dist/src/ast/logical-op.d.ts +0 -4
- package/dist/src/ast/logical-op.d.ts.map +1 -1
- package/dist/src/ast/logical-op.js +0 -9
- package/dist/src/ast/logical-op.js.map +1 -1
- package/dist/src/ast/node.d.ts +13 -0
- package/dist/src/ast/node.d.ts.map +1 -1
- package/dist/src/ast/node.js +25 -3
- package/dist/src/ast/node.js.map +1 -1
- package/dist/src/ast/order-by.d.ts +0 -4
- package/dist/src/ast/order-by.d.ts.map +1 -1
- package/dist/src/ast/order-by.js +0 -13
- package/dist/src/ast/order-by.js.map +1 -1
- package/dist/src/ast/param.d.ts +0 -4
- package/dist/src/ast/param.d.ts.map +1 -1
- package/dist/src/ast/param.js +0 -7
- package/dist/src/ast/param.js.map +1 -1
- package/dist/src/ast/query.d.ts +43 -15
- package/dist/src/ast/query.d.ts.map +1 -1
- package/dist/src/ast/query.js +71 -92
- package/dist/src/ast/query.js.map +1 -1
- package/dist/src/ast/sample.d.ts +0 -4
- package/dist/src/ast/sample.d.ts.map +1 -1
- package/dist/src/ast/sample.js +0 -9
- package/dist/src/ast/sample.js.map +1 -1
- package/dist/src/ast/select.d.ts +0 -4
- package/dist/src/ast/select.d.ts.map +1 -1
- package/dist/src/ast/select.js +0 -16
- package/dist/src/ast/select.js.map +1 -1
- package/dist/src/ast/subquery.d.ts +0 -4
- package/dist/src/ast/subquery.d.ts.map +1 -1
- package/dist/src/ast/subquery.js +0 -6
- package/dist/src/ast/subquery.js.map +1 -1
- package/dist/src/ast/table-ref.d.ts +0 -4
- package/dist/src/ast/table-ref.d.ts.map +1 -1
- package/dist/src/ast/table-ref.js +0 -7
- package/dist/src/ast/table-ref.js.map +1 -1
- package/dist/src/ast/unary-op.d.ts +0 -8
- package/dist/src/ast/unary-op.d.ts.map +1 -1
- package/dist/src/ast/unary-op.js +0 -12
- package/dist/src/ast/unary-op.js.map +1 -1
- package/dist/src/ast/unnest.d.ts +15 -0
- package/dist/src/ast/unnest.d.ts.map +1 -0
- package/dist/src/ast/unnest.js +21 -0
- package/dist/src/ast/unnest.js.map +1 -0
- package/dist/src/ast/verbatim.d.ts +0 -4
- package/dist/src/ast/verbatim.d.ts.map +1 -1
- package/dist/src/ast/verbatim.js +0 -6
- package/dist/src/ast/verbatim.js.map +1 -1
- package/dist/src/ast/window-frame.d.ts +0 -8
- package/dist/src/ast/window-frame.d.ts.map +1 -1
- package/dist/src/ast/window-frame.js +1 -29
- package/dist/src/ast/window-frame.js.map +1 -1
- package/dist/src/ast/window.d.ts +0 -16
- package/dist/src/ast/window.d.ts.map +1 -1
- package/dist/src/ast/window.js +0 -39
- package/dist/src/ast/window.js.map +1 -1
- package/dist/src/ast/with.d.ts +0 -4
- package/dist/src/ast/with.d.ts.map +1 -1
- package/dist/src/ast/with.js +0 -10
- package/dist/src/ast/with.js.map +1 -1
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/functions/from.d.ts +11 -0
- package/dist/src/functions/from.d.ts.map +1 -0
- package/dist/src/functions/from.js +12 -0
- package/dist/src/functions/from.js.map +1 -0
- package/dist/src/functions/join.d.ts +49 -0
- package/dist/src/functions/join.d.ts.map +1 -0
- package/dist/src/functions/join.js +50 -0
- package/dist/src/functions/join.js.map +1 -0
- package/dist/src/functions/list.d.ts +31 -0
- package/dist/src/functions/list.d.ts.map +1 -0
- package/dist/src/functions/list.js +49 -0
- package/dist/src/functions/list.js.map +1 -0
- package/dist/src/functions/unnest.d.ts +10 -0
- package/dist/src/functions/unnest.d.ts.map +1 -0
- package/dist/src/functions/unnest.js +12 -0
- package/dist/src/functions/unnest.js.map +1 -0
- package/dist/src/index.d.ts +11 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/init.d.ts +2 -0
- package/dist/src/init.d.ts.map +1 -0
- package/dist/src/init.js +5 -0
- package/dist/src/init.js.map +1 -0
- package/dist/src/transforms/filter-query.d.ts.map +1 -1
- package/dist/src/transforms/filter-query.js +2 -0
- package/dist/src/transforms/filter-query.js.map +1 -1
- package/dist/src/visit/codegen/duckdb.d.ts +49 -0
- package/dist/src/visit/codegen/duckdb.d.ts.map +1 -0
- package/dist/src/visit/codegen/duckdb.js +332 -0
- package/dist/src/visit/codegen/duckdb.js.map +1 -0
- package/dist/src/visit/codegen/sql.d.ts +60 -0
- package/dist/src/visit/codegen/sql.d.ts.map +1 -0
- package/dist/src/visit/codegen/sql.js +85 -0
- package/dist/src/visit/codegen/sql.js.map +1 -0
- package/dist/src/visit/duckdb-visitor.d.ts +50 -0
- package/dist/src/visit/duckdb-visitor.d.ts.map +1 -0
- package/dist/src/visit/duckdb-visitor.js +350 -0
- package/dist/src/visit/duckdb-visitor.js.map +1 -0
- package/dist/src/visit/recurse.d.ts.map +1 -1
- package/dist/src/visit/recurse.js +3 -1
- package/dist/src/visit/recurse.js.map +1 -1
- package/dist/src/visit/to-string-visitor.d.ts +60 -0
- package/dist/src/visit/to-string-visitor.d.ts.map +1 -0
- package/dist/src/visit/to-string-visitor.js +80 -0
- package/dist/src/visit/to-string-visitor.js.map +1 -0
- package/package.json +2 -2
- package/src/ast/aggregate.ts +0 -16
- package/src/ast/between-op.ts +0 -14
- package/src/ast/binary-op.ts +0 -7
- package/src/ast/case.ts +0 -18
- package/src/ast/cast.ts +0 -8
- package/src/ast/collate.ts +0 -7
- package/src/ast/column-ref.ts +0 -11
- package/src/ast/fragment.ts +0 -7
- package/src/ast/from.ts +12 -12
- package/src/ast/function.ts +0 -8
- package/src/ast/in-op.ts +0 -7
- package/src/ast/interval.ts +0 -7
- package/src/ast/join.ts +66 -0
- package/src/ast/list.ts +16 -0
- package/src/ast/literal.ts +0 -7
- package/src/ast/logical-op.ts +0 -10
- package/src/ast/node.ts +30 -3
- package/src/ast/order-by.ts +0 -14
- package/src/ast/param.ts +0 -8
- package/src/ast/query.ts +80 -104
- package/src/ast/sample.ts +0 -10
- package/src/ast/select.ts +0 -18
- package/src/ast/subquery.ts +0 -7
- package/src/ast/table-ref.ts +0 -8
- package/src/ast/unary-op.ts +0 -14
- package/src/ast/unnest.ts +22 -0
- package/src/ast/verbatim.ts +0 -7
- package/src/ast/window-frame.ts +1 -32
- package/src/ast/window.ts +0 -43
- package/src/ast/with.ts +0 -11
- package/src/constants.ts +4 -0
- package/src/functions/from.ts +18 -0
- package/src/functions/join.ts +101 -0
- package/src/functions/list.ts +63 -0
- package/src/functions/unnest.ts +13 -0
- package/src/index.ts +13 -1
- package/src/init.ts +5 -0
- package/src/transforms/filter-query.ts +2 -0
- package/src/visit/codegen/duckdb.ts +444 -0
- package/src/visit/codegen/sql.ts +213 -0
- package/src/visit/recurse.ts +4 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ColumnRefNode } from '../ast/column-ref.js';
|
|
2
|
+
import { FromNode } from '../ast/from.js';
|
|
3
|
+
import { JoinNode, type JoinType, type JoinVariant } from '../ast/join.js';
|
|
4
|
+
import { ExprNode } from '../ast/node.js';
|
|
5
|
+
import { asNode, asTableRef } from '../util/ast.js';
|
|
6
|
+
|
|
7
|
+
type TableArg = string | string[] | FromNode;
|
|
8
|
+
|
|
9
|
+
/** Options for a JOIN operation. */
|
|
10
|
+
interface JoinOptions {
|
|
11
|
+
/** The join type (INNER, LEFT, RIGHT, FULL, SEMI, ANTI). */
|
|
12
|
+
type?: JoinType;
|
|
13
|
+
/**
|
|
14
|
+
* The join condition as a boolean expression.
|
|
15
|
+
* If specified, the *using* option must not be specified.
|
|
16
|
+
*/
|
|
17
|
+
on?: ExprNode;
|
|
18
|
+
/**
|
|
19
|
+
* The join condition as an array of columns to match.
|
|
20
|
+
* The column names must exist in both tables.
|
|
21
|
+
* If specified, the *on* option must not be specified.
|
|
22
|
+
*/
|
|
23
|
+
using?: (string | ColumnRefNode)[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tableRef(x: TableArg) {
|
|
27
|
+
return x instanceof FromNode ? x : asTableRef(x)!;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeJoin(
|
|
31
|
+
left: TableArg,
|
|
32
|
+
right: TableArg,
|
|
33
|
+
variant?: JoinVariant,
|
|
34
|
+
options: JoinOptions = {}
|
|
35
|
+
) {
|
|
36
|
+
if (options.on && options.using) {
|
|
37
|
+
throw new Error('Only one join condition (on or using) can be applied.');
|
|
38
|
+
}
|
|
39
|
+
return new JoinNode(
|
|
40
|
+
tableRef(left),
|
|
41
|
+
tableRef(right),
|
|
42
|
+
variant,
|
|
43
|
+
options.type,
|
|
44
|
+
options.on,
|
|
45
|
+
options.using?.map(c => asNode(c) as ColumnRefNode)
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a new cross (cartesian product) join.
|
|
51
|
+
* @param left The left table to join.
|
|
52
|
+
* @param right The right table to join.
|
|
53
|
+
*/
|
|
54
|
+
export function cross_join(left: TableArg, right: TableArg) {
|
|
55
|
+
return makeJoin(left, right, 'CROSS');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create a new POSITIONAL join.
|
|
60
|
+
* @param left The left table to join.
|
|
61
|
+
* @param right The right table to join.
|
|
62
|
+
*/
|
|
63
|
+
export function positional_join(left: TableArg, right: TableArg) {
|
|
64
|
+
return makeJoin(left, right, 'POSITIONAL');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a new join.
|
|
69
|
+
* @param left The left table to join.
|
|
70
|
+
* @param right The right table to join.
|
|
71
|
+
* @param options The join options.
|
|
72
|
+
*/
|
|
73
|
+
export function join(
|
|
74
|
+
left: TableArg,
|
|
75
|
+
right: TableArg,
|
|
76
|
+
options?: JoinOptions
|
|
77
|
+
) {
|
|
78
|
+
return makeJoin(
|
|
79
|
+
left,
|
|
80
|
+
right,
|
|
81
|
+
options?.on || options?.using ? 'REGULAR' : 'NATURAL',
|
|
82
|
+
options
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Create a new ASOF join.
|
|
88
|
+
* @param left The left table to join.
|
|
89
|
+
* @param right The right table to join.
|
|
90
|
+
* @param options The join options.
|
|
91
|
+
*/
|
|
92
|
+
export function asof_join(
|
|
93
|
+
left: TableArg,
|
|
94
|
+
right: TableArg,
|
|
95
|
+
options: JoinOptions
|
|
96
|
+
) {
|
|
97
|
+
if (!(options.on || options.using)) {
|
|
98
|
+
throw new Error('ASOF join requires a join condition.');
|
|
99
|
+
}
|
|
100
|
+
return makeJoin(left, right, 'ASOF', options);
|
|
101
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ExprValue } from "../types.js";
|
|
2
|
+
import { asLiteral, asNode } from "../util/ast.js";
|
|
3
|
+
import { argsList, fn } from "../util/function.js";
|
|
4
|
+
import { ListNode } from "../ast/list.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a List containing the argument values.
|
|
8
|
+
* @param values
|
|
9
|
+
*/
|
|
10
|
+
export function list(values: ExprValue[]) {
|
|
11
|
+
return new ListNode(argsList(values).map(asLiteral));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Convert a single value or an array of values to either a ListNode if the input
|
|
16
|
+
* is an array, or a single ExprNode if it is a single value. A single string will
|
|
17
|
+
* be interpreted as a column reference.
|
|
18
|
+
* @param values
|
|
19
|
+
*/
|
|
20
|
+
function asList(values: ExprValue | ExprValue[]) {
|
|
21
|
+
return Array.isArray(values) ? list(values) : asNode(values);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Function that returns true if the list contains the element,
|
|
26
|
+
* false otherwise. If the first argument is a string, it is
|
|
27
|
+
* interpreted as a column reference, otherwise it is coerced
|
|
28
|
+
* to a list.
|
|
29
|
+
* @param list1
|
|
30
|
+
* @param element
|
|
31
|
+
*/
|
|
32
|
+
export function listContains(
|
|
33
|
+
list1: ExprValue | ExprValue[],
|
|
34
|
+
element: ExprValue,
|
|
35
|
+
) {
|
|
36
|
+
return fn("list_contains", asList(list1), asLiteral(element));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Function that returns true if all elements of list2 exist in list1,
|
|
41
|
+
* false otherwise.
|
|
42
|
+
* @param list1
|
|
43
|
+
* @param list2
|
|
44
|
+
*/
|
|
45
|
+
export function listHasAll(
|
|
46
|
+
list1: ExprValue | ExprValue[],
|
|
47
|
+
list2: ExprValue | ExprValue[],
|
|
48
|
+
) {
|
|
49
|
+
return fn("list_has_all", asList(list1), asList(list2));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Function that returns true if any elements exist in both lists,
|
|
54
|
+
* false otherwise.
|
|
55
|
+
* @param list1
|
|
56
|
+
* @param list2
|
|
57
|
+
*/
|
|
58
|
+
export function listHasAny(
|
|
59
|
+
list1: ExprValue | ExprValue[],
|
|
60
|
+
list2: ExprValue | ExprValue[],
|
|
61
|
+
) {
|
|
62
|
+
return fn("list_has_any", asList(list1), asList(list2));
|
|
63
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExprValue } from "../types.js";
|
|
2
|
+
import { asNode } from "../util/ast.js";
|
|
3
|
+
import { UnnestNode } from "../ast/unnest.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create an Unnest Node to flatten nested structures, either structs or lists.
|
|
7
|
+
* @param value
|
|
8
|
+
* @param recursive
|
|
9
|
+
* @param maxDepth
|
|
10
|
+
*/
|
|
11
|
+
export function unnest(value: ExprValue, recursive = false, maxDepth = 0) {
|
|
12
|
+
return new UnnestNode(asNode(value), recursive, maxDepth);
|
|
13
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Initialize default visitor
|
|
2
|
+
import './init.js';
|
|
3
|
+
|
|
1
4
|
export { AggregateNode, aggregateNames, isAggregateFunction } from './ast/aggregate.js';
|
|
2
5
|
export { BetweenOpNode, NotBetweenOpNode } from './ast/between-op.js'
|
|
3
6
|
export { BinaryOpNode } from './ast/binary-op.js';
|
|
@@ -11,8 +14,10 @@ export { FromClauseNode } from './ast/from.js';
|
|
|
11
14
|
export { FunctionNode } from './ast/function.js';
|
|
12
15
|
export { InOpNode } from './ast/in-op.js';
|
|
13
16
|
export { IntervalNode } from './ast/interval.js';
|
|
17
|
+
export { JoinNode, type JoinType, type JoinVariant } from './ast/join.js';
|
|
18
|
+
export { ListNode } from './ast/list.js';
|
|
14
19
|
export { LiteralNode } from './ast/literal.js';
|
|
15
|
-
export { AndNode, OrNode } from './ast/logical-op.js';
|
|
20
|
+
export { LogicalOpNode, AndNode, OrNode } from './ast/logical-op.js';
|
|
16
21
|
export { SQLNode, ExprNode, isNode } from './ast/node.js';
|
|
17
22
|
export { OrderByNode } from './ast/order-by.js';
|
|
18
23
|
export { ParamNode } from './ast/param.js';
|
|
@@ -22,6 +27,7 @@ export { ScalarSubqueryNode } from './ast/subquery.js';
|
|
|
22
27
|
export { SelectClauseNode } from './ast/select.js';
|
|
23
28
|
export { TableRefNode, isTableRef } from './ast/table-ref.js';
|
|
24
29
|
export { UnaryOpNode, UnaryPostfixOpNode } from './ast/unary-op.js';
|
|
30
|
+
export { UnnestNode } from './ast/unnest.js';
|
|
25
31
|
export { VerbatimNode } from './ast/verbatim.js';
|
|
26
32
|
export { WindowClauseNode, WindowDefNode, WindowFunctionNode, WindowNode } from './ast/window.js';
|
|
27
33
|
export { WindowFrameNode, WindowFrameExprNode, type FrameExclude, type FrameExtent, type FrameScope, type FrameType, type FrameValue } from './ast/window-frame.js';
|
|
@@ -34,7 +40,10 @@ export { collate } from './functions/collate.js';
|
|
|
34
40
|
export { column } from './functions/column.js';
|
|
35
41
|
export { cte } from './functions/cte.js';
|
|
36
42
|
export { dateBin, dateMonth, dateMonthDay, dateDay, epoch_ms } from './functions/datetime.js';
|
|
43
|
+
export { from } from './functions/from.js';
|
|
37
44
|
export { days, hours, interval, microseconds, minutes, milliseconds, months, seconds, years } from './functions/interval.js';
|
|
45
|
+
export { asof_join, cross_join, join, positional_join } from './functions/join.js';
|
|
46
|
+
export { list, listContains, listHasAll, listHasAny } from './functions/list.js';
|
|
38
47
|
export { literal, verbatim } from './functions/literal.js';
|
|
39
48
|
export { abs, ceil, exp, floor, greatest, isFinite, isInfinite, isNaN, least, ln, log, round, sign, sqrt, trunc } from './functions/numeric.js';
|
|
40
49
|
export { and, or, not, isNull, isNotNull, bitNot, bitAnd, bitOr, bitLeft, bitRight, add, sub, mul, div, idiv, mod, pow, eq, neq, lt, gt, lte, gte, isDistinct, isNotDistinct, isBetween, isNotBetween, isIn } from './functions/operators.js';
|
|
@@ -42,6 +51,7 @@ export { asc, desc } from './functions/order-by.js';
|
|
|
42
51
|
export { geojson, x, y, centroid, centroidX, centroidY } from './functions/spatial.js';
|
|
43
52
|
export { sql } from './functions/sql-template-tag.js';
|
|
44
53
|
export { regexp_matches, contains, prefix, suffix, lower, upper, length } from './functions/string.js';
|
|
54
|
+
export { unnest } from './functions/unnest.js';
|
|
45
55
|
export { coalesce } from './functions/util.js';
|
|
46
56
|
export { cume_dist, dense_rank, first_value, lag, last_value, lead, nth_value, ntile, percent_rank, rank, row_number } from './functions/window.js';
|
|
47
57
|
export { currentRow, following, frameGroups, frameRange, frameRows, preceding } from './functions/window-frame.js';
|
|
@@ -50,6 +60,8 @@ export { deepClone } from './visit/clone.js';
|
|
|
50
60
|
export { rewrite } from './visit/rewrite.js';
|
|
51
61
|
export { collectAggregates, collectColumns, collectParams, isAggregateExpression } from './visit/visitors.js';
|
|
52
62
|
export { walk, type VisitorCallback, type VisitorResult } from './visit/walk.js';
|
|
63
|
+
export { SQLCodeGenerator } from './visit/codegen/sql.js';
|
|
64
|
+
export { DuckDBCodeGenerator, duckDBCodeGenerator } from './visit/codegen/duckdb.js';
|
|
53
65
|
|
|
54
66
|
export { createTable, createSchema } from './load/create.js';
|
|
55
67
|
export { loadExtension } from './load/extension.js';
|
package/src/init.ts
ADDED
|
@@ -3,6 +3,7 @@ import { isSelectQuery, type Query } from '../ast/query.js';
|
|
|
3
3
|
import { isTableRef, type TableRefNode } from '../ast/table-ref.js';
|
|
4
4
|
import { deepClone } from '../visit/clone.js';
|
|
5
5
|
import { walk } from '../visit/walk.js';
|
|
6
|
+
import { FromClauseNode } from '../ast/from.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Returns a generator function that clones the given query and adds
|
|
@@ -18,6 +19,7 @@ export function filterQuery(query: Query, tableRef: TableRefNode) {
|
|
|
18
19
|
if (
|
|
19
20
|
isSelectQuery(node) &&
|
|
20
21
|
node._from.length === 1 &&
|
|
22
|
+
node._from[0] instanceof FromClauseNode &&
|
|
21
23
|
isTableRef(node._from[0].expr) &&
|
|
22
24
|
arrayEquals(node._from[0].expr.table, tableRef.table)
|
|
23
25
|
) {
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SQLNode,
|
|
3
|
+
ExprNode,
|
|
4
|
+
AggregateNode,
|
|
5
|
+
BetweenOpNode,
|
|
6
|
+
NotBetweenOpNode,
|
|
7
|
+
BinaryOpNode,
|
|
8
|
+
CaseNode,
|
|
9
|
+
WhenNode,
|
|
10
|
+
CastNode,
|
|
11
|
+
CollateNode,
|
|
12
|
+
ColumnParamNode,
|
|
13
|
+
ColumnRefNode,
|
|
14
|
+
FragmentNode,
|
|
15
|
+
FromClauseNode,
|
|
16
|
+
FunctionNode,
|
|
17
|
+
InOpNode,
|
|
18
|
+
IntervalNode,
|
|
19
|
+
JoinNode,
|
|
20
|
+
ListNode,
|
|
21
|
+
LiteralNode,
|
|
22
|
+
LogicalOpNode,
|
|
23
|
+
OrderByNode,
|
|
24
|
+
ParamNode,
|
|
25
|
+
DescribeQuery,
|
|
26
|
+
SelectQuery,
|
|
27
|
+
SetOperation,
|
|
28
|
+
SampleClauseNode,
|
|
29
|
+
SelectClauseNode,
|
|
30
|
+
ScalarSubqueryNode,
|
|
31
|
+
TableRefNode,
|
|
32
|
+
UnaryOpNode,
|
|
33
|
+
UnaryPostfixOpNode,
|
|
34
|
+
UnnestNode,
|
|
35
|
+
VerbatimNode,
|
|
36
|
+
WindowNode,
|
|
37
|
+
WindowClauseNode,
|
|
38
|
+
WindowDefNode,
|
|
39
|
+
WindowFunctionNode,
|
|
40
|
+
WindowFrameNode,
|
|
41
|
+
WindowFrameExprNode,
|
|
42
|
+
WithClauseNode,
|
|
43
|
+
isNode,
|
|
44
|
+
isQuery,
|
|
45
|
+
isTableRef
|
|
46
|
+
} from '../../index.js';
|
|
47
|
+
import { quoteIdentifier } from '../../util/string.js';
|
|
48
|
+
import { literalToSQL } from '../../ast/literal.js';
|
|
49
|
+
import { SQLCodeGenerator } from './sql.js';
|
|
50
|
+
import { CURRENT_ROW, FOLLOWING, PRECEDING, UNBOUNDED } from '../../ast/window-frame.js';
|
|
51
|
+
import { WINDOW_EXTENT_EXPR } from '../../constants.js';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* DuckDB SQL dialect visitor for converting AST nodes to DuckDB-compatible SQL.
|
|
55
|
+
*/
|
|
56
|
+
export class DuckDBCodeGenerator extends SQLCodeGenerator {
|
|
57
|
+
visitAggregate(node: AggregateNode): string {
|
|
58
|
+
const { name, args, isDistinct, filter, order } = node;
|
|
59
|
+
const arg = [
|
|
60
|
+
isDistinct ? 'DISTINCT' : '',
|
|
61
|
+
args?.length ? this.mapToString(args).join(', ')
|
|
62
|
+
: name.toLowerCase() === 'count' ? '*'
|
|
63
|
+
: '',
|
|
64
|
+
order.length ? `ORDER BY ${this.mapToString(order).join(', ')}` : ''
|
|
65
|
+
].filter(x => x).join(' ');
|
|
66
|
+
const filt = filter ? ` FILTER (WHERE ${this.toString(filter)})` : '';
|
|
67
|
+
return `${name}(${arg})${filt}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
visitBetween(node: BetweenOpNode): string {
|
|
71
|
+
return betweenToString(this, node, 'BETWEEN');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
visitBinary(node: BinaryOpNode): string {
|
|
75
|
+
const { left, right, op } = node;
|
|
76
|
+
return `(${this.toString(left)} ${op} ${this.toString(right)})`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
visitCase(node: CaseNode): string {
|
|
80
|
+
const { expr, _when, _else } = node;
|
|
81
|
+
return 'CASE '
|
|
82
|
+
+ (expr ? `${this.toString(expr)} ` : '')
|
|
83
|
+
+ this.mapToString(_when).join(' ')
|
|
84
|
+
+ (_else ? ` ELSE ${this.toString(_else)}` : '')
|
|
85
|
+
+ ' END';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
visitCast(node: CastNode): string {
|
|
89
|
+
const { expr, cast } = node;
|
|
90
|
+
return `(${this.toString(expr)})::${cast}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
visitCollate(node: CollateNode): string {
|
|
94
|
+
const { expr, collation } = node;
|
|
95
|
+
return `${this.toString(expr)} COLLATE ${collation}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
visitColumnParam(node: ColumnParamNode): string {
|
|
99
|
+
return this.visitColumnRef(node);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
visitColumnRef(node: ColumnRefNode): string {
|
|
103
|
+
const { column, table } = node;
|
|
104
|
+
const tref = table ? `${this.toString(table)}.` : '';
|
|
105
|
+
const id = column === '*' ? '*' : quoteIdentifier(column);
|
|
106
|
+
return `${tref}${id}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
visitDescribeQuery(node: DescribeQuery): string {
|
|
110
|
+
const { query } = node;
|
|
111
|
+
return `DESC ${this.toString(query)}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
visitExpression(node: ExprNode): string {
|
|
115
|
+
// This method might not be used in practice, but needs to exist for the interface
|
|
116
|
+
// If we reach here, it might be an error or a generic fallback
|
|
117
|
+
throw new Error(`Unexpected EXPRESSION node type. Node: ${JSON.stringify(node)}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
visitFragment(node: FragmentNode): string {
|
|
121
|
+
const { spans } = node;
|
|
122
|
+
return spans.map((span: string | SQLNode) => {
|
|
123
|
+
return typeof span === 'string' ? span : this.toString(span);
|
|
124
|
+
}).join('');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
visitFromClause(node: FromClauseNode): string {
|
|
128
|
+
const { expr, alias, sample } = node;
|
|
129
|
+
const ref = isQuery(expr) ? `(${this.toString(expr)})` : `${this.toString(expr)}`;
|
|
130
|
+
const from = alias && !(isTableRef(expr) && expr.table?.join('.') === alias)
|
|
131
|
+
? `${ref} AS ${quoteIdentifier(alias)}`
|
|
132
|
+
: `${ref}`;
|
|
133
|
+
return `${from}${sample ? ` TABLESAMPLE ${this.toString(sample)}` : ''}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
visitFunction(node: FunctionNode): string {
|
|
137
|
+
const { name, args } = node;
|
|
138
|
+
return `${name}(${this.mapToString(args).join(', ')})`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
visitIn(node: InOpNode): string {
|
|
142
|
+
const { expr, values } = node;
|
|
143
|
+
return `(${this.toString(expr)} IN (${this.mapToString(values).join(', ')}))`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
visitInterval(node: IntervalNode): string {
|
|
147
|
+
const { steps, name } = node;
|
|
148
|
+
return `INTERVAL ${steps} ${name}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
visitJoinClause(node: JoinNode): string {
|
|
152
|
+
const { left, right, joinVariant, joinType, condition, using, sample } = node;
|
|
153
|
+
const variant = joinVariant === 'REGULAR' ? '' : `${joinVariant} `;
|
|
154
|
+
let type = '';
|
|
155
|
+
let cond = '';
|
|
156
|
+
|
|
157
|
+
if (joinVariant !== 'CROSS') {
|
|
158
|
+
type = joinType !== 'INNER' ? `${joinType} ` : '';
|
|
159
|
+
cond = condition ? ` ON ${this.toString(condition)}`
|
|
160
|
+
: using ? ` USING (${this.mapToString(using).join(', ')})`
|
|
161
|
+
: '';
|
|
162
|
+
}
|
|
163
|
+
const samp = sample ? ` USING SAMPLE ${this.toString(sample)}` : '';
|
|
164
|
+
return `${this.toString(left)} ${variant}${type}JOIN ${this.toString(right)}${cond}${samp}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
visitList(node: ListNode): string {
|
|
168
|
+
const { values } = node;
|
|
169
|
+
return `[${this.mapToString(values).join(', ')}]`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
visitLiteral(node: LiteralNode): string {
|
|
173
|
+
const { value } = node;
|
|
174
|
+
return literalToSQL(value);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
visitLogicalOperator(node: LogicalOpNode<ExprNode>): string {
|
|
178
|
+
const { clauses, op } = node;
|
|
179
|
+
const c = this.mapToString(clauses);
|
|
180
|
+
return c.length === 0 ? ''
|
|
181
|
+
: c.length === 1 ? `${c[0]}`
|
|
182
|
+
: `(${c.join(` ${op} `)})`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
visitNotBetween(node: NotBetweenOpNode): string {
|
|
186
|
+
return betweenToString(this, node, 'NOT BETWEEN');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
visitOrderBy(node: OrderByNode): string {
|
|
190
|
+
const { expr, desc, nullsFirst } = node;
|
|
191
|
+
const dir = desc ? ' DESC'
|
|
192
|
+
: desc === false ? ' ASC'
|
|
193
|
+
: '';
|
|
194
|
+
const nf = nullsFirst ? ' NULLS FIRST'
|
|
195
|
+
: nullsFirst === false ? ' NULLS LAST'
|
|
196
|
+
: '';
|
|
197
|
+
return `${this.toString(expr)}${dir}${nf}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
visitParam(node: ParamNode): string {
|
|
201
|
+
const { param } = node;
|
|
202
|
+
// Get the current value from the parameter and format it as a literal
|
|
203
|
+
return literalToSQL(param.value);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
visitSampleClause(node: SampleClauseNode): string {
|
|
207
|
+
const { size, perc, method, seed } = node;
|
|
208
|
+
const m = method ? `${method} ` : '';
|
|
209
|
+
const s = seed != null ? ` REPEATABLE (${seed})` : '';
|
|
210
|
+
return `${m}(${size}${perc ? '%' : ' ROWS'})${s}`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
visitScalarSubquery(node: ScalarSubqueryNode): string {
|
|
214
|
+
return `(${this.toString(node.subquery)})`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
visitSelectClause(node: SelectClauseNode): string {
|
|
218
|
+
const { expr, alias } = node;
|
|
219
|
+
|
|
220
|
+
return !alias || isColumnRefFor(expr, alias)
|
|
221
|
+
? this.toString(expr)
|
|
222
|
+
: `${this.toString(expr)} AS ${quoteIdentifier(alias)}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
visitSelectQuery(node: SelectQuery): string {
|
|
226
|
+
const {
|
|
227
|
+
_with, _select, _distinct, _from, _sample, _where,
|
|
228
|
+
_groupby, _having, _window, _qualify, _orderby,
|
|
229
|
+
_limitPerc, _limit, _offset
|
|
230
|
+
} = node;
|
|
231
|
+
|
|
232
|
+
const sql = [];
|
|
233
|
+
|
|
234
|
+
// WITH
|
|
235
|
+
if (_with.length) {
|
|
236
|
+
sql.push(`WITH ${this.mapToString(_with).join(', ')}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// SELECT
|
|
240
|
+
sql.push(`SELECT${_distinct ? ' DISTINCT' : ''} ${this.mapToString(_select).join(', ')}`);
|
|
241
|
+
|
|
242
|
+
// FROM
|
|
243
|
+
if (_from.length) {
|
|
244
|
+
sql.push(`FROM ${this.mapToString(_from).join(', ')}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// SAMPLE
|
|
248
|
+
if (_sample) {
|
|
249
|
+
sql.push(`USING SAMPLE ${this.toString(_sample)}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// WHERE
|
|
253
|
+
if (_where.length) {
|
|
254
|
+
const clauses = this.mapToString(_where).filter(x => x).join(' AND ');
|
|
255
|
+
if (clauses) sql.push(`WHERE ${clauses}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// GROUP BY
|
|
259
|
+
if (_groupby.length) {
|
|
260
|
+
sql.push(`GROUP BY ${this.mapToString(_groupby).join(', ')}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// HAVING
|
|
264
|
+
if (_having.length) {
|
|
265
|
+
const clauses = this.mapToString(_having).filter(x => x).join(' AND ');
|
|
266
|
+
if (clauses) sql.push(`HAVING ${clauses}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// WINDOW
|
|
270
|
+
if (_window.length) {
|
|
271
|
+
sql.push(`WINDOW ${this.mapToString(_window).join(', ')}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// QUALIFY
|
|
275
|
+
if (_qualify.length) {
|
|
276
|
+
const clauses = this.mapToString(_qualify).filter(x => x).join(' AND ');
|
|
277
|
+
if (clauses) sql.push(`QUALIFY ${clauses}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ORDER BY
|
|
281
|
+
if (_orderby.length) {
|
|
282
|
+
sql.push(`ORDER BY ${this.mapToString(_orderby).join(', ')}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// LIMIT
|
|
286
|
+
if (_limit) {
|
|
287
|
+
sql.push(`LIMIT ${this.toString(_limit)}${_limitPerc ? '%' : ''}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// OFFSET
|
|
291
|
+
if (_offset != null) {
|
|
292
|
+
sql.push(`OFFSET ${this.toString(_offset)}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return sql.join(' ');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
visitSetOperation(node: SetOperation): string {
|
|
299
|
+
const { op, queries, _with, _orderby, _limitPerc, _limit, _offset } = node;
|
|
300
|
+
const sql = [];
|
|
301
|
+
|
|
302
|
+
// WITH
|
|
303
|
+
if (_with.length) sql.push(`WITH ${this.mapToString(_with).join(', ')}`);
|
|
304
|
+
|
|
305
|
+
// SUBQUERIES
|
|
306
|
+
sql.push(queries.join(` ${op} `));
|
|
307
|
+
|
|
308
|
+
// ORDER BY
|
|
309
|
+
if (_orderby.length) sql.push(`ORDER BY ${this.mapToString(_orderby).join(', ')}`);
|
|
310
|
+
|
|
311
|
+
// LIMIT
|
|
312
|
+
if (_limit) sql.push(`LIMIT ${this.toString(_limit)}${_limitPerc ? '%' : ''}`);
|
|
313
|
+
|
|
314
|
+
// OFFSET
|
|
315
|
+
if (_offset) sql.push(`OFFSET ${this.toString(_offset)}`);
|
|
316
|
+
|
|
317
|
+
return sql.join(' ');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
visitTableRef(node: TableRefNode): string {
|
|
321
|
+
const { table } = node;
|
|
322
|
+
return table.map((t: string) => quoteIdentifier(t)).join('.');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
visitUnary(node: UnaryOpNode): string {
|
|
326
|
+
const { expr, op } = node;
|
|
327
|
+
return `(${op} ${this.toString(expr)})`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
visitUnaryPostfix(node: UnaryPostfixOpNode): string {
|
|
331
|
+
const { expr, op } = node;
|
|
332
|
+
return `(${this.toString(expr)} ${op})`;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
visitUnnest(node: UnnestNode): string {
|
|
336
|
+
const { expr, recursive, maxDepth } = node;
|
|
337
|
+
const args = [this.toString(expr)];
|
|
338
|
+
|
|
339
|
+
if (recursive) {
|
|
340
|
+
args.push('recursive := true');
|
|
341
|
+
}
|
|
342
|
+
if (maxDepth != null && maxDepth > 0) {
|
|
343
|
+
args.push(`max_depth := ${maxDepth}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return `UNNEST(${args.join(', ')})`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
visitVerbatim(node: VerbatimNode): string {
|
|
350
|
+
const { value } = node;
|
|
351
|
+
return value;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
visitWhen(node: WhenNode): string {
|
|
355
|
+
const { when, then } = node;
|
|
356
|
+
return `WHEN ${this.toString(when)} THEN ${this.toString(then)}`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
visitWindow(node: WindowNode): string {
|
|
360
|
+
const { func, def } = node;
|
|
361
|
+
return `${this.toString(func)} OVER ${this.toString(def)}`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
visitWindowClause(node: WindowClauseNode): string {
|
|
365
|
+
const { name, def } = node;
|
|
366
|
+
return `${quoteIdentifier(name)} AS ${this.toString(def)}`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
visitWindowDef(node: WindowDefNode): string {
|
|
370
|
+
const { name, partition, order, framedef } = node;
|
|
371
|
+
const base = name && quoteIdentifier(name);
|
|
372
|
+
const def = [
|
|
373
|
+
base,
|
|
374
|
+
partition?.length && `PARTITION BY ${this.mapToString(partition).join(', ')}`,
|
|
375
|
+
order?.length && `ORDER BY ${this.mapToString(order).join(', ')}`,
|
|
376
|
+
framedef && this.toString(framedef)
|
|
377
|
+
].filter(x => x);
|
|
378
|
+
return base && def.length < 2 ? base : `(${def.join(' ')})`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
visitWindowExtentExpr(node: WindowFrameExprNode): string {
|
|
382
|
+
const { scope, expr } = node;
|
|
383
|
+
return scope === CURRENT_ROW
|
|
384
|
+
? scope
|
|
385
|
+
: `${isNode(expr) ? this.toString(expr) : (expr ?? UNBOUNDED)} ${scope}`;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
visitWindowFrame(node: WindowFrameNode): string {
|
|
389
|
+
const { frameType, exclude, extent } = node;
|
|
390
|
+
const [prev, next] = isNode(extent)
|
|
391
|
+
? extent.value as [unknown, unknown]
|
|
392
|
+
: extent;
|
|
393
|
+
const a = formatFrameExpr(this, prev, PRECEDING);
|
|
394
|
+
const b = formatFrameExpr(this, next, FOLLOWING);
|
|
395
|
+
return `${frameType} BETWEEN ${a} AND ${b}${exclude ? ` ${exclude}` : ''}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
visitWindowFunction(node: WindowFunctionNode): string {
|
|
399
|
+
const { name, args, ignoreNulls, order } = node;
|
|
400
|
+
const arg = [
|
|
401
|
+
this.mapToString(args).join(', '),
|
|
402
|
+
order.length ? `ORDER BY ${this.mapToString(order).join(', ')}` : '',
|
|
403
|
+
ignoreNulls ? 'IGNORE NULLS' : ''
|
|
404
|
+
].filter(x => x).join(' ');
|
|
405
|
+
return `${name}(${arg})`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
visitWithClause(node: WithClauseNode): string {
|
|
409
|
+
const { name, query, materialized } = node;
|
|
410
|
+
const mat = materialized === true ? 'MATERIALIZED '
|
|
411
|
+
: materialized === false ? 'NOT MATERIALIZED '
|
|
412
|
+
: '';
|
|
413
|
+
return `${quoteIdentifier(name)} AS ${mat}(${this.toString(query)})`;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function isColumnRefFor(expr: unknown, name: string): expr is ColumnRefNode {
|
|
418
|
+
return expr instanceof ColumnRefNode
|
|
419
|
+
&& expr.table == null
|
|
420
|
+
&& expr.column === name;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function betweenToString(
|
|
424
|
+
visitor: SQLCodeGenerator,
|
|
425
|
+
node: BetweenOpNode | NotBetweenOpNode,
|
|
426
|
+
op: string
|
|
427
|
+
) {
|
|
428
|
+
const { extent, expr } = node;
|
|
429
|
+
if (!extent) return '';
|
|
430
|
+
const [a, b] = visitor.mapToString(extent);
|
|
431
|
+
return `(${visitor.toString(expr)} ${op} ${a} AND ${b})`;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function formatFrameExpr(visitor: SQLCodeGenerator, value: unknown, scope: string) {
|
|
435
|
+
const x = isNode(value) ? visitor.toString(value) : value;
|
|
436
|
+
return isNode(value) && value.type === WINDOW_EXTENT_EXPR ? x
|
|
437
|
+
: x != null && typeof x !== 'number' ? `${x} ${scope}`
|
|
438
|
+
: x === 0 ? CURRENT_ROW
|
|
439
|
+
: !(x && Number.isFinite(x)) ? `${UNBOUNDED} ${scope}`
|
|
440
|
+
: `${Math.abs(x)} ${scope}`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Create a default DuckDB visitor instance for convenience
|
|
444
|
+
export const duckDBCodeGenerator = new DuckDBCodeGenerator();
|