csvql-query 2.5.0 → 2.6.1
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 +44 -3
- package/binaries/darwin-arm64.node +0 -0
- package/binaries/darwin-x64.node +0 -0
- package/binaries/linux-arm64.node +0 -0
- package/binaries/linux-x64.node +0 -0
- package/binaries/win32-x64.node +0 -0
- package/cli.js +142 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
# csvql-query
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**SQL on CSV files, in place.** No database, no import, no ingest — point a query at the file where it already lives. A Zig/SIMD engine via N-API, with prebuilt binaries for macOS (arm64/x64), Linux (x64/arm64), and Windows (x64). No compiler needed.
|
|
4
4
|
|
|
5
|
-
The engine streams
|
|
5
|
+
The engine streams the file instead of loading it, so RAM stays flat — tens of MB — regardless of file size.
|
|
6
|
+
|
|
7
|
+
## Try it without installing anything
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx csvql-query "SELECT city, COUNT(*) AS n FROM 'data.csv' GROUP BY city ORDER BY n DESC"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
File paths go in single quotes inside `FROM`. `npx csvql-query --help` for the rest.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx csvql-query --json "SELECT * FROM 'sales.csv' WHERE amount > 1000 LIMIT 10"
|
|
17
|
+
npx csvql-query -d '\t' "SELECT * FROM 'data.tsv' LIMIT 3"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Exit codes are meant to be branched on: `0` success, `1` usage error, `2` query error, `3` file/IO error.
|
|
6
21
|
|
|
7
22
|
## Install
|
|
8
23
|
|
|
@@ -69,9 +84,35 @@ Operators in `where`: `= != > >= < <=`, combined with `AND` / `OR`. Values are a
|
|
|
69
84
|
|
|
70
85
|
`opts`: `{ delimiter, comment, skipEmptyLines }`. TypeScript declarations are bundled.
|
|
71
86
|
|
|
87
|
+
## For AI agents (MCP)
|
|
88
|
+
|
|
89
|
+
Pasting a 417 MB CSV into an LLM costs ~230 million tokens — it fits in no context window. Over [MCP](https://modelcontextprotocol.io/), an agent queries the file in place and gets back only the answer, for a few hundred tokens.
|
|
90
|
+
|
|
91
|
+
The MCP server ships in the standalone binary rather than this package:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
brew install melihbirim/csvql/csvql
|
|
95
|
+
csvql install # registers csvql with Claude Code + Claude Desktop
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
There's also a one-click `.mcpb` extension for Claude Desktop on the [releases page](https://github.com/melihbirim/csvql/releases). Full setup — VS Code Copilot, Claude Desktop, manual config — in the [main README](https://github.com/melihbirim/csvql#mcp).
|
|
99
|
+
|
|
100
|
+
**Read-only by design.** csvql only runs `SELECT`. It has no `INSERT`/`UPDATE`/`DELETE`/`DROP` and physically cannot modify your data. It makes zero network calls and runs fully air-gapped, so you can put it next to the data instead of shipping files out to an LLM.
|
|
101
|
+
|
|
102
|
+
## Correctness
|
|
103
|
+
|
|
104
|
+
csvql treats [DuckDB](https://duckdb.org) as the reference implementation and diffs its own output against it — because an agent can't eyeball a wrong answer the way a human scanning a spreadsheet can.
|
|
105
|
+
|
|
106
|
+
- **97 differential checks** against DuckDB on every PR, across Linux x86_64 and macOS ARM
|
|
107
|
+
- **553 unit tests**
|
|
108
|
+
- **A differential query generator** runs ~50,000 generated queries against DuckDB nightly; every run appends a row to a public [verification log](https://github.com/melihbirim/csvql/blob/main/VERIFICATION-LOG.md) — wins and losses both
|
|
109
|
+
- Where behavior intentionally differs from DuckDB, it's [documented and audited](https://github.com/melihbirim/csvql/blob/main/CORRECTNESS.md), not hand-waved
|
|
110
|
+
|
|
111
|
+
Unsupported SQL **errors clearly** rather than silently returning wrong data. That distinction is the entire point.
|
|
112
|
+
|
|
72
113
|
## Supported SQL
|
|
73
114
|
|
|
74
|
-
`SELECT`, `WHERE`, `GROUP BY`, `ORDER BY`, `LIMIT`, `JOIN`, `DISTINCT`, `LIKE`,
|
|
115
|
+
`SELECT`, `WHERE`, `GROUP BY`, `HAVING`, `ORDER BY`, `LIMIT`, `JOIN`, `DISTINCT`, `LIKE`, `BETWEEN`, `IN` (including `IN (SELECT ...)` subqueries), `COUNT`/`SUM`/`AVG`/`MIN`/`MAX`, and scalar functions (`UPPER`, `LOWER`, `TRIM`, `LENGTH`, `SUBSTR`, `REPLACE`, `COALESCE`, `CAST`, `ROUND`, …).
|
|
75
116
|
|
|
76
117
|
## License
|
|
77
118
|
|
|
Binary file
|
package/binaries/darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
package/binaries/linux-x64.node
CHANGED
|
Binary file
|
package/binaries/win32-x64.node
CHANGED
|
Binary file
|
package/cli.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// csvql-query CLI — the zero-install entry point.
|
|
5
|
+
//
|
|
6
|
+
// npx csvql-query "SELECT city, COUNT(*) FROM 'data.csv' GROUP BY city"
|
|
7
|
+
//
|
|
8
|
+
// This exists so someone can try csvql in one line without adding a
|
|
9
|
+
// dependency or writing a script. It is a thin wrapper over the same N-API
|
|
10
|
+
// module index.js exports — no query logic lives here, so it cannot drift
|
|
11
|
+
// from the library.
|
|
12
|
+
//
|
|
13
|
+
// For the MCP server (what an AI agent connects to), install the standalone
|
|
14
|
+
// binary: `brew install melihbirim/csvql/csvql` then `csvql install`. That
|
|
15
|
+
// server lives in the Zig binary, not in this package — see --help.
|
|
16
|
+
|
|
17
|
+
const csvql = require('./index.js');
|
|
18
|
+
|
|
19
|
+
const VERSION = require('./package.json').version;
|
|
20
|
+
|
|
21
|
+
const HELP = `csvql-query ${VERSION} — SQL on CSV files, in place
|
|
22
|
+
|
|
23
|
+
USAGE
|
|
24
|
+
npx csvql-query [options] "<SQL>"
|
|
25
|
+
|
|
26
|
+
File paths go in single quotes inside the FROM clause:
|
|
27
|
+
npx csvql-query "SELECT * FROM 'data.csv' LIMIT 5"
|
|
28
|
+
|
|
29
|
+
OPTIONS
|
|
30
|
+
-j, --json Output JSON instead of CSV
|
|
31
|
+
-d, --delimiter <c> Input delimiter (e.g. -d '\\t' for TSV)
|
|
32
|
+
-c, --comment <s> Skip lines starting with <s>
|
|
33
|
+
-s, --skip-empty Skip blank lines
|
|
34
|
+
-h, --help Show this help
|
|
35
|
+
-v, --version Show version
|
|
36
|
+
|
|
37
|
+
EXAMPLES
|
|
38
|
+
npx csvql-query "SELECT * FROM 'sales.csv' WHERE amount > 1000 LIMIT 10"
|
|
39
|
+
npx csvql-query "SELECT region, SUM(amount) FROM 'sales.csv' GROUP BY region"
|
|
40
|
+
npx csvql-query --json "SELECT COUNT(*) AS n FROM 'sales.csv'"
|
|
41
|
+
npx csvql-query -d '\\t' "SELECT * FROM 'data.tsv' LIMIT 3"
|
|
42
|
+
|
|
43
|
+
SUPPORTED SQL
|
|
44
|
+
SELECT, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, JOIN, DISTINCT, LIKE,
|
|
45
|
+
IN (incl. subqueries), BETWEEN, COUNT/SUM/AVG/MIN/MAX, and scalar functions.
|
|
46
|
+
Read-only: there is no INSERT/UPDATE/DELETE/DROP.
|
|
47
|
+
|
|
48
|
+
FOR AI AGENTS (MCP)
|
|
49
|
+
This package is the Node library + CLI. The MCP server ships in the
|
|
50
|
+
standalone binary so an agent can query a multi-GB CSV for a few hundred
|
|
51
|
+
tokens instead of pasting it into context:
|
|
52
|
+
|
|
53
|
+
brew install melihbirim/csvql/csvql # or grab a release binary
|
|
54
|
+
csvql install # registers it with Claude
|
|
55
|
+
|
|
56
|
+
https://github.com/melihbirim/csvql#mcp
|
|
57
|
+
|
|
58
|
+
EXIT CODES
|
|
59
|
+
0 success 1 usage error 2 query error 3 file/IO error
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
function fail(code, msg) {
|
|
63
|
+
process.stderr.write(`csvql-query: ${msg}\n`);
|
|
64
|
+
process.exit(code);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseArgs(argv) {
|
|
68
|
+
const opts = {};
|
|
69
|
+
let sql = null;
|
|
70
|
+
let json = false;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < argv.length; i++) {
|
|
73
|
+
const a = argv[i];
|
|
74
|
+
const needsValue = (name) => {
|
|
75
|
+
const v = argv[++i];
|
|
76
|
+
if (v === undefined) fail(1, `${name} requires a value`);
|
|
77
|
+
return v;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
switch (a) {
|
|
81
|
+
case '-h':
|
|
82
|
+
case '--help':
|
|
83
|
+
process.stdout.write(HELP);
|
|
84
|
+
process.exit(0);
|
|
85
|
+
break;
|
|
86
|
+
case '-v':
|
|
87
|
+
case '--version':
|
|
88
|
+
process.stdout.write(`${VERSION}\n`);
|
|
89
|
+
process.exit(0);
|
|
90
|
+
break;
|
|
91
|
+
case '-j':
|
|
92
|
+
case '--json':
|
|
93
|
+
json = true;
|
|
94
|
+
break;
|
|
95
|
+
case '-d':
|
|
96
|
+
case '--delimiter':
|
|
97
|
+
// Accept a literal backslash-t from the shell as a real tab, so
|
|
98
|
+
// `-d '\t'` works without the caller needing $'\t' quoting.
|
|
99
|
+
opts.delimiter = needsValue(a).replace(/\\t/g, '\t');
|
|
100
|
+
break;
|
|
101
|
+
case '-c':
|
|
102
|
+
case '--comment':
|
|
103
|
+
opts.comment = needsValue(a);
|
|
104
|
+
break;
|
|
105
|
+
case '-s':
|
|
106
|
+
case '--skip-empty':
|
|
107
|
+
opts.skipEmptyLines = true;
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
if (a.startsWith('-') && a !== '-') fail(1, `unknown option: ${a}`);
|
|
111
|
+
if (sql !== null) fail(1, 'more than one SQL statement given (quote the whole query)');
|
|
112
|
+
sql = a;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { sql, opts, json };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function main() {
|
|
120
|
+
const { sql, opts, json } = parseArgs(process.argv.slice(2));
|
|
121
|
+
|
|
122
|
+
if (sql === null || sql.trim() === '') {
|
|
123
|
+
process.stderr.write(HELP);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let out;
|
|
128
|
+
try {
|
|
129
|
+
out = json ? JSON.stringify(csvql.query(sql, opts), null, 2) : csvql.queryCsv(sql, opts);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
// Distinguish "your file is missing" from "your SQL is wrong" so a
|
|
132
|
+
// script (or an agent) can branch on it, matching the standalone
|
|
133
|
+
// binary's exit-code contract.
|
|
134
|
+
const msg = String((err && err.message) || err);
|
|
135
|
+
const isIO = /ENOENT|no such file|FileNotFound|cannot open|EmptyFile/i.test(msg);
|
|
136
|
+
fail(isIO ? 3 : 2, msg);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
process.stdout.write(out.endsWith('\n') ? out : out + '\n');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "csvql-query",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.6.1",
|
|
4
|
+
"description": "SQL on CSV files in place — no import, no database. Zig/SIMD engine for Node and AI agents (MCP). Read-only by design.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"csvql-query": "cli.js"
|
|
9
|
+
},
|
|
7
10
|
"files": [
|
|
8
11
|
"index.js",
|
|
9
12
|
"index.d.ts",
|
|
13
|
+
"cli.js",
|
|
10
14
|
"binaries/"
|
|
11
15
|
],
|
|
12
16
|
"scripts": {
|
|
13
17
|
"build": "zig build node -Doptimize=ReleaseFast && mkdir -p binaries && cp ../zig-out/lib/csvql.node binaries/$(node -p \"process.platform + '-' + process.arch\").node",
|
|
14
18
|
"test": "node test.js",
|
|
19
|
+
"test:cli": "node test_cli.js",
|
|
15
20
|
"bench": "node bench.js"
|
|
16
21
|
},
|
|
17
22
|
"keywords": [
|
|
@@ -24,7 +29,12 @@
|
|
|
24
29
|
"parser",
|
|
25
30
|
"agent",
|
|
26
31
|
"ai",
|
|
27
|
-
"mcp"
|
|
32
|
+
"mcp",
|
|
33
|
+
"llm",
|
|
34
|
+
"cli",
|
|
35
|
+
"duckdb",
|
|
36
|
+
"analytics",
|
|
37
|
+
"dataframe"
|
|
28
38
|
],
|
|
29
39
|
"license": "MIT",
|
|
30
40
|
"repository": {
|