dsq 0.23.0 配置参数详解
dsq 无配置文件,配置面即 11 个命令行 flag(--pretty/--schema/-f/-s/--cache/-i/-n 等)+ 3 个环境变量(DSQ_CACHE/DSQ_CONVERT_NUMBERS/DSQ_NO_SQLITE_WRITER)+ /tmp SHA1 导入缓存机制;逐项语义与实测回显、支持格式全清单、输出格式控制与 REPL 热缓存缺陷等 7 条报错实录。
dsq 0.23.0 配置参数详解
目标:dsq 没有配置文件、没有守护进程,它的全部"配置面"就是 11 个命令行 flag、3 个环境变量,外加
/tmp/dsq-cache-<sha1>.db磁盘缓存机制。本文逐项给出语义、默认值、相互关系与实测回显,并整理支持格式全清单与输出格式控制方法。dsq 二进制安装步骤见 《Ubuntu 22.04 安装 dsq 0.23.0 指南》;查询语法与命令实战见后续 CLI 命令详解篇。
一、环境信息与测试数据
| 项目 | 值 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 22.04 LTS(x86_64) | 操作账号 root |
| 组件 | dsq v0.23.0 | /usr/local/bin/dsq,dsq --version 实测回显 dsq v0.23.0 |
| 配置文件 | 无 | dsq 不读取任何配置文件 |
| 查询引擎 | SQLite(默认内存临时库) | 经 DataStation runner 库驱动 |
| 缓存文件 | /tmp/dsq-cache-<sha1>.db | 仅 --cache/-C/-i 触发产生 |
本文示例统一使用以下测试数据(均可一条命令重建):
$ printf "id,name\n1,Ted\n2,Marjory\n3,Micah\n" > users.csv
$ printf "name,score\nFritz,90\nRainer,95.2\nFountainer,100\n" > scores.csv
$ cat > user_addresses.json <<'EOF'
[
{"name": "Agarrah", "location": {"city": "Toronto", "address": { "number": 1002 }}},
{"name": "Minoara", "location": {"city": "Mexico City", "address": { "number": 19 }}},
{"name": "Fontoon", "location": {"city": "New London", "address": { "number": 12 }}}
]
EOF
$ cat > fields.json <<'EOF'
[
{"field1": [1]},
{"field1": [2]}
]
EOF
$ cat > api-results.json <<'EOF'
{
"data": {
"data": [
{"id": 1, "name": "Corah"},
{"id": 3, "name": "Minh"}
]
},
"total": 2
}
EOF
$ cat > orders.ndjson <<'EOF'
{"id": 1, "item": "keyboard", "qty": 2, "price": 49.9}
{"id": 2, "item": "mouse", "qty": 5, "price": 12.5}
{"id": 3, "item": "monitor", "qty": 1, "price": 219.0}
EOF
$ cat > query.sql <<'EOF'
SELECT name, score FROM {0} WHERE CAST(score AS REAL) > 90 ORDER BY CAST(score AS REAL) DESC
EOF
另有 testdata/userdata.parquet(1000 行用户数据)与 testdata/avro/test_data.avro(1000 行 Avro)两份大文件用于缓存与 schema 演示,可按官方仓库 testdata/ 目录取用。
二、Flag 总览
dsq --help 实测全文回显:
$ dsq --help
dsq (Version v0.23.0) - commandline SQL engine for data files
Usage: dsq [file...] $query
dsq $file [query]
cat $file | dsq -s $filetype [query]
dsq is a tool for running SQL on one or more data files. It uses
SQLite's SQL dialect. Files as tables are accessible via "{N}" where N
is the 0-based index of the file in the commandline.
The shorthand "{}" is replaced with "{0}".
Examples:
# This simply dumps the CSV as JSON
$ dsq test.csv
# This dumps the first 10 rows of the parquet file as JSON.
$ dsq data.parquet "SELECT * FROM {} LIMIT 10"
# This joins two datasets of differing origin types (CSV and JSON).
$ dsq testdata/join/users.csv testdata/join/ages.json \
"select {0}.name, {1}.age from {0} join {1} on {0}.id = {1}.id"
See the repo for more details: https://github.com/multiprocessio/dsq
帮助文本不列参数表,完整 flag 集合以源码 main.go 实查为准,共 11 个:
| Flag | 短名 | 语义 | 默认值 |
|---|---|---|---|
--pretty | -p | 结果输出为 ASCII 表格 | 关(输出 JSON) |
--schema | -c | 打印推断出的数据 schema 并退出,不执行查询 | 关 |
--file | -f | 从文件读取 SQL 查询(替代位置参数) | 无 |
| (无长名) | -s | 声明 stdin 管道输入的数据类型(扩展名或 MIME) | 无(文件输入时靠扩展名识别) |
--cache | -C | 把导入的 SQLite 库持久化到 /tmp,跨查询复用 | 关(每次内存库重建) |
--cache-file | -D | 只打印该查询对应的缓存文件路径并退出 | 关 |
--interactive | -i | 进入交互式 REPL(强制开启 pretty + cache) | 关 |
--convert-numbers | -n | CSV/TSV 中的数值字符串自动转为数字类型 | 关(全按字符串导入) |
--version | — | 打印版本并退出 | — |
--help | — | 打印帮助并退出 | — |
优先级关系:命令行 flag 显式开启 > 对应环境变量 > 内置默认。三个环境变量与 flag 一一呼应(见第四章),DSQ_NO_SQLITE_WRITER 则无对应 flag。
三、各 Flag 详解
3.1 -p / --pretty:ASCII 表格输出
默认 JSON 输出为逐行对象流(机器友好、最高效);--pretty 切换为带边框 ASCII 表格,适合人眼核对:
$ dsq -p users.csv "SELECT * FROM {}"
+----+---------+
| id | name |
+----+---------+
| 1 | Ted |
| 2 | Marjory |
| 3 | Micah |
+----+---------+
(3 rows)
长名 --pretty 与短名 -p 等价(实测回显一致)。表格末尾的 (N rows) 行数统计仅 pretty 模式提供。
💡 技术拓展:JSON 模式下对象内键序不保证稳定(官方 README "Output column order" 明示 unordered),同一查询两次执行键序可能不同;需要稳定键序时管道接
jq --sort-keys排序(实测有效),这也是 dsq 官方推荐做法。
3.2 -c / --schema:打印推断 schema
只输出 dsq 对输入文件的类型推断结果(JSON 树),不执行查询。对 users.csv:
$ dsq --schema users.csv
{
"kind": "array",
"array": {
"kind": "object",
"object": {
"id": {
"kind": "scalar",
"scalar": "string"
},
"name": {
"kind": "scalar",
"scalar": "string"
}
}
}
}
要点:CSV 未开 -n 时所有列推断为 string(见 3.7);嵌套 JSON 会展开对象层级——对 user_addresses.json 的实测完整输出中 location 为 kind: object,其下 city 为 string、address.number 为 number:
$ dsq --schema user_addresses.json
{
"kind": "array",
"array": {
"kind": "object",
"object": {
"location": {
"kind": "object",
"object": {
"city": {
"kind": "scalar",
"scalar": "string"
},
"address": {
"kind": "object",
"object": {
"number": {
"kind": "scalar",
"scalar": "number"
}
}
}
}
},
"name": {
"kind": "scalar",
"scalar": "string"
}
}
}
}
--schema 与 --pretty 可叠加,Avro 等复杂 schema 会以可读文本而非 JSON 树呈现(对 1000 行 Avro 文件的实测节选):
$ dsq testdata/avro/test_data.avro --schema --pretty
Array of
Object of
birthdate of
string
cc of
Varied of
Object of
long of
number or
Unknown
comments of
string
...
title of
string
Varied of 表示该列在不同行中类型不一致(混合类型),Unknown 表示无法推断——排查脏数据时先看这两个标记。
3.3 -f / --file:从文件读取查询
复杂 SQL 不必塞进命令行位置参数,写入文件后引用:
$ cat query.sql
SELECT name, score FROM {0} WHERE CAST(score AS REAL) > 90 ORDER BY CAST(score AS REAL) DESC
$ dsq scores.csv -f query.sql | cat
[{"score":"100","name":"Fountainer"},
{"score":"95.2","name":"Rainer"}]
-f 之后查询位置参数省略;文件内仍可用 {N}/{} 表引用语法。
3.4 -s:声明 stdin 输入类型
管道输入没有扩展名可依据,必须用 -s 给出扩展名或 MIME 类型。实测 CSV:
$ cat scores.csv | dsq -s csv "SELECT * FROM {} LIMIT 2" | cat
[{"name":"Fritz","score":"90"},
{"name":"Rainer","score":"95.2"}]
实测 nginx 访问日志(无扩展名概念,只能管道 + -s,输出节选,实测首尾):
$ cat access.log | dsq -s text/nginxaccess "SELECT * FROM {}" | cat
[{"agent":null,"method":"GET","path":"/apache_pb.gif","referer":null,"remote":"127.0.0.1","size":"2326","time":"10/Oct/2000:13:55:36 -0700","user":"frank","code":"200","host":"-","http_x_forwarded_for":null},
{"time":"10/Oct/2000:14:02:11 -0700","agent":null,"code":"500","host":"-","http_x_forwarded_for":null,"path":"/api/order","referer":null,"remote":"192.168.1.5","method":"POST","size":"0","user":"-"},
{"path":"/index.html","remote":"10.0.0.8","size":"128","agent":null,"host":"-","http_x_forwarded_for":null,"method":"GET","referer":null,"time":"10/Oct/2000:14:10:05 -0700","user":"alice","code":"404"}]
解析出的列名为 agent/code/host/http_x_forwarded_for/method/path/referer/remote/size/time/user(实测 11 列)。
支持类型全清单(官方 README "Supported Data Types" 表,查询日期 2026-11-03):
| 名称 | 扩展名 | MIME | 备注 |
|---|---|---|---|
| CSV | csv | text/csv | |
| TSV | tsv, tab | text/tab-separated-values | |
| JSON | json | application/json | 须为对象数组,或注明数组路径 |
| Newline-delimited JSON | ndjson, jsonl | application/jsonlines | |
| Concatenated JSON | cjson | application/jsonconcat | |
| ORC | orc | orc | |
| Parquet | parquet | parquet | |
| Avro | avro | application/avro | |
| YAML | yaml, yml | application/yaml | |
| Excel | xlsx, xls | application/vnd.ms-excel | 多 sheet 须指定 sheet 路径 |
| ODS | ods | application/vnd.oasis.opendocument.spreadsheet | 多 sheet 同上 |
| Apache Error Logs | — | text/apache2error | 仅支持管道输入 |
| Apache Access Logs | — | text/apache2access | 仅支持管道输入 |
| Nginx Access Logs | — | text/nginxaccess | 仅支持管道输入 |
| LogFmt Logs | logfmt | text/logfmt |
文件扩展名不在清单内时直接拒绝(实测):
$ dsq unknown.xyz "SELECT * FROM {}"
Unknown mimetype for file: unknown.xyz.
$ echo $?
1
3.5 --cache / -C 与 --cache-file / -D:导入缓存
默认每次查询都把文件重新导入内存 SQLite;--cache 把导入结果持久化成磁盘 SQLite 库,跨查询复用。缓存的是导入而非查询结果——换查询语句只要文件没变依然命中(官方 README "Caching" 明示)。实测对 1000 行 Parquet 连续两次 --cache 查询:
$ time dsq testdata/userdata.parquet --cache "SELECT COUNT(1) FROM {}" | cat
[{"COUNT(1)":1000}]
real 0m0.022s
user 0m0.018s
sys 0m0.009s
$ time dsq testdata/userdata.parquet --cache "SELECT COUNT(1) FROM {}" | cat
[{"COUNT(1)":1000}]
real 0m0.026s
user 0m0.019s
sys 0m0.012s
上两次运行均为缓存复用(无重导入提示)。缓存不存在或文件集合的 SHA1 变化时触发重新导入,实测回显(短名 -C 与 --cache 等价):
$ dsq testdata/userdata.parquet -C "SELECT COUNT(1) FROM {}" | cat
Cache invalid, re-import required.
[{"COUNT(1)":1000}]
--cache-file(短名 -D)只打印本次查询会用的缓存文件路径并退出,即使带查询也不执行:
$ dsq testdata/userdata.parquet --cache-file "SELECT COUNT(1) FROM {}" | cat
/tmp/dsq-cache-f683ce9b49590888a5c9c24d8aa10013ad8ceadf.db
该路径按输入文件集合的 SHA1 命名(官方 README:"calculates a SHA1 sum of all the files you specified. If the sum ever changes then it will reimport")。
3.6 -i / --interactive:REPL 交互模式
-i 进入交互 REPL,强制开启 pretty 与 cache,可连续执行多条 SQL(官方 README 示例提示符为 dsq>)。管道喂入两条查询加一条 .exit 的实测回显:
$ printf "SELECT COUNT(1) FROM {};\nSELECT name FROM {} WHERE id = 1;\n.exit\n" | dsq users.csv -i
Cache invalid, re-import required.
+----------+
| COUNT(1) |
+----------+
| 3 |
+----------+
(1 row)
+------+
| name |
+------+
| Ted |
+------+
(1 row)
near ".": syntax error
$ echo $?
1
两个实测坑(详见第七章):
- 退出用
Ctrl+D,.exit会被当 SQL 执行报near ".": syntax error; - ⚠️ 同一文件第二次进入 REPL 直接失败——首跑已生成缓存库,再进
-i时向既有库重复建表:
$ printf "SELECT COUNT(1) FROM {};\n" | dsq users.csv -i
table "t_0" already exists
$ echo $?
1
处理办法:进 REPL 前先删对应缓存文件(rm -f /tmp/dsq-cache-*.db 或按 --cache-file 打印的精确路径删),删除后首跑即恢复正常(实测)。第七章 .help 报错行同此根因。
3.7 -n / --convert-numbers:CSV/TSV 数值转换
CSV/TSV 不带类型信息,默认全部按字符串导入——数值列的排序、比较都会变成字典序。对比实测(scores.csv 按 score 排序):
$ dsq scores.csv "SELECT * FROM {} ORDER BY score" | cat
[{"name":"Fountainer","score":"100"},
{"name":"Fritz","score":"90"},
{"name":"Rainer","score":"95.2"}]
"100" < "90" < "95.2" 是字符串比较的结果。开启 -n 后:
$ dsq scores.csv --convert-numbers "SELECT * FROM {} ORDER BY score" | cat
[{"name":"Fritz","score":90},
{"name":"Rainer","score":95.2},
{"name":"Fountainer","score":100]}]
数值自动转 int/float(JSON 输出不再带引号),排序按数值大小。官方 README 注明开启此选项会关闭部分导入优化;对 JSON/Parquet 等自带类型的格式无必要开启。
四、环境变量
3 个环境变量,识别值均为字符串 true,语义与对应 flag 相同(源码 main.go 与官方 README 实查)。
4.1 DSQ_CACHE(等价 --cache)
全局默认开启导入缓存,适合频繁探索同一批数据的会话;写入 ~/.bashrc 即永久生效(官方 README 推荐做法)。实测:
$ DSQ_CACHE=true dsq testdata/userdata.parquet "SELECT COUNT(1) FROM {}" | cat
[{"COUNT(1)":1000}]
4.2 DSQ_CONVERT_NUMBERS(等价 -n)
全局默认开启 CSV/TSV 数值转换。实测(对照 3.7 的字符串序):
$ DSQ_CONVERT_NUMBERS=true dsq scores.csv "SELECT * FROM {} ORDER BY score" | cat
[{"name":"Fritz","score":90},
{"score":95.2,"name":"Rainer"},
{"name":"Fountainer","score":100}]
4.3 DSQ_NO_SQLITE_WRITER(无对应 flag)
源码级开关(README 未记载),置 true 禁用"直接以 SQLite writer 写库"的快速导入路径,回退常规导入。实测输出与默认路径完全一致:
$ DSQ_NO_SQLITE_WRITER=true dsq users.csv | cat
[{"id":"1","name":"Ted"},
{"id":"2","name":"Marjory"},
{"id":"3","name":"Micah"}]
用途是对照排查导入路径相关缺陷,日常无需设置。
五、缓存机制与缓存文件
- 位置与命名:
/tmp/dsq-cache-<sha1>.db,一个输入文件集合对应一个库文件。实测目录状态:
$ ls -la /tmp/dsq-cache-*.db
-rw-r--r-- 1 root root 8192 Nov 3 03:18 /tmp/dsq-cache-6d9658d2e50acadd7b2060245fda9e94740dfb06.db
-rw-r--r-- 1 root root 4096 Nov 3 03:18 /tmp/dsq-cache-f683ce9b49590888a5c9c24d8aa10013ad8ceadf.db
- 键与失效:SHA1 由指定文件集合计算,文件变则失效重导(回显
Cache invalid, re-import required.);查询语句变化不影响命中。 - 库结构:缓存文件是标准 SQLite 数据库,每个输入文件对应一张表
t_0、t_1……({N}表引用的实体)。实测用 sqlite3 打开 Parquet 的缓存库:
$ sqlite3 /tmp/dsq-cache-f683ce9b49590888a5c9c24d8aa10013ad8ceadf.db ".tables"
t_0
$ sqlite3 /tmp/dsq-cache-f683ce9b49590888a5c9c24d8aa10013ad8ceadf.db ".schema t_0"
CREATE TABLE IF NOT EXISTS "t_0" ("Activated" BOOLEAN, "Company9" TEXT, "Created32At32" REAL, "Department" TEXT, "Email" TEXT, "PARGO_PREFIX_32323232City32" TEXT, "PARGO_PREFIX_3232Description" TEXT, "PARGO_PREFIX_32Name32" TEXT, "Phone32Number32" TEXT, "Profile32Photo" TEXT, "Routing32Number323232" REAL, "State" TEXT, "Street" TEXT, "Zip32Code32" TEXT);
可直接拿 sqlite3 对缓存库做二次分析;列名中的 PARGO_PREFIX_、32 等是 Parquet 嵌套字段扁平化的产物。
- 清理:dsq 不自动回收 /tmp 缓存,磁盘敏感环境定期
rm -f /tmp/dsq-cache-*.db(或依赖 /tmp 重启清空)。
六、输出格式控制
| 形态 | 触发方式 | 特点 |
|---|---|---|
| JSON 对象流(默认) | 不加参数 | 每行一个对象、整体合法 JSON 数组;最高效;键序不保证 |
| ASCII 表格 | --pretty/-p 或 -i | 带边框与 (N rows) 行数;不受重定向缺陷影响 |
| 美化 JSON | 默认输出接 | jq | 官方推荐的人读方案,键序可用 jq --sort-keys 固定 |
⚠️ 实测缺陷(v0.23.0):默认 JSON 模式向 stdout 写数据使用 copy_file_range,追加重定向 >> 会失败:
$ dsq users.csv >> out2.json
write /dev/stdout: copy_file_range: bad file descriptor
$ echo $?
1
覆盖重定向 > 与管道(| cat、| jq)均正常(实测 dsq users.csv > out1.json exit=0,落盘内容完整)。脚本里追加落盘统一经 cat 中转,实测连跑两次后文件里是两段完整 JSON 数组:
$ dsq users.csv | cat >> out2.json
$ dsq users.csv | cat >> out2.json
$ cat out2.json
[{"id":"1","name":"Ted"},
{"id":"2","name":"Marjory"},
{"id":"3","name":"Micah"}]
[{"id":"1","name":"Ted"},
{"id":"2","name":"Marjory"},
{"id":"3","name":"Micah"}]
七、参数相关常见报错(实测)
| 场景命令 | 实测报错 | 原因与处理 |
|---|---|---|
dsq unknown.xyz "SELECT * FROM {}" | Unknown mimetype for file: unknown.xyz. | 扩展名不在支持清单;管道输入改 -s 声明类型 |
dsq users.csv "SELCT 1" | near "SELCT": syntax error | SQL 拼写错误,dsq 原样透传 SQLite 解析器报错 |
dsq users.csv "SELECT * FROM {5}" | no such table: (表名为空) | {N} 越界——只传了 1 个文件却引用 {5} |
dsq missing.csv "SELECT * FROM {}" | open missing.csv: no such file or directory | 文件不存在,先核对路径 |
dsq testdata/userdata.parquet "SELECT SUM(id) FROM {}" | no such column: id | 该 Parquet 无 id 列;先用 --schema 查真实列名 |
printf ".exit\n" | dsq users.csv -i | near ".": syntax error | REPL 不支持 sqlite3 式点命令,退出按 Ctrl+D |
printf ".help\n" | dsq users.csv -i | table "t_0" already exists | 根因同 3.6 坑 2:既有缓存库被重复建表(与输入内容无关,任何语句都可能触发);先删 /tmp/dsq-cache-*.db 再进 REPL |
八、参考链接
- dsq 源码仓库(含 README 与 main.go flag 定义):https://github.com/multiprocessio/dsq
- go-sqlite3-stdlib 扩展函数清单:https://github.com/multiprocessio/go-sqlite3-stdlib
- SQLite SQL 方言:https://www.sqlite.org/lang.html
- jq 手册:https://jqlang.github.io/jq/manual/