@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 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.
@@ -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`, `status`, `use`, `shell`, `doctor`, `upgrade`, `completion`
467
+ **Supported commands:** `init`, `use`, `list`, `schema`, `query`, `insert`, `update`, `delete`, `status`, `shell`, `doctor`, `upgrade`, `completion`
468
+
469
+ **Limited support:**
346
470
 
347
- **Not supported (exit with error):** `schema`, `insert`, `update`, `delete`, `export`, `diff`, `migrate`, `check`
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;