squirreling 0.16.8 → 0.16.9
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 +2 -1
- package/package.json +7 -7
- package/src/ast.d.ts +6 -1
- package/src/execute/execute.js +30 -0
- package/src/index.d.ts +1 -0
- package/src/parse/extractTables.js +4 -0
- package/src/parse/parse.js +11 -0
- package/src/plan/columns.js +6 -1
- package/src/plan/plan.js +3 -0
- package/src/plan/types.d.ts +6 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
Squirreling is a streaming async SQL engine in pure JavaScript. Built for the browser from the ground up: streaming input and output, pluggable data sources, and lazy async cell evaluation. This makes Squirreling ideal for querying data from network sources, APIs, or LLMs where latency and cost matter.
|
|
14
14
|
|
|
15
|
-
>
|
|
15
|
+
> Powers **[HypAware](https://hypaware.ai/?utm_source=github-readme)**, the improvement loop for all your agents.
|
|
16
16
|
|
|
17
17
|
- **Standard SQL**: Full SQL support for querying data (read-only)
|
|
18
18
|
- **Async UDFs**: User-defined functions can call APIs or models
|
|
@@ -146,6 +146,7 @@ Squirreling mostly follows the SQL standard. The following features are supporte
|
|
|
146
146
|
- `JOIN` operations: `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, `FULL JOIN`, `CROSS JOIN`, `POSITIONAL JOIN`, `LATERAL VIEW [OUTER] EXPLODE(...)`, with `ON` or `USING (col, ...)` conditions
|
|
147
147
|
- `GROUP BY` and `HAVING` clauses, including `GROUP BY ALL`
|
|
148
148
|
- Set operations: `UNION`, `UNION ALL`, `INTERSECT`, `INTERSECT ALL`, `EXCEPT`, `EXCEPT ALL`
|
|
149
|
+
- `SHOW TABLES` to list the available tables
|
|
149
150
|
- Expressions: `CASE`, `CAST`, `TRY_CAST`, `BETWEEN`, `IN`, `LIKE`, `IS NULL`, `IS NOT NULL`, string concatenation `||`
|
|
150
151
|
- Subscript access: zero-based array indexing `col[0]`, struct field access `col['field']`, and chains like `col[0].field`
|
|
151
152
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "squirreling",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.9",
|
|
4
4
|
"description": "Squirreling Async SQL Engine",
|
|
5
5
|
"author": "Hyperparam",
|
|
6
6
|
"homepage": "https://hyperparam.app",
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@eslint/js": "10.0.1",
|
|
43
|
-
"@types/node": "26.6.
|
|
44
|
-
"@vitest/coverage-v8": "5.0.
|
|
45
|
-
"eslint": "10.
|
|
46
|
-
"eslint-plugin-jsdoc": "65.
|
|
47
|
-
"globals": "17.
|
|
43
|
+
"@types/node": "26.6.5",
|
|
44
|
+
"@vitest/coverage-v8": "5.0.4",
|
|
45
|
+
"eslint": "10.12.0",
|
|
46
|
+
"eslint-plugin-jsdoc": "65.3.4",
|
|
47
|
+
"globals": "17.13.0",
|
|
48
48
|
"typescript": "7.0.2",
|
|
49
|
-
"vitest": "5.0.
|
|
49
|
+
"vitest": "5.0.4"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/ast.d.ts
CHANGED
|
@@ -41,7 +41,12 @@ export interface WithStatement extends AstBase {
|
|
|
41
41
|
query: Statement
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// SHOW TABLES statement: lists the names of the available tables
|
|
45
|
+
export interface ShowTablesStatement extends AstBase {
|
|
46
|
+
type: 'show tables'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type Statement = SelectStatement | SetOperationStatement | WithStatement | ShowTablesStatement
|
|
45
50
|
|
|
46
51
|
export interface CTEDefinition extends AstBase {
|
|
47
52
|
name: string
|
package/src/execute/execute.js
CHANGED
|
@@ -37,6 +37,9 @@ const YIELD_INTERVAL = 4000
|
|
|
37
37
|
export function executeSql({ tables, query, functions, signal }) {
|
|
38
38
|
const parsed = typeof query === 'string' ? parseSql({ query, functions }) : query
|
|
39
39
|
|
|
40
|
+
// Listing names does not require table contents or inferred schemas.
|
|
41
|
+
if (parsed.type === 'show tables') return executeShowTables({ tables, signal })
|
|
42
|
+
|
|
40
43
|
// Normalize tables: convert arrays to AsyncDataSource
|
|
41
44
|
/** @type {Record<string, AsyncDataSource>} */
|
|
42
45
|
const normalizedTables = {}
|
|
@@ -97,6 +100,8 @@ export function executePlan({ plan, context }) {
|
|
|
97
100
|
return executeScan(plan, context)
|
|
98
101
|
} else if (plan.type === 'SingleRow') {
|
|
99
102
|
return executeSingleRow()
|
|
103
|
+
} else if (plan.type === 'ShowTables') {
|
|
104
|
+
return executeShowTables(context)
|
|
100
105
|
} else if (plan.type === 'Count') {
|
|
101
106
|
return executeCount(plan, context)
|
|
102
107
|
} else if (plan.type === 'Filter') {
|
|
@@ -148,6 +153,31 @@ function executeSingleRow() {
|
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Executes SHOW TABLES: yields one row per available table name, sorted.
|
|
158
|
+
*
|
|
159
|
+
* @param {Pick<ExecuteSqlOptions, 'tables' | 'signal'>} context
|
|
160
|
+
* @returns {QueryResults}
|
|
161
|
+
*/
|
|
162
|
+
function executeShowTables(context) {
|
|
163
|
+
const columns = ['name']
|
|
164
|
+
const names = Object.keys(context.tables).sort()
|
|
165
|
+
return {
|
|
166
|
+
columns,
|
|
167
|
+
numRows: names.length,
|
|
168
|
+
maxRows: names.length,
|
|
169
|
+
async *rows() {
|
|
170
|
+
for (const name of names) {
|
|
171
|
+
context.signal?.throwIfAborted()
|
|
172
|
+
yield {
|
|
173
|
+
columns,
|
|
174
|
+
cells: { name: () => Promise.resolve(name) },
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
151
181
|
/**
|
|
152
182
|
* Executes a table-valued function (e.g. UNNEST, JSON_EACH).
|
|
153
183
|
* Evaluates the argument once against the outer row (for lateral joins) or an
|
package/src/index.d.ts
CHANGED
|
@@ -46,6 +46,10 @@ function walkStatement(stmt, cteScope, refs) {
|
|
|
46
46
|
for (const o of stmt.orderBy) walkExpr(o.expr, cteScope, refs)
|
|
47
47
|
return
|
|
48
48
|
}
|
|
49
|
+
if (stmt.type === 'show tables') {
|
|
50
|
+
// SHOW TABLES references no source tables
|
|
51
|
+
return
|
|
52
|
+
}
|
|
49
53
|
// select
|
|
50
54
|
if (!stmt.from) {
|
|
51
55
|
// FROM-less SELECT (e.g. `SELECT 1`), no source tables
|
package/src/parse/parse.js
CHANGED
|
@@ -47,6 +47,17 @@ export function parseSql({ query, functions }) {
|
|
|
47
47
|
*/
|
|
48
48
|
export function parseStatement(state) {
|
|
49
49
|
const positionStart = state.lastPos
|
|
50
|
+
const showTok = current(state)
|
|
51
|
+
if (showTok.type === 'identifier' && showTok.value.toUpperCase() === 'SHOW') {
|
|
52
|
+
consume(state)
|
|
53
|
+
const tok = current(state)
|
|
54
|
+
if (tok.type !== 'identifier' || tok.value.toUpperCase() !== 'TABLES') {
|
|
55
|
+
throw parseError(state, 'TABLES')
|
|
56
|
+
}
|
|
57
|
+
consume(state)
|
|
58
|
+
match(state, 'semicolon')
|
|
59
|
+
return { type: 'show tables', positionStart: showTok.positionStart, positionEnd: state.lastPos }
|
|
60
|
+
}
|
|
50
61
|
if (match(state, 'keyword', 'WITH')) {
|
|
51
62
|
const recursiveTok = current(state)
|
|
52
63
|
if (recursiveTok.type === 'identifier' && recursiveTok.value.toUpperCase() === 'RECURSIVE') {
|
package/src/plan/columns.js
CHANGED
|
@@ -40,7 +40,7 @@ export function fromAlias(from) {
|
|
|
40
40
|
*/
|
|
41
41
|
export function statementScope(stmt) {
|
|
42
42
|
if (stmt.type === 'with') return statementScope(stmt.query)
|
|
43
|
-
if (stmt.type === 'compound') return undefined
|
|
43
|
+
if (stmt.type === 'compound' || stmt.type === 'show tables') return undefined
|
|
44
44
|
const alias = fromAlias(stmt.from)
|
|
45
45
|
const joins = stmt.joins.map(j => j.alias ?? j.table)
|
|
46
46
|
return alias === undefined ? joins : [alias, ...joins]
|
|
@@ -329,6 +329,7 @@ export function collectColumnsFromExpr(expr, columns, aliases, context) {
|
|
|
329
329
|
* @param {ColumnCollectionContext} [context]
|
|
330
330
|
*/
|
|
331
331
|
function collectCorrelatedColumnsFromStatement(stmt, columns, context) {
|
|
332
|
+
if (stmt.type === 'show tables') return
|
|
332
333
|
if (stmt.type === 'compound') {
|
|
333
334
|
collectCorrelatedColumnsFromStatement(stmt.left, columns, context)
|
|
334
335
|
collectCorrelatedColumnsFromStatement(stmt.right, columns, context)
|
|
@@ -386,6 +387,7 @@ function collectCorrelatedColumnsFromStatement(stmt, columns, context) {
|
|
|
386
387
|
* @param {ColumnCollectionContext} [context]
|
|
387
388
|
*/
|
|
388
389
|
function collectFromFunctionArgs(stmt, columns, context) {
|
|
390
|
+
if (stmt.type === 'show tables') return
|
|
389
391
|
if (stmt.type === 'compound') {
|
|
390
392
|
collectFromFunctionArgs(stmt.left, columns, context)
|
|
391
393
|
collectFromFunctionArgs(stmt.right, columns, context)
|
|
@@ -414,6 +416,9 @@ function collectFromFunctionArgs(stmt, columns, context) {
|
|
|
414
416
|
* @returns {string[]}
|
|
415
417
|
*/
|
|
416
418
|
export function inferStatementColumns({ stmt, cteColumns, tables }) {
|
|
419
|
+
if (stmt.type === 'show tables') {
|
|
420
|
+
return ['name']
|
|
421
|
+
}
|
|
417
422
|
if (stmt.type === 'with') {
|
|
418
423
|
return inferStatementColumns({ stmt: stmt.query, cteColumns, tables })
|
|
419
424
|
}
|
package/src/plan/plan.js
CHANGED
|
@@ -38,6 +38,9 @@ export function planSql({ query, functions, tables, ctePlans, cteColumns }) {
|
|
|
38
38
|
* @returns {QueryPlan}
|
|
39
39
|
*/
|
|
40
40
|
export function planStatement({ stmt, ctePlans, cteColumns, tables, parentColumns, outerScope }) {
|
|
41
|
+
if (stmt.type === 'show tables') {
|
|
42
|
+
return { type: 'ShowTables' }
|
|
43
|
+
}
|
|
41
44
|
if (stmt.type === 'with') {
|
|
42
45
|
// Build CTE plans in order (each CTE can reference preceding CTEs)
|
|
43
46
|
ctePlans ??= new Map()
|
package/src/plan/types.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type QueryPlan =
|
|
|
15
15
|
| NestedLoopJoinNode
|
|
16
16
|
| PositionalJoinNode
|
|
17
17
|
| SetOperationNode
|
|
18
|
+
| ShowTablesNode
|
|
18
19
|
| SubqueryNode
|
|
19
20
|
| TableFunctionNode
|
|
20
21
|
| WindowNode
|
|
@@ -35,6 +36,11 @@ export interface SingleRowNode {
|
|
|
35
36
|
type: 'SingleRow'
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
// SHOW TABLES: yields one row per available table name
|
|
40
|
+
export interface ShowTablesNode {
|
|
41
|
+
type: 'ShowTables'
|
|
42
|
+
}
|
|
43
|
+
|
|
38
44
|
// Count node for COUNT(*) optimization
|
|
39
45
|
export interface CountNode {
|
|
40
46
|
type: 'Count'
|