@carllee1983/dbcli 1.6.0 → 1.7.0
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/CHANGELOG.md +12 -0
- package/README.md +58 -0
- package/README.zh-TW.md +54 -0
- package/assets/SKILL.md +42 -0
- package/assets/reference.md +142 -3
- package/assets/snippets/.keep +0 -0
- package/assets/snippets/README.md +18 -0
- package/assets/snippets/diag/cache-hit.mysql.sql +19 -0
- package/assets/snippets/diag/cache-hit.postgres.sql +18 -0
- package/assets/snippets/diag/connections.mysql.sql +16 -0
- package/assets/snippets/diag/connections.postgres.sql +16 -0
- package/assets/snippets/diag/db-size.mysql.sql +10 -0
- package/assets/snippets/diag/db-size.postgres.sql +9 -0
- package/assets/snippets/diag/index-usage.mysql.sql +15 -0
- package/assets/snippets/diag/index-usage.postgres.sql +14 -0
- package/assets/snippets/diag/locks.mysql.sql +14 -0
- package/assets/snippets/diag/locks.postgres.sql +15 -0
- package/assets/snippets/diag/long-running.mysql.sql +14 -0
- package/assets/snippets/diag/long-running.postgres.sql +19 -0
- package/assets/snippets/diag/missing-indexes.mysql.sql +13 -0
- package/assets/snippets/diag/missing-indexes.postgres.sql +15 -0
- package/assets/snippets/diag/table-sizes.mysql.sql +14 -0
- package/assets/snippets/diag/table-sizes.postgres.sql +13 -0
- package/dist/cli.mjs +1859 -179
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to dbcli are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.7.0] - 2026-05-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `dbcli q @<name>` 執行已保存的參數化 SELECT 片段
|
|
13
|
+
- `dbcli queries list/show/new/edit/check` 管理片段
|
|
14
|
+
- 兩層片段儲存:`.dbcli-shared/queries/`(共享)+ `.dbcli/queries/`(個人覆蓋)
|
|
15
|
+
- 完整安全 invariants:拒絕非 SELECT/WITH、多語句、`${...}` / `{{...}}` 模板語法
|
|
16
|
+
- 子查詢式 size guard 包裹 (`SELECT * FROM (...) AS _dbcli_guard LIMIT 1000`)
|
|
17
|
+
- 內建 YAML 子集 frontmatter parser(無新增 npm 依賴)
|
|
18
|
+
- `queries list/show --format json` 為未來 MCP server 預留契約
|
|
19
|
+
|
|
8
20
|
## [1.6.0] - 2026-04-23
|
|
9
21
|
|
|
10
22
|
### Added
|
package/README.md
CHANGED
|
@@ -114,6 +114,8 @@ dbcli query '{"status":"active"}' --collection users --use atlas
|
|
|
114
114
|
|
|
115
115
|
For MongoDB, `list` and `query` operate on the database configured for the connection, and `query` requires `--collection <name>`.
|
|
116
116
|
|
|
117
|
+
For a command-by-command support matrix across PostgreSQL, MySQL, MariaDB, and MongoDB, see [docs/feature-matrix.md](./docs/feature-matrix.md).
|
|
118
|
+
|
|
117
119
|
---
|
|
118
120
|
|
|
119
121
|
## Multi-connection Support (v2)
|
|
@@ -769,6 +771,43 @@ dbcli migrate drop-enum status --execute --force
|
|
|
769
771
|
|
|
770
772
|
---
|
|
771
773
|
|
|
774
|
+
## Query Risk Planning
|
|
775
|
+
|
|
776
|
+
Use `plan` to inspect SQL safety before execution. It reads local dbcli config, permissions, blacklist rules, and cached schema metadata only; it does not connect to the database.
|
|
777
|
+
|
|
778
|
+
```bash
|
|
779
|
+
dbcli plan "UPDATE users SET status='inactive'" --format json
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Decisions are:
|
|
783
|
+
|
|
784
|
+
- `ALLOW` — no obvious risk was detected.
|
|
785
|
+
- `WARN` — inspect warnings before executing.
|
|
786
|
+
- `BLOCK` — unsafe, unsupported, or violates configured safety constraints.
|
|
787
|
+
|
|
788
|
+
Text output is concise for humans:
|
|
789
|
+
|
|
790
|
+
```text
|
|
791
|
+
Decision: BLOCK
|
|
792
|
+
Operation: UPDATE
|
|
793
|
+
Target tables: users
|
|
794
|
+
|
|
795
|
+
Risk factors:
|
|
796
|
+
- UPDATE statement has no WHERE clause.
|
|
797
|
+
|
|
798
|
+
Recommendations:
|
|
799
|
+
- Add a WHERE clause.
|
|
800
|
+
- Use --dry-run on the actual write command.
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
JSON output includes `suggestedCommands` for agents:
|
|
804
|
+
|
|
805
|
+
```bash
|
|
806
|
+
dbcli plan "SELECT id FROM users WHERE id = 1 LIMIT 1" --format json
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
772
811
|
## Global Options
|
|
773
812
|
|
|
774
813
|
All commands support these global options:
|
|
@@ -957,6 +996,22 @@ A Query-only agent cannot write to any table, and also cannot read blacklisted t
|
|
|
957
996
|
|
|
958
997
|
---
|
|
959
998
|
|
|
999
|
+
## Saved queries
|
|
1000
|
+
|
|
1001
|
+
Save parameterised SELECT snippets and re-run them by name:
|
|
1002
|
+
|
|
1003
|
+
```bash
|
|
1004
|
+
dbcli queries list
|
|
1005
|
+
dbcli queries show @dau
|
|
1006
|
+
dbcli q @dau --param days=30 --format json
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
Snippets live in `.dbcli-shared/queries/` (committed) or `.dbcli/queries/`
|
|
1010
|
+
(gitignored, personal override). Each `.sql` file declares its frontmatter
|
|
1011
|
+
in a `-- ---` block. Read `assets/reference.md` for the full schema.
|
|
1012
|
+
|
|
1013
|
+
---
|
|
1014
|
+
|
|
960
1015
|
## AI Integration Guide
|
|
961
1016
|
|
|
962
1017
|
dbcli ships AI-consumable skill files (`assets/SKILL.md` and `assets/reference.md`) and can copy them into your favorite AI tool directories.
|
|
@@ -1258,12 +1313,15 @@ chmod +x dist/cli.mjs
|
|
|
1258
1313
|
|
|
1259
1314
|
```bash
|
|
1260
1315
|
bun test # full test suite (Bun test runner)
|
|
1316
|
+
bun run typecheck # TypeScript compile-time validation
|
|
1261
1317
|
bun run test:unit # unit + core tests only
|
|
1262
1318
|
bun run test:integration # integration tests
|
|
1263
1319
|
bun run test:docker # integration tests with docker-compose.test.yml (MySQL + PostgreSQL)
|
|
1264
1320
|
bun run build # bundle CLI to dist/ (used before publish)
|
|
1265
1321
|
```
|
|
1266
1322
|
|
|
1323
|
+
CI treats `bun run typecheck` and `bun test` as the required pass/fail validation gate on every push and pull request. Lint, build, smoke checks, and benchmarks run in addition to that gate.
|
|
1324
|
+
|
|
1267
1325
|
Live database integration tests use `.dbcli/config.json` by default. If your live
|
|
1268
1326
|
config lives elsewhere, set `LIVE_DB_CONFIG_PATH=/path/to/.dbcli` before running:
|
|
1269
1327
|
|
package/README.zh-TW.md
CHANGED
|
@@ -779,6 +779,43 @@ dbcli migrate drop-enum status --execute --force
|
|
|
779
779
|
|
|
780
780
|
---
|
|
781
781
|
|
|
782
|
+
## 查詢風險規劃
|
|
783
|
+
|
|
784
|
+
使用 `plan` 在執行前檢查 SQL 安全性。它只讀取本機 dbcli 設定、權限、黑名單規則與已快取的 schema metadata;不會連線到資料庫。
|
|
785
|
+
|
|
786
|
+
```bash
|
|
787
|
+
dbcli plan "UPDATE users SET status='inactive'" --format json
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
決策結果:
|
|
791
|
+
|
|
792
|
+
- `ALLOW` — 未偵測到明顯風險。
|
|
793
|
+
- `WARN` — 執行前應檢查警告。
|
|
794
|
+
- `BLOCK` — 不安全、不支援,或違反安全設定。
|
|
795
|
+
|
|
796
|
+
文字輸出保持精簡,適合人工閱讀:
|
|
797
|
+
|
|
798
|
+
```text
|
|
799
|
+
Decision: BLOCK
|
|
800
|
+
Operation: UPDATE
|
|
801
|
+
Target tables: users
|
|
802
|
+
|
|
803
|
+
Risk factors:
|
|
804
|
+
- UPDATE statement has no WHERE clause.
|
|
805
|
+
|
|
806
|
+
Recommendations:
|
|
807
|
+
- Add a WHERE clause.
|
|
808
|
+
- Use --dry-run on the actual write command.
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
JSON 輸出會包含給 AI agent 使用的 `suggestedCommands`:
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
dbcli plan "SELECT id FROM users WHERE id = 1 LIMIT 1" --format json
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
782
819
|
## 全域選項
|
|
783
820
|
|
|
784
821
|
所有指令皆支援下列全域選項:
|
|
@@ -963,6 +1000,23 @@ Query-only 代理無法寫入任何表,也無法讀取黑名單表或欄位
|
|
|
963
1000
|
|
|
964
1001
|
---
|
|
965
1002
|
|
|
1003
|
+
## 已保存查詢片段(Saved queries)
|
|
1004
|
+
|
|
1005
|
+
將參數化的 SELECT 片段保存於版控,並依名稱重複執行:
|
|
1006
|
+
|
|
1007
|
+
```bash
|
|
1008
|
+
dbcli queries list
|
|
1009
|
+
dbcli queries show @dau
|
|
1010
|
+
dbcli q @dau --param days=30 --format json
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
片段位於兩層儲存:`.dbcli-shared/queries/`(commit 進版控、團隊共享)與
|
|
1014
|
+
`.dbcli/queries/`(gitignore、個人覆蓋)。每個 `.sql` 檔以 `-- ---` 區塊
|
|
1015
|
+
宣告 frontmatter(name、description、engine、params、tags),完整 schema 見
|
|
1016
|
+
`assets/reference.md`。
|
|
1017
|
+
|
|
1018
|
+
---
|
|
1019
|
+
|
|
966
1020
|
## AI 整合指南
|
|
967
1021
|
|
|
968
1022
|
dbcli 內建供 AI 使用的 skill 文件(`assets/SKILL.md` 與 `assets/reference.md`),並可複製到常見 AI 開發工具的目錄。
|
package/assets/SKILL.md
CHANGED
|
@@ -74,6 +74,48 @@ dbcli query "SELECT * FROM users" # Execute SQL (auto LIMIT 1000)
|
|
|
74
74
|
- No auto-limit on MongoDB queries — use `$limit` in the pipeline if needed.
|
|
75
75
|
- See reference.md MongoDB section for full syntax and examples.
|
|
76
76
|
|
|
77
|
+
## Saved queries
|
|
78
|
+
|
|
79
|
+
Run reusable parameterised SELECT snippets stored in your repo.
|
|
80
|
+
|
|
81
|
+
| Step | Command |
|
|
82
|
+
|------|---------|
|
|
83
|
+
| 1. Discover | `dbcli queries list` |
|
|
84
|
+
| 2. Inspect | `dbcli queries show @<name>` |
|
|
85
|
+
| 3. Run | `dbcli q @<name> --param k=v` |
|
|
86
|
+
|
|
87
|
+
Snippets resolve from three layers, **local > shared > builtin** (local wins):
|
|
88
|
+
- `builtin` — bundled with dbcli (e.g. `@diag/*`); read-only at runtime
|
|
89
|
+
- `.dbcli-shared/queries/` — committed, team-shared
|
|
90
|
+
- `.dbcli/queries/` — gitignored, personal override
|
|
91
|
+
|
|
92
|
+
Manage local snippets with `queries new | edit | delete | rename | copy | import | export`
|
|
93
|
+
(see reference.md). Use `copy` / `import` to fork a builtin or shared snippet into the
|
|
94
|
+
local layer for editing.
|
|
95
|
+
|
|
96
|
+
Each `.sql` file may declare YAML frontmatter inside `-- ---` blocks
|
|
97
|
+
(name, description, engine, params, tags). See `dbcli queries show @<name> --format json`
|
|
98
|
+
for the machine-readable contract.
|
|
99
|
+
|
|
100
|
+
### Built-in diagnostic snippets
|
|
101
|
+
|
|
102
|
+
dbcli ships ready-made diagnostic queries. Run with `dbcli q @diag/<topic>`:
|
|
103
|
+
|
|
104
|
+
| key | purpose |
|
|
105
|
+
|-------------------------|------------------------------------------|
|
|
106
|
+
| `@diag/connections` | active sessions |
|
|
107
|
+
| `@diag/long-running` | queries above `min_seconds` (default 30) |
|
|
108
|
+
| `@diag/table-sizes` | table data/index size with row counts |
|
|
109
|
+
| `@diag/index-usage` | indexes by scan count |
|
|
110
|
+
| `@diag/missing-indexes` | tables dominated by sequential scans |
|
|
111
|
+
| `@diag/locks` | lock-wait chains |
|
|
112
|
+
| `@diag/db-size` | database size summary |
|
|
113
|
+
| `@diag/cache-hit` | buffer cache hit ratios |
|
|
114
|
+
|
|
115
|
+
Engine variants are picked automatically based on the active connection.
|
|
116
|
+
Override any of them by placing a same-named file under `.dbcli-shared/queries/`
|
|
117
|
+
or `.dbcli/queries/`.
|
|
118
|
+
|
|
77
119
|
## Common workflows
|
|
78
120
|
|
|
79
121
|
- **Debug odd state:** `schema` → `check` → `query` with tight `WHERE` → follow FKs from schema JSON. Evidence over theory.
|
package/assets/reference.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Companion to [SKILL.md](SKILL.md). Exhaustive flags, copy-paste examples, `shell`, `completion`, `upgrade`, `migrate` DDL, and extended MongoDB examples.
|
|
4
4
|
|
|
5
|
+
For cross-engine support status, see `docs/feature-matrix.md` in the repository.
|
|
6
|
+
|
|
5
7
|
## Commands
|
|
6
8
|
|
|
7
9
|
### init
|
|
@@ -114,6 +116,126 @@ dbcli query '[{"$match": {"status": "active"}}, {"$group": {"_id": "$role", "cou
|
|
|
114
116
|
> - `--collection <name>` is required
|
|
115
117
|
> - Auto-limit does not apply; use `$limit` in your pipeline if needed
|
|
116
118
|
|
|
119
|
+
### q
|
|
120
|
+
|
|
121
|
+
Run a saved query snippet by `@name`. Snippets are parameterised SELECT/WITH statements resolved from three layers, with **local > shared > builtin** precedence (a local file always shadows shared and builtin variants of the same key):
|
|
122
|
+
|
|
123
|
+
- `builtin` — bundled with dbcli (e.g. `@diag/*`); read-only at runtime.
|
|
124
|
+
- `.dbcli-shared/queries/` — committed, team-shared.
|
|
125
|
+
- `.dbcli/queries/` — gitignored, personal override.
|
|
126
|
+
|
|
127
|
+
Engine variants (`name.postgres.sql` / `name.mysql.sql`) at the same layer are merged; the variant matching the active connection's engine is selected at execution time.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
dbcli q @dau # run with declared defaults
|
|
131
|
+
dbcli q @dau --param days=30 --format json # override a param
|
|
132
|
+
dbcli q @analytics/revenue --param-file params.json
|
|
133
|
+
dbcli q @dau --dry-run # show final SQL + bind values
|
|
134
|
+
dbcli q @dau --no-limit # disable size guard wrap
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Options:**
|
|
138
|
+
- `--format <table|json|csv>` — output format (default: `table`)
|
|
139
|
+
- `--param <key=value>` — pass a parameter (repeatable)
|
|
140
|
+
- `--param-file <path>` — JSON object whose keys are param names
|
|
141
|
+
- `--no-limit` — skip the `SELECT * FROM (…) AS _dbcli_guard LIMIT 1000` wrap
|
|
142
|
+
- `--dry-run` — print the bound SQL + values without executing
|
|
143
|
+
- `--use <name>` — pick a v2 named connection
|
|
144
|
+
|
|
145
|
+
**Permission:** query-only+
|
|
146
|
+
|
|
147
|
+
#### Snippet file format
|
|
148
|
+
|
|
149
|
+
Each `.sql` file is plain SQL with optional YAML frontmatter inside a leading `-- ---` block. Lines outside frontmatter form the SQL body.
|
|
150
|
+
|
|
151
|
+
```sql
|
|
152
|
+
-- ---
|
|
153
|
+
-- name: DAU
|
|
154
|
+
-- description: Daily Active Users
|
|
155
|
+
-- engine: postgres # or [postgres, mysql]
|
|
156
|
+
-- params:
|
|
157
|
+
-- days:
|
|
158
|
+
-- type: int # int | string | float | bool | date | datetime
|
|
159
|
+
-- default: 7
|
|
160
|
+
-- required: false
|
|
161
|
+
-- description: lookback window in days
|
|
162
|
+
-- enum: [7, 30, 90]
|
|
163
|
+
-- tags: [analytics]
|
|
164
|
+
-- ---
|
|
165
|
+
SELECT COUNT(DISTINCT user_id) AS dau
|
|
166
|
+
FROM events
|
|
167
|
+
WHERE created_at > NOW() - (:days || ' days')::interval;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Param placeholders use `:name`. They are rewritten to `$1, $2, …` (Postgres) or `?, ?, …` (MySQL) at execution time and passed as bind values — string interpolation is never used.
|
|
171
|
+
|
|
172
|
+
#### Param type coercion
|
|
173
|
+
|
|
174
|
+
| Declared `type` | Accepts |
|
|
175
|
+
|-----------------|---------|
|
|
176
|
+
| `int` | integer literal |
|
|
177
|
+
| `float` | decimal literal |
|
|
178
|
+
| `bool` | `true` / `false` / `1` / `0` / `yes` / `no` |
|
|
179
|
+
| `string` | any value |
|
|
180
|
+
| `date` | `YYYY-MM-DD` |
|
|
181
|
+
| `datetime` | ISO 8601 |
|
|
182
|
+
|
|
183
|
+
`enum` (optional) restricts the accepted values; mismatch is a hard error. CLI `--param` overrides `--param-file`, which overrides the snippet's `default`.
|
|
184
|
+
|
|
185
|
+
#### Safety invariants
|
|
186
|
+
|
|
187
|
+
- Only `SELECT` / `WITH` (CTE) bodies are accepted; `INSERT/UPDATE/DELETE/DDL` are rejected by the parser.
|
|
188
|
+
- Multi-statement bodies (`SELECT 1; DROP TABLE x`) are rejected.
|
|
189
|
+
- Template syntax inside SQL (`${…}`, `{{…}}`) is rejected — use `:name` parameters.
|
|
190
|
+
- Files exceeding 64 KiB are rejected.
|
|
191
|
+
- `--no-limit` is honoured only at the outermost level; nested subqueries are still wrapped by the size guard.
|
|
192
|
+
|
|
193
|
+
### queries
|
|
194
|
+
|
|
195
|
+
Manage saved snippets — discover, inspect, scaffold, and edit local copies. Mutating
|
|
196
|
+
subcommands (`delete`, `rename`, `copy`, `import`) only operate on the local layer
|
|
197
|
+
(`.dbcli/queries/`); builtin and shared snippets are never modified in place.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Discovery / inspection
|
|
201
|
+
dbcli queries list # all snippets (builtin + shared + local)
|
|
202
|
+
dbcli queries list --tag analytics --engine postgres --format json
|
|
203
|
+
dbcli queries list --source local # only personal overrides
|
|
204
|
+
dbcli queries show @dau # frontmatter + SQL
|
|
205
|
+
dbcli queries show @dau --format json # MCP-shaped contract
|
|
206
|
+
|
|
207
|
+
# Authoring
|
|
208
|
+
dbcli queries new @new/sample # scaffold under .dbcli-shared/queries/
|
|
209
|
+
dbcli queries new @scratch --local # personal copy under .dbcli/queries/
|
|
210
|
+
dbcli queries edit @dau # opens local first, falls back to shared
|
|
211
|
+
dbcli queries edit @dau --shared # always edit the shared file
|
|
212
|
+
dbcli queries check # parse all snippets; exit 1 on errors
|
|
213
|
+
dbcli queries check --strict # promote warnings (e.g. missing engine) to errors
|
|
214
|
+
|
|
215
|
+
# Local-layer file management
|
|
216
|
+
dbcli queries delete @scratch # remove local file(s); prompts unless --force
|
|
217
|
+
dbcli queries delete @scratch --force
|
|
218
|
+
dbcli queries rename @scratch @analytics/dau # rename within local layer; preserves engine suffix
|
|
219
|
+
dbcli queries copy @diag/connections @my/connections # fork builtin/shared into local for editing
|
|
220
|
+
dbcli queries import ./hotfix.sql # import an external .sql into .dbcli/queries/
|
|
221
|
+
dbcli queries import ./hotfix.sql --as @diag/custom # override the snippet key
|
|
222
|
+
dbcli queries export @dau --output dau.sql # write snippet body to a file (stdout if omitted)
|
|
223
|
+
dbcli queries export @diag/connections --engine postgres # pick a variant when multiple engines exist
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**`list` options:** `--format <table|json|csv>`, `--tag <tag>`, `--engine <postgres|mysql>`, `--source <local|shared>`
|
|
227
|
+
**`show` options:** `--format <table|json|csv>`
|
|
228
|
+
**`new` options:** `--local`, `--edit`
|
|
229
|
+
**`edit` options:** `--shared`
|
|
230
|
+
**`check` options:** `--strict`, `--format <table|json|csv>`
|
|
231
|
+
**`delete` options:** `--force` (skip the confirmation prompt). Refuses to run if `@name` has no local copy.
|
|
232
|
+
**`rename` options:** `--force`. Both names must start with `@`. Engine suffix (`.postgres.sql` / `.mysql.sql`) is preserved; frontmatter `name:` is rewritten to the new key.
|
|
233
|
+
**`copy` options:** *(none)*. Copies every variant (all engines) of the source into the local layer; fails if the destination already has a local copy.
|
|
234
|
+
**`import` options:** `--force` (overwrite existing local file), `--as <name>` (override snippet key; defaults to filename without `.postgres` / `.mysql` suffix). Source must be `.sql` and parse cleanly (frontmatter validated, non-SELECT bodies rejected).
|
|
235
|
+
**`export` options:** `--output <path>` (write to file; otherwise stdout), `--engine <postgres|mysql>` (required when the snippet has multiple engine variants).
|
|
236
|
+
|
|
237
|
+
`--format json` on `list` and `show` emits a stable, machine-readable shape — designed to back a future MCP server without further refactor.
|
|
238
|
+
|
|
117
239
|
### insert
|
|
118
240
|
|
|
119
241
|
Insert data into a table.
|
|
@@ -338,13 +460,25 @@ dbcli migrate drop-enum status --execute --force
|
|
|
338
460
|
|
|
339
461
|
## MongoDB Support
|
|
340
462
|
|
|
341
|
-
MongoDB connections use a JSON-based query model instead of SQL.
|
|
463
|
+
MongoDB connections use a JSON-based query model instead of SQL. Treat MongoDB support as a narrower document-database path, not as a full SQL feature equivalent.
|
|
342
464
|
|
|
343
465
|
Atlas-style `mongodb+srv://` URIs are supported. `list` and `query` run against the database configured for the connection, and `query` always requires `--collection <name>`.
|
|
344
466
|
|
|
345
|
-
**Supported commands:** `init`, `list`, `query`, `
|
|
467
|
+
**Supported commands:** `init`, `use`, `list`, `schema`, `query`, `insert`, `update`, `delete`, `status`, `shell`, `doctor`, `upgrade`, `completion`
|
|
468
|
+
|
|
469
|
+
**Limited support:**
|
|
346
470
|
|
|
347
|
-
|
|
471
|
+
- `schema` samples collection documents to infer field names/types. It does not provide relational constraints, primary keys, foreign keys, or reliable index metadata.
|
|
472
|
+
- `query` accepts only JSON object filters or aggregation pipeline arrays and always requires `--collection <name>`.
|
|
473
|
+
- `insert` inserts one JSON document into the named collection.
|
|
474
|
+
- `update` accepts a JSON filter in `--where` or simple `key=value` conditions. If `--set` does not use MongoDB update operators such as `$set`, dbcli wraps it in `$set`.
|
|
475
|
+
- `delete` deletes all documents matching the JSON/simple filter.
|
|
476
|
+
- MongoDB write paths do not currently provide the same SQL dry-run, relational schema validation, or column-level blacklist filtering guarantees as SQL writes.
|
|
477
|
+
- `shell` blocks raw SQL for MongoDB; use `query <json> --collection <name>` inside the shell.
|
|
478
|
+
|
|
479
|
+
**Not supported (exit with error):** `q` saved-query execution, `export`, `diff`, `migrate`
|
|
480
|
+
|
|
481
|
+
**Not a supported MongoDB target:** `check` is designed for relational health checks and emits SQL-style checks.
|
|
348
482
|
|
|
349
483
|
### MongoDB-specific workflow
|
|
350
484
|
|
|
@@ -359,6 +493,11 @@ dbcli list --format json
|
|
|
359
493
|
dbcli query '{}' --collection orders --format json # All documents
|
|
360
494
|
dbcli query '{"status": "paid"}' --collection orders # Filter
|
|
361
495
|
dbcli query '[{"$match": {"status":"paid"}}, {"$count":"total"}]' --collection orders # Pipeline
|
|
496
|
+
|
|
497
|
+
# 4. Document writes (permission-gated; no SQL dry-run semantics)
|
|
498
|
+
dbcli insert orders --data '{"status":"paid","total":42}'
|
|
499
|
+
dbcli update orders --where '{"status":"pending"}' --set '{"status":"paid"}'
|
|
500
|
+
dbcli delete orders --where '{"status":"cancelled"}' --force
|
|
362
501
|
```
|
|
363
502
|
|
|
364
503
|
### Query syntax
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Built-in snippets
|
|
2
|
+
|
|
3
|
+
Files under this directory are bundled with dbcli and resolved at runtime as
|
|
4
|
+
the `builtin` tier. Every file is a valid `.sql` snippet.
|
|
5
|
+
|
|
6
|
+
## Naming
|
|
7
|
+
|
|
8
|
+
- Single-engine variant: `<topic>.<engine>.sql` — loader derives key
|
|
9
|
+
`@<dir>/<topic>` and engine from the suffix.
|
|
10
|
+
- Cross-engine variant: `<topic>.sql` with explicit
|
|
11
|
+
`engine: [postgres, mysql]` in frontmatter.
|
|
12
|
+
|
|
13
|
+
## Override
|
|
14
|
+
|
|
15
|
+
Users can shadow any built-in snippet by placing a same-key file in
|
|
16
|
+
`.dbcli-shared/queries/` (team) or `.dbcli/queries/` (personal). Override is
|
|
17
|
+
per-engine: a local `connections.postgres.sql` only shadows the postgres
|
|
18
|
+
variant; the mysql variant is still served from builtin.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: InnoDB buffer pool hit ratio (mysql)
|
|
3
|
+
-- description: Reads from disk vs. read requests from the buffer pool.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT
|
|
7
|
+
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
|
|
8
|
+
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') AS pool_reads,
|
|
9
|
+
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
|
|
10
|
+
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') AS pool_read_requests,
|
|
11
|
+
ROUND(
|
|
12
|
+
1 -
|
|
13
|
+
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
|
|
14
|
+
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads')
|
|
15
|
+
/
|
|
16
|
+
NULLIF(
|
|
17
|
+
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
|
|
18
|
+
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'), 0)
|
|
19
|
+
, 4) AS hit_ratio;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Cache hit ratio (postgres)
|
|
3
|
+
-- description: Heap and index buffer cache hit ratios across user tables.
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT SUM(heap_blks_read) AS heap_read,
|
|
7
|
+
SUM(heap_blks_hit) AS heap_hit,
|
|
8
|
+
ROUND(
|
|
9
|
+
SUM(heap_blks_hit)::numeric
|
|
10
|
+
/ NULLIF(SUM(heap_blks_hit) + SUM(heap_blks_read), 0)
|
|
11
|
+
, 4) AS heap_hit_ratio,
|
|
12
|
+
SUM(idx_blks_read) AS idx_read,
|
|
13
|
+
SUM(idx_blks_hit) AS idx_hit,
|
|
14
|
+
ROUND(
|
|
15
|
+
SUM(idx_blks_hit)::numeric
|
|
16
|
+
/ NULLIF(SUM(idx_blks_hit) + SUM(idx_blks_read), 0)
|
|
17
|
+
, 4) AS idx_hit_ratio
|
|
18
|
+
FROM pg_statio_user_tables;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Active connections (mysql)
|
|
3
|
+
-- description: Non-sleep processes ordered by elapsed time.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT id,
|
|
7
|
+
user,
|
|
8
|
+
host,
|
|
9
|
+
db,
|
|
10
|
+
command,
|
|
11
|
+
time AS duration_seconds,
|
|
12
|
+
state,
|
|
13
|
+
info AS query
|
|
14
|
+
FROM information_schema.processlist
|
|
15
|
+
WHERE command <> 'Sleep'
|
|
16
|
+
ORDER BY time DESC;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Active connections (postgres)
|
|
3
|
+
-- description: Active sessions excluding idle, ordered by query start.
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT pid,
|
|
7
|
+
usename AS user,
|
|
8
|
+
application_name AS app,
|
|
9
|
+
client_addr AS client,
|
|
10
|
+
state,
|
|
11
|
+
NOW() - query_start AS duration,
|
|
12
|
+
query
|
|
13
|
+
FROM pg_stat_activity
|
|
14
|
+
WHERE state IS NOT NULL
|
|
15
|
+
AND state <> 'idle'
|
|
16
|
+
ORDER BY query_start;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Database size (mysql)
|
|
3
|
+
-- description: Total data + index size per schema in MB.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT table_schema AS `database`,
|
|
7
|
+
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
|
|
8
|
+
FROM information_schema.tables
|
|
9
|
+
GROUP BY table_schema
|
|
10
|
+
ORDER BY size_mb DESC;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Database size (postgres)
|
|
3
|
+
-- description: Each database with pretty-printed total size.
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT datname AS database,
|
|
7
|
+
pg_size_pretty(pg_database_size(datname)) AS size
|
|
8
|
+
FROM pg_database
|
|
9
|
+
ORDER BY pg_database_size(datname) DESC;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Index usage (mysql)
|
|
3
|
+
-- description: Index I/O wait counts ordered by total uses.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT object_schema AS `schema`,
|
|
7
|
+
object_name AS `table`,
|
|
8
|
+
index_name,
|
|
9
|
+
count_star AS uses,
|
|
10
|
+
count_read AS reads,
|
|
11
|
+
count_write AS writes
|
|
12
|
+
FROM performance_schema.table_io_waits_summary_by_index_usage
|
|
13
|
+
WHERE object_schema NOT IN ('mysql','performance_schema','sys')
|
|
14
|
+
AND index_name IS NOT NULL
|
|
15
|
+
ORDER BY count_star ASC;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Index usage (postgres)
|
|
3
|
+
-- description: Indexes ordered by scan count (low scans = candidates to drop).
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT schemaname AS schema,
|
|
7
|
+
relname AS table,
|
|
8
|
+
indexrelname AS index,
|
|
9
|
+
idx_scan AS scans,
|
|
10
|
+
idx_tup_read AS tuples_read,
|
|
11
|
+
idx_tup_fetch AS tuples_fetched,
|
|
12
|
+
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
|
|
13
|
+
FROM pg_stat_user_indexes
|
|
14
|
+
ORDER BY idx_scan ASC;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Lock waits (mysql)
|
|
3
|
+
-- description: InnoDB lock waits with waiting and blocking transactions.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT waiting.trx_mysql_thread_id AS waiting_thread,
|
|
7
|
+
waiting.trx_query AS waiting_query,
|
|
8
|
+
blocking.trx_mysql_thread_id AS blocking_thread,
|
|
9
|
+
blocking.trx_query AS blocking_query
|
|
10
|
+
FROM performance_schema.data_lock_waits AS w
|
|
11
|
+
JOIN information_schema.innodb_trx AS waiting
|
|
12
|
+
ON w.requesting_engine_transaction_id = waiting.trx_id
|
|
13
|
+
JOIN information_schema.innodb_trx AS blocking
|
|
14
|
+
ON w.blocking_engine_transaction_id = blocking.trx_id;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Lock waits (postgres)
|
|
3
|
+
-- description: Sessions blocked by other sessions with both queries shown.
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT blocked.pid AS blocked_pid,
|
|
7
|
+
blocked.usename AS blocked_user,
|
|
8
|
+
blocked.query AS blocked_query,
|
|
9
|
+
blocking.pid AS blocking_pid,
|
|
10
|
+
blocking.usename AS blocking_user,
|
|
11
|
+
blocking.query AS blocking_query
|
|
12
|
+
FROM pg_stat_activity AS blocked
|
|
13
|
+
JOIN pg_stat_activity AS blocking
|
|
14
|
+
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
|
|
15
|
+
WHERE blocked.pid <> blocking.pid;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Long-running queries (mysql)
|
|
3
|
+
-- description: Non-sleep processes whose elapsed time exceeds min_seconds.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- params:
|
|
6
|
+
-- min_seconds:
|
|
7
|
+
-- type: int
|
|
8
|
+
-- default: 30
|
|
9
|
+
-- ---
|
|
10
|
+
SELECT id, user, host, db, time AS duration_seconds, state, info AS query
|
|
11
|
+
FROM information_schema.processlist
|
|
12
|
+
WHERE command <> 'Sleep'
|
|
13
|
+
AND time > :min_seconds
|
|
14
|
+
ORDER BY time DESC;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Long-running queries (postgres)
|
|
3
|
+
-- description: Queries running longer than min_seconds.
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- params:
|
|
6
|
+
-- min_seconds:
|
|
7
|
+
-- type: int
|
|
8
|
+
-- default: 30
|
|
9
|
+
-- ---
|
|
10
|
+
SELECT pid,
|
|
11
|
+
usename AS user,
|
|
12
|
+
NOW() - query_start AS duration,
|
|
13
|
+
state,
|
|
14
|
+
query
|
|
15
|
+
FROM pg_stat_activity
|
|
16
|
+
WHERE state IS NOT NULL
|
|
17
|
+
AND state <> 'idle'
|
|
18
|
+
AND NOW() - query_start > make_interval(secs => :min_seconds)
|
|
19
|
+
ORDER BY duration DESC;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Missing indexes (mysql)
|
|
3
|
+
-- description: Tables with significant full-scan I/O and no index used.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT object_schema AS `schema`,
|
|
7
|
+
object_name AS `table`,
|
|
8
|
+
count_read AS full_scan_reads
|
|
9
|
+
FROM performance_schema.table_io_waits_summary_by_index_usage
|
|
10
|
+
WHERE index_name IS NULL
|
|
11
|
+
AND object_schema NOT IN ('mysql','performance_schema','sys')
|
|
12
|
+
AND count_read > 1000
|
|
13
|
+
ORDER BY count_read DESC;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Missing indexes (postgres)
|
|
3
|
+
-- description: User tables where seq scans dominate over index scans (>1k rows).
|
|
4
|
+
-- engine: postgres
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT schemaname AS schema,
|
|
7
|
+
relname AS table,
|
|
8
|
+
seq_scan,
|
|
9
|
+
seq_tup_read,
|
|
10
|
+
idx_scan,
|
|
11
|
+
n_live_tup AS estimated_rows
|
|
12
|
+
FROM pg_stat_user_tables
|
|
13
|
+
WHERE seq_scan > COALESCE(idx_scan, 0)
|
|
14
|
+
AND n_live_tup > 1000
|
|
15
|
+
ORDER BY seq_tup_read DESC;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- ---
|
|
2
|
+
-- name: Table sizes (mysql)
|
|
3
|
+
-- description: Data + index size in MB with estimated row count.
|
|
4
|
+
-- engine: mysql
|
|
5
|
+
-- ---
|
|
6
|
+
SELECT table_schema AS `schema`,
|
|
7
|
+
table_name AS `table`,
|
|
8
|
+
ROUND((data_length + index_length) / 1024 / 1024, 2) AS total_mb,
|
|
9
|
+
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
|
|
10
|
+
ROUND(index_length / 1024 / 1024, 2) AS index_mb,
|
|
11
|
+
table_rows AS estimated_rows
|
|
12
|
+
FROM information_schema.tables
|
|
13
|
+
WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys')
|
|
14
|
+
ORDER BY data_length + index_length DESC;
|