squirreling 0.8.0 → 0.9.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/package.json +3 -3
- package/src/execute/aggregates.js +18 -30
- package/src/execute/execute.js +22 -35
- package/src/execute/join.js +15 -31
- package/src/execute/sort.js +5 -10
- package/src/expression/evaluate.js +56 -60
- package/src/index.d.ts +24 -2
- package/src/index.js +2 -2
- package/src/parse/comparison.js +7 -7
- package/src/parse/expression.js +14 -14
- package/src/parse/functions.js +23 -6
- package/src/parse/parse.js +55 -68
- package/src/parse/state.js +0 -9
- package/src/parse/tokenize.js +2 -2
- package/src/parse/types.d.ts +1 -1
- package/src/parseErrors.js +5 -4
- package/src/plan/columns.js +149 -0
- package/src/plan/plan.js +116 -46
- package/src/plan/types.d.ts +44 -47
- package/src/types.d.ts +19 -0
- package/src/validationErrors.js +10 -5
- package/src/execute/columns.js +0 -102
package/src/plan/types.d.ts
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export interface ExecuteContext {
|
|
4
|
-
tables: Record<string, AsyncDataSource>
|
|
5
|
-
functions?: Record<string, UserDefinedFunction>
|
|
6
|
-
signal?: AbortSignal
|
|
7
|
-
}
|
|
1
|
+
import { ExprNode, JoinType, OrderByItem, ScanOptions, SelectColumn } from '../types.js'
|
|
8
2
|
|
|
9
3
|
export type QueryPlan =
|
|
10
4
|
| ScanNode
|
|
11
5
|
| FilterNode
|
|
12
6
|
| ProjectNode
|
|
13
|
-
| HashJoinNode
|
|
14
|
-
| NestedLoopJoinNode
|
|
15
|
-
| PositionalJoinNode
|
|
16
|
-
| HashAggregateNode
|
|
17
|
-
| ScalarAggregateNode
|
|
18
7
|
| SortNode
|
|
19
8
|
| DistinctNode
|
|
20
9
|
| LimitNode
|
|
10
|
+
| HashAggregateNode
|
|
11
|
+
| ScalarAggregateNode
|
|
12
|
+
| HashJoinNode
|
|
13
|
+
| NestedLoopJoinNode
|
|
14
|
+
| PositionalJoinNode
|
|
21
15
|
|
|
16
|
+
// Scan node
|
|
22
17
|
export interface ScanNode {
|
|
23
18
|
type: 'Scan'
|
|
24
19
|
table: string
|
|
25
|
-
hints
|
|
20
|
+
hints: ScanOptions
|
|
26
21
|
}
|
|
27
22
|
|
|
23
|
+
// Single-child nodes
|
|
28
24
|
export interface FilterNode {
|
|
29
25
|
type: 'Filter'
|
|
30
26
|
condition: ExprNode
|
|
@@ -37,6 +33,41 @@ export interface ProjectNode {
|
|
|
37
33
|
child: QueryPlan
|
|
38
34
|
}
|
|
39
35
|
|
|
36
|
+
export interface SortNode {
|
|
37
|
+
type: 'Sort'
|
|
38
|
+
orderBy: OrderByItem[]
|
|
39
|
+
child: QueryPlan
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface DistinctNode {
|
|
43
|
+
type: 'Distinct'
|
|
44
|
+
child: QueryPlan
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface LimitNode {
|
|
48
|
+
type: 'Limit'
|
|
49
|
+
limit?: number
|
|
50
|
+
offset?: number
|
|
51
|
+
child: QueryPlan
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Aggregate nodes
|
|
55
|
+
export interface HashAggregateNode {
|
|
56
|
+
type: 'HashAggregate'
|
|
57
|
+
groupBy: ExprNode[]
|
|
58
|
+
columns: SelectColumn[]
|
|
59
|
+
having?: ExprNode
|
|
60
|
+
child: QueryPlan
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ScalarAggregateNode {
|
|
64
|
+
type: 'ScalarAggregate'
|
|
65
|
+
columns: SelectColumn[]
|
|
66
|
+
having?: ExprNode
|
|
67
|
+
child: QueryPlan
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Join nodes
|
|
40
71
|
export interface HashJoinNode {
|
|
41
72
|
type: 'HashJoin'
|
|
42
73
|
joinType: JoinType
|
|
@@ -65,37 +96,3 @@ export interface PositionalJoinNode {
|
|
|
65
96
|
left: QueryPlan
|
|
66
97
|
right: QueryPlan
|
|
67
98
|
}
|
|
68
|
-
|
|
69
|
-
export interface HashAggregateNode {
|
|
70
|
-
type: 'HashAggregate'
|
|
71
|
-
groupBy: ExprNode[]
|
|
72
|
-
columns: SelectColumn[]
|
|
73
|
-
having?: ExprNode
|
|
74
|
-
child: QueryPlan
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface ScalarAggregateNode {
|
|
78
|
-
type: 'ScalarAggregate'
|
|
79
|
-
columns: SelectColumn[]
|
|
80
|
-
having?: ExprNode
|
|
81
|
-
child: QueryPlan
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface SortNode {
|
|
85
|
-
type: 'Sort'
|
|
86
|
-
orderBy: OrderByItem[]
|
|
87
|
-
aliases?: Map<string, ExprNode>
|
|
88
|
-
child: QueryPlan
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface DistinctNode {
|
|
92
|
-
type: 'Distinct'
|
|
93
|
-
child: QueryPlan
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface LimitNode {
|
|
97
|
-
type: 'Limit'
|
|
98
|
-
limit?: number
|
|
99
|
-
offset?: number
|
|
100
|
-
child: QueryPlan
|
|
101
|
-
}
|
package/src/types.d.ts
CHANGED
|
@@ -15,6 +15,19 @@ export interface ExecuteSqlOptions {
|
|
|
15
15
|
signal?: AbortSignal
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// planSql(options)
|
|
19
|
+
export interface PlanSqlOptions {
|
|
20
|
+
query: string | SelectStatement
|
|
21
|
+
functions?: Record<string, UserDefinedFunction>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// executePlan(plan, context)
|
|
25
|
+
export interface ExecuteContext {
|
|
26
|
+
tables: Record<string, AsyncDataSource>
|
|
27
|
+
functions?: Record<string, UserDefinedFunction>
|
|
28
|
+
signal?: AbortSignal
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
// AsyncRow represents a row with async cell values
|
|
19
32
|
export interface AsyncRow {
|
|
20
33
|
columns: string[]
|
|
@@ -285,3 +298,9 @@ export interface JoinClause {
|
|
|
285
298
|
alias?: string
|
|
286
299
|
on?: ExprNode
|
|
287
300
|
}
|
|
301
|
+
|
|
302
|
+
export interface ExecuteContext {
|
|
303
|
+
tables: Record<string, AsyncDataSource>
|
|
304
|
+
functions?: Record<string, UserDefinedFunction>
|
|
305
|
+
signal?: AbortSignal
|
|
306
|
+
}
|
package/src/validationErrors.js
CHANGED
|
@@ -89,15 +89,20 @@ export function argValueError({ funcName, message, positionStart, positionEnd, h
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Error for aggregate function misuse
|
|
92
|
+
* Error for aggregate function misuse.
|
|
93
93
|
*
|
|
94
94
|
* @param {Object} options
|
|
95
95
|
* @param {string} options.funcName - The aggregate function
|
|
96
|
-
* @param {
|
|
97
|
-
* @
|
|
96
|
+
* @param {number} options.positionStart - Start position in query
|
|
97
|
+
* @param {number} options.positionEnd - End position in query
|
|
98
|
+
* @returns {ExecutionError}
|
|
98
99
|
*/
|
|
99
|
-
export function aggregateError({ funcName,
|
|
100
|
-
return new
|
|
100
|
+
export function aggregateError({ funcName, positionStart, positionEnd }) {
|
|
101
|
+
return new ExecutionError({
|
|
102
|
+
message: `Aggregate function ${funcName} must exist in a GROUP BY clause or be part of an aggregate SELECT list`,
|
|
103
|
+
positionStart,
|
|
104
|
+
positionEnd,
|
|
105
|
+
})
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
/**
|
package/src/execute/columns.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import { ExprNode, SelectStatement, SelectColumn } from '../types.js'
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Extracts column names needed from a SELECT statement.
|
|
7
|
-
*
|
|
8
|
-
* @param {SelectStatement} select
|
|
9
|
-
* @returns {string[] | undefined} array of column names, or undefined if all columns needed
|
|
10
|
-
*/
|
|
11
|
-
export function extractColumns(select) {
|
|
12
|
-
// If any column is SELECT *, we need all columns
|
|
13
|
-
if (select.columns.some(col => col.kind === 'star')) {
|
|
14
|
-
return undefined
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** @type {Set<string>} */
|
|
18
|
-
const columns = new Set()
|
|
19
|
-
|
|
20
|
-
// Columns from SELECT list
|
|
21
|
-
for (const col of select.columns) {
|
|
22
|
-
collectColumnsFromSelectColumn(col, columns)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Columns from WHERE
|
|
26
|
-
collectColumnsFromExpr(select.where, columns)
|
|
27
|
-
|
|
28
|
-
// Columns from ORDER BY
|
|
29
|
-
for (const item of select.orderBy) {
|
|
30
|
-
collectColumnsFromExpr(item.expr, columns)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Columns from GROUP BY
|
|
34
|
-
for (const expr of select.groupBy) {
|
|
35
|
-
collectColumnsFromExpr(expr, columns)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Columns from HAVING
|
|
39
|
-
collectColumnsFromExpr(select.having, columns)
|
|
40
|
-
|
|
41
|
-
return [...columns]
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Collects column names from a SELECT column
|
|
46
|
-
*
|
|
47
|
-
* @param {SelectColumn} col
|
|
48
|
-
* @param {Set<string>} columns
|
|
49
|
-
*/
|
|
50
|
-
function collectColumnsFromSelectColumn(col, columns) {
|
|
51
|
-
if (col.kind === 'derived') {
|
|
52
|
-
collectColumnsFromExpr(col.expr, columns)
|
|
53
|
-
}
|
|
54
|
-
// 'star' columns handled separately (returns undefined for all columns)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Recursively collects column names (identifiers) from an expression
|
|
59
|
-
*
|
|
60
|
-
* @param {ExprNode | undefined} expr
|
|
61
|
-
* @param {Set<string>} columns
|
|
62
|
-
*/
|
|
63
|
-
function collectColumnsFromExpr(expr, columns) {
|
|
64
|
-
if (!expr) return
|
|
65
|
-
if (expr.type === 'identifier' && expr.name !== '*') {
|
|
66
|
-
columns.add(expr.name)
|
|
67
|
-
} else if (expr.type === 'literal') {
|
|
68
|
-
// No columns
|
|
69
|
-
} else if (expr.type === 'binary') {
|
|
70
|
-
collectColumnsFromExpr(expr.left, columns)
|
|
71
|
-
collectColumnsFromExpr(expr.right, columns)
|
|
72
|
-
} else if (expr.type === 'unary') {
|
|
73
|
-
collectColumnsFromExpr(expr.argument, columns)
|
|
74
|
-
} else if (expr.type === 'function') {
|
|
75
|
-
for (const arg of expr.args) {
|
|
76
|
-
collectColumnsFromExpr(arg, columns)
|
|
77
|
-
}
|
|
78
|
-
} else if (expr.type === 'cast') {
|
|
79
|
-
collectColumnsFromExpr(expr.expr, columns)
|
|
80
|
-
} else if (expr.type === 'in valuelist') {
|
|
81
|
-
collectColumnsFromExpr(expr.expr, columns)
|
|
82
|
-
for (const val of expr.values) {
|
|
83
|
-
collectColumnsFromExpr(val, columns)
|
|
84
|
-
}
|
|
85
|
-
} else if (expr.type === 'in') {
|
|
86
|
-
collectColumnsFromExpr(expr.expr, columns)
|
|
87
|
-
// Subquery columns are from a different scope, don't collect
|
|
88
|
-
} else if (expr.type === 'exists' || expr.type === 'not exists') {
|
|
89
|
-
// Subquery columns are from a different scope, don't collect
|
|
90
|
-
} else if (expr.type === 'case') {
|
|
91
|
-
if (expr.caseExpr) {
|
|
92
|
-
collectColumnsFromExpr(expr.caseExpr, columns)
|
|
93
|
-
}
|
|
94
|
-
for (const when of expr.whenClauses) {
|
|
95
|
-
collectColumnsFromExpr(when.condition, columns)
|
|
96
|
-
collectColumnsFromExpr(when.result, columns)
|
|
97
|
-
}
|
|
98
|
-
if (expr.elseResult) {
|
|
99
|
-
collectColumnsFromExpr(expr.elseResult, columns)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|