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 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
- > Part of **[HypStack](https://hypstack.ai/)**, an open-source stack for AI observability.
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.8",
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.3",
44
- "@vitest/coverage-v8": "5.0.2",
45
- "eslint": "10.11.0",
46
- "eslint-plugin-jsdoc": "65.0.0",
47
- "globals": "17.12.0",
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.2"
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
- export type Statement = SelectStatement | SetOperationStatement | WithStatement
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
@@ -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
@@ -35,6 +35,7 @@ export type {
35
35
  ScanResults,
36
36
  SelectStatement,
37
37
  SetOperationStatement,
38
+ ShowTablesStatement,
38
39
  SqlType,
39
40
  SqlPrimitive,
40
41
  Statement,
@@ -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
@@ -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') {
@@ -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()
@@ -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'