squirreling 0.12.16 → 0.12.18
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 +1 -1
- package/src/execute/execute.js +6 -2
- package/src/index.d.ts +12 -0
- package/src/index.js +1 -0
- package/src/parse/extractTables.js +125 -0
package/package.json
CHANGED
package/src/execute/execute.js
CHANGED
|
@@ -381,7 +381,11 @@ function computeScanRows(tableNumRows, limit, offset) {
|
|
|
381
381
|
*/
|
|
382
382
|
async function* filterRows(rows, condition, context, limit) {
|
|
383
383
|
const MAX_CHUNK = 256
|
|
384
|
-
|
|
384
|
+
// Without a LIMIT hint, evaluate row-by-row to preserve streaming.
|
|
385
|
+
// With a LIMIT hint, batch in growing chunks to parallelize async cell
|
|
386
|
+
// evaluation across rows that may be discarded anyway.
|
|
387
|
+
let chunkSize = limit ?? 1
|
|
388
|
+
const grow = limit !== undefined
|
|
385
389
|
let rowIndex = 0
|
|
386
390
|
|
|
387
391
|
/** @type {{ row: AsyncRow, rowIndex: number }[]} */
|
|
@@ -400,7 +404,7 @@ async function* filterRows(rows, condition, context, limit) {
|
|
|
400
404
|
if (results[i]) yield buffer[i].row
|
|
401
405
|
}
|
|
402
406
|
buffer = []
|
|
403
|
-
chunkSize = Math.min(chunkSize * 2, MAX_CHUNK)
|
|
407
|
+
if (grow) chunkSize = Math.min(chunkSize * 2, MAX_CHUNK)
|
|
404
408
|
}
|
|
405
409
|
}
|
|
406
410
|
|
package/src/index.d.ts
CHANGED
|
@@ -53,6 +53,18 @@ export function executePlan(options: { plan: QueryPlan, context: ExecuteContext
|
|
|
53
53
|
*/
|
|
54
54
|
export function parseSql(options: ParseSqlOptions): Statement
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Collects every external table referenced from FROM and JOIN clauses in a
|
|
58
|
+
* parsed statement, descending into subqueries (IN, EXISTS, derived tables,
|
|
59
|
+
* scalar subqueries) and the branches of compound queries. CTE names defined
|
|
60
|
+
* by an enclosing WITH are skipped. Returned in first-seen order with
|
|
61
|
+
* duplicates removed.
|
|
62
|
+
*
|
|
63
|
+
* @param statement - parsed SQL statement (output of `parseSql`)
|
|
64
|
+
* @returns table names referenced in the query, excluding CTE aliases
|
|
65
|
+
*/
|
|
66
|
+
export function extractTables(statement: Statement): string[]
|
|
67
|
+
|
|
56
68
|
/**
|
|
57
69
|
* Builds a query plan from a SQL query string or AST
|
|
58
70
|
*
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { ExprNode, Statement } from '../types.js'
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Collect every external table referenced from FROM and JOIN clauses in a
|
|
7
|
+
* parsed statement, including those inside subqueries (IN, EXISTS, derived
|
|
8
|
+
* tables, scalar subqueries) and the branches of compound (UNION /
|
|
9
|
+
* INTERSECT / EXCEPT) queries. CTE names defined by an enclosing WITH are
|
|
10
|
+
* skipped, including across sibling CTEs and nested WITHs — the result is
|
|
11
|
+
* the set of names a caller would need to provide as `tables` to
|
|
12
|
+
* `executeSql`.
|
|
13
|
+
*
|
|
14
|
+
* Returned in first-seen order with duplicates removed. Names are returned
|
|
15
|
+
* in the original case they were written in the query.
|
|
16
|
+
*
|
|
17
|
+
* @param {Statement} statement
|
|
18
|
+
* @returns {string[]}
|
|
19
|
+
*/
|
|
20
|
+
export function extractTables(statement) {
|
|
21
|
+
/** @type {Set<string>} */
|
|
22
|
+
const refs = new Set()
|
|
23
|
+
walkStatement(statement, new Set(), refs)
|
|
24
|
+
return [...refs]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Statement} stmt
|
|
29
|
+
* @param {Set<string>} cteScope - lowercased CTE names visible at this point
|
|
30
|
+
* @param {Set<string>} refs
|
|
31
|
+
* @returns {void}
|
|
32
|
+
*/
|
|
33
|
+
function walkStatement(stmt, cteScope, refs) {
|
|
34
|
+
if (stmt.type === 'with') {
|
|
35
|
+
const scope = new Set(cteScope)
|
|
36
|
+
for (const cte of stmt.ctes) {
|
|
37
|
+
walkStatement(cte.query, scope, refs)
|
|
38
|
+
scope.add(cte.name.toLowerCase())
|
|
39
|
+
}
|
|
40
|
+
walkStatement(stmt.query, scope, refs)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
if (stmt.type === 'compound') {
|
|
44
|
+
walkStatement(stmt.left, cteScope, refs)
|
|
45
|
+
walkStatement(stmt.right, cteScope, refs)
|
|
46
|
+
for (const o of stmt.orderBy) walkExpr(o.expr, cteScope, refs)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
// select
|
|
50
|
+
if (stmt.from.type === 'table') {
|
|
51
|
+
if (!cteScope.has(stmt.from.table.toLowerCase())) refs.add(stmt.from.table)
|
|
52
|
+
} else if (stmt.from.type === 'subquery') {
|
|
53
|
+
walkStatement(stmt.from.query, cteScope, refs)
|
|
54
|
+
} else {
|
|
55
|
+
for (const a of stmt.from.args) walkExpr(a, cteScope, refs)
|
|
56
|
+
}
|
|
57
|
+
for (const j of stmt.joins) {
|
|
58
|
+
if (j.fromFunction) {
|
|
59
|
+
for (const a of j.fromFunction.args) walkExpr(a, cteScope, refs)
|
|
60
|
+
} else if (!cteScope.has(j.table.toLowerCase())) {
|
|
61
|
+
refs.add(j.table)
|
|
62
|
+
}
|
|
63
|
+
if (j.on) walkExpr(j.on, cteScope, refs)
|
|
64
|
+
}
|
|
65
|
+
for (const c of stmt.columns) {
|
|
66
|
+
if (c.type === 'derived') walkExpr(c.expr, cteScope, refs)
|
|
67
|
+
}
|
|
68
|
+
if (stmt.where) walkExpr(stmt.where, cteScope, refs)
|
|
69
|
+
for (const g of stmt.groupBy) walkExpr(g, cteScope, refs)
|
|
70
|
+
if (stmt.having) walkExpr(stmt.having, cteScope, refs)
|
|
71
|
+
for (const o of stmt.orderBy) walkExpr(o.expr, cteScope, refs)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {ExprNode} expr
|
|
76
|
+
* @param {Set<string>} cteScope
|
|
77
|
+
* @param {Set<string>} refs
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
80
|
+
function walkExpr(expr, cteScope, refs) {
|
|
81
|
+
switch (expr.type) {
|
|
82
|
+
case 'unary':
|
|
83
|
+
walkExpr(expr.argument, cteScope, refs)
|
|
84
|
+
return
|
|
85
|
+
case 'binary':
|
|
86
|
+
walkExpr(expr.left, cteScope, refs)
|
|
87
|
+
walkExpr(expr.right, cteScope, refs)
|
|
88
|
+
return
|
|
89
|
+
case 'function':
|
|
90
|
+
for (const a of expr.args) walkExpr(a, cteScope, refs)
|
|
91
|
+
if (expr.filter) walkExpr(expr.filter, cteScope, refs)
|
|
92
|
+
return
|
|
93
|
+
case 'window':
|
|
94
|
+
for (const a of expr.args) walkExpr(a, cteScope, refs)
|
|
95
|
+
for (const p of expr.partitionBy) walkExpr(p, cteScope, refs)
|
|
96
|
+
for (const o of expr.orderBy) walkExpr(o.expr, cteScope, refs)
|
|
97
|
+
return
|
|
98
|
+
case 'cast':
|
|
99
|
+
walkExpr(expr.expr, cteScope, refs)
|
|
100
|
+
return
|
|
101
|
+
case 'in':
|
|
102
|
+
walkExpr(expr.expr, cteScope, refs)
|
|
103
|
+
walkStatement(expr.subquery, cteScope, refs)
|
|
104
|
+
return
|
|
105
|
+
case 'in valuelist':
|
|
106
|
+
walkExpr(expr.expr, cteScope, refs)
|
|
107
|
+
for (const v of expr.values) walkExpr(v, cteScope, refs)
|
|
108
|
+
return
|
|
109
|
+
case 'exists':
|
|
110
|
+
case 'not exists':
|
|
111
|
+
walkStatement(expr.subquery, cteScope, refs)
|
|
112
|
+
return
|
|
113
|
+
case 'case':
|
|
114
|
+
if (expr.caseExpr) walkExpr(expr.caseExpr, cteScope, refs)
|
|
115
|
+
for (const w of expr.whenClauses) {
|
|
116
|
+
walkExpr(w.condition, cteScope, refs)
|
|
117
|
+
walkExpr(w.result, cteScope, refs)
|
|
118
|
+
}
|
|
119
|
+
if (expr.elseResult) walkExpr(expr.elseResult, cteScope, refs)
|
|
120
|
+
return
|
|
121
|
+
case 'subquery':
|
|
122
|
+
walkStatement(expr.subquery, cteScope, refs)
|
|
123
|
+
}
|
|
124
|
+
// 'literal' / 'identifier' / 'interval' / 'star' are leaves with no children.
|
|
125
|
+
}
|