squirreling 0.12.17 → 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/index.d.ts +12 -0
- package/src/index.js +1 -0
- package/src/parse/extractTables.js +125 -0
package/package.json
CHANGED
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
|
+
}
|