@carllee1983/dbcli 1.57.0 → 2.0.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/.cursor/rules/dbcli.mdc +19 -2
- package/.cursor/skills/dbcli/reference.md +55 -4
- package/.github/skills/dbcli/SKILL.md +19 -2
- package/.github/skills/dbcli/reference.md +55 -4
- package/CHANGELOG.md +71 -0
- package/assets/SKILL.md +19 -2
- package/assets/SKILL.zh-TW.md +15 -1
- package/assets/reference.md +55 -4
- package/dist/cli-runtime.mjs +1323 -688
- package/dist/cli.mjs +4 -2
- package/dist/core.d.ts +79 -24
- package/dist/core.mjs +349 -343
- package/package.json +4 -2
- package/plugins/dbcli-agent/skills/dbcli/SKILL.md +19 -2
- package/plugins/dbcli-agent/skills/dbcli/reference.md +55 -4
- package/skills/dbcli/SKILL.md +19 -2
- package/skills/dbcli/reference.md +55 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carllee1983/dbcli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Database CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -74,12 +74,14 @@
|
|
|
74
74
|
"test:unit": "bun test tests/unit tests/core",
|
|
75
75
|
"test:integration": "bun test tests/integration",
|
|
76
76
|
"test:gherkin": "bun test tests/gherkin",
|
|
77
|
-
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration
|
|
77
|
+
"test:docker": "docker compose -f docker-compose.test.yml up -d --wait && bun test tests/integration; docker compose -f docker-compose.test.yml down",
|
|
78
|
+
"services:check": "bun run scripts/check-test-services.ts",
|
|
78
79
|
"docs:check": "bun run scripts/check-user-docs.ts",
|
|
79
80
|
"contract:check": "bun run scripts/check-cli-contract.ts",
|
|
80
81
|
"skill:check": "bun run scripts/check-skill-parity.ts",
|
|
81
82
|
"platform:check": "bun run scripts/check-platform-parity.ts",
|
|
82
83
|
"agent-core:check": "bun run scripts/check-agent-core-purity.ts",
|
|
84
|
+
"core-stdout:check": "bun run scripts/check-core-no-stdout.ts",
|
|
83
85
|
"typecheck": "tsc --noEmit --pretty false",
|
|
84
86
|
"test:perf": "bun test ./tests/perf/*.bench.ts",
|
|
85
87
|
"lint": "eslint src tests scripts --ext .ts --max-warnings=0",
|
|
@@ -43,6 +43,18 @@ condition, first `query` / `export` the target rows' primary keys, then run one
|
|
|
43
43
|
`update` / `delete --where "id=<pk>"` per key — or escalate to a human. (MongoDB `--where`
|
|
44
44
|
takes a full JSON filter and is exempt.)
|
|
45
45
|
|
|
46
|
+
**Write gate (2.0.0) — the rule that will refuse you.** Every write is classified into two
|
|
47
|
+
tiers. Ordinary writes (`INSERT`, `UPDATE` / `DELETE` with a `WHERE`, `CREATE`, `ALTER`)
|
|
48
|
+
run unattended exactly as before; `--yes` skips the terminal prompt a human would see.
|
|
49
|
+
**Statements that are not limited to specific rows are refused outright when nobody can
|
|
50
|
+
answer a prompt** — `UPDATE` / `DELETE` with no `WHERE`, `DROP`, `TRUNCATE`, a statement
|
|
51
|
+
the SQL parser cannot read, several statements in one string, and `update` / `delete --where` matching on no primary key and
|
|
52
|
+
no unique index. The process exits `1` with `reason=no_where`, `reason=ddl_destruction`,
|
|
53
|
+
`reason=unparseable` or `reason=non_unique_where`, and **nothing reaches the database**.
|
|
54
|
+
**No flag bypasses this** — not `--yes`, not `--force`. To write every row on purpose, put
|
|
55
|
+
the intent in the SQL itself: add `WHERE 1=1` or a `LIMIT`. `DROP` / `TRUNCATE` have no
|
|
56
|
+
unattended route at all; escalate to a human.
|
|
57
|
+
|
|
46
58
|
> `report` and `guide` already embed an `inspect` snapshot — you do **not** need to run
|
|
47
59
|
> `dbcli inspect` first. Run `dbcli inspect --for-agent` manually only when you want the
|
|
48
60
|
> audit-recent context or to diagnose a connection problem.
|
|
@@ -449,8 +461,13 @@ changing the default. `--recovery` is honoured by `query`, `q`, `insert`, `updat
|
|
|
449
461
|
(no `>=`, `!=`, `LIKE`, `OR`). MongoDB `--where` takes a full JSON filter
|
|
450
462
|
(`'{"status":"pending"}'`), falling back to `col=val` when it is not valid JSON.
|
|
451
463
|
- `--dry-run` prints the parameterized SQL (with `$1` / `?` placeholders, not real values)
|
|
452
|
-
and `rows_affected: 0
|
|
453
|
-
intended `--where` / `--set`.
|
|
464
|
+
and `status:"dry_run"` with `rows_affected: 0` — never `success`, which now means the
|
|
465
|
+
write really ran. Proceed once the SQL shape matches the intended `--where` / `--set`.
|
|
466
|
+
Declining at the confirmation prompt reports `status:"cancelled"`, also not `success`.
|
|
467
|
+
MongoDB prints a shell-style preview.
|
|
468
|
+
- `--force` skips the confirmation prompt. Every `insert` / `update` / `delete` asks
|
|
469
|
+
first — SQL, MongoDB and Redis alike — and a non-interactive run cannot answer, so an
|
|
470
|
+
unattended write without `--force` ends as `status:"cancelled"` having changed nothing.
|
|
454
471
|
- `--recovery` is recommended for automated agent pipelines (enables `dbcli recover --apply`
|
|
455
472
|
after a failure); optional for one-off manual writes.
|
|
456
473
|
|
|
@@ -334,9 +334,50 @@ dbcli query "SELECT day, dau FROM dau_daily" --ui # open in brows
|
|
|
334
334
|
dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdout
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
**Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--recovery`
|
|
337
|
+
**Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--yes`, `--recovery`
|
|
338
338
|
**Permission:** query-only+ (Redis: per-command; Elasticsearch: per HTTP method/path)
|
|
339
339
|
|
|
340
|
+
#### Write confirmation gate (2.0.0)
|
|
341
|
+
|
|
342
|
+
A SQL write passes through a two-tier gate before the connection is opened. The gate is
|
|
343
|
+
separate from the permission axis: permission says what the connection may do, the gate
|
|
344
|
+
says whether this particular statement may run right now.
|
|
345
|
+
|
|
346
|
+
| Tier | Statements | Interactive terminal | Non-interactive (or `--format json`) |
|
|
347
|
+
| :--- | :--- | :--- | :--- |
|
|
348
|
+
| One | `INSERT`, `UPDATE` / `DELETE` **with** a `WHERE` or `LIMIT`, `CREATE`, `ALTER` | Summary + `y/N`; `--yes` skips it | Runs, exactly as before |
|
|
349
|
+
| Two | `UPDATE` / `DELETE` with **no** `WHERE`, `DROP`, `TRUNCATE`, unparseable statements, several statements in one string | Type the target table name; **no flag skips it** | **Refused**: exit `1`, nothing sent to the database |
|
|
350
|
+
|
|
351
|
+
A refusal message names a machine-readable reason — `reason=no_where`,
|
|
352
|
+
`reason=ddl_destruction`, `reason=unparseable`, `reason=multiple_statements` — so a caller can tell it apart from a
|
|
353
|
+
connection failure or a permission denial.
|
|
354
|
+
|
|
355
|
+
**Escape routes.** For a statement that accepts a `WHERE`, put the intent in the SQL:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
dbcli query "UPDATE users SET banned = 1" # refused, reason=no_where
|
|
359
|
+
dbcli query "UPDATE users SET banned = 1 WHERE 1=1" # runs — intent is explicit
|
|
360
|
+
dbcli query "DELETE FROM sessions LIMIT 1000" # runs — damage is bounded
|
|
361
|
+
dbcli query "UPDATE users SET banned = 1 WHERE id = 3" --yes # tier one, question skipped
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
This is deliberately not a flag. `WHERE 1=1` appended to a statement that already has a
|
|
365
|
+
`WHERE` is a syntax error, so a blanket "always add it" habit breaks on the first ordinary
|
|
366
|
+
statement; a flag would be harmless everywhere and therefore added everywhere. For `DROP`
|
|
367
|
+
and `TRUNCATE` there is no clause to add — the connection's `permission` level decides
|
|
368
|
+
whether they are possible at all, and the typed confirmation must come from a person.
|
|
369
|
+
|
|
370
|
+
Every tier-two evaluation is written to the audit log with
|
|
371
|
+
`metadata.write_gate_outcome` (`allowed` / `declined` / `refused`) and
|
|
372
|
+
`metadata.write_gate_reason`.
|
|
373
|
+
|
|
374
|
+
> **Elasticsearch tiers by scope.** A read (`GET` / `HEAD`, plus `POST _search` and
|
|
375
|
+
> `POST _count`) is query-only. Indexing or updating a document is read-write.
|
|
376
|
+
> `DELETE /<index>/_doc/<id>` — one document — is data-admin. Everything that removes or
|
|
377
|
+
> reshapes a container is **admin**: `DELETE /<index>`, a wildcard or `_all` delete,
|
|
378
|
+
> templates and aliases, and `PUT` against `_mapping` or `_settings`. A request whose
|
|
379
|
+
> scope cannot be established is treated as admin rather than guessed downward.
|
|
380
|
+
|
|
340
381
|
> **Server-side scripts are rejected on every path.** MongoDB `$where`,
|
|
341
382
|
> `$function`, and `$accumulator`, and Elasticsearch `script` / `script_fields`,
|
|
342
383
|
> execute code on the database server. The adapters reject them anywhere in a
|
|
@@ -925,7 +966,7 @@ dbcli insert users --data '{"name":"Alice"}' --force
|
|
|
925
966
|
dbcli insert users --data '{"name":"Alice"}' --plan --format json # risk analysis only; no DB connection
|
|
926
967
|
```
|
|
927
968
|
|
|
928
|
-
**Options:** `--data <json>`, `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
969
|
+
**Options:** `--data <json>`, `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
929
970
|
**Permission:** read-write+
|
|
930
971
|
|
|
931
972
|
### update
|
|
@@ -938,7 +979,7 @@ dbcli update users --where "id=1" --set '{"name":"Bob"}' --dry-run
|
|
|
938
979
|
dbcli update users --where "id=1" --set '{"name":"Bob"}' --plan --format json # risk analysis only; no DB connection
|
|
939
980
|
```
|
|
940
981
|
|
|
941
|
-
**Options:** `--where <condition>` (required), `--set <json>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
982
|
+
**Options:** `--where <condition>` (required), `--set <json>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
942
983
|
**Permission:** read-write+
|
|
943
984
|
|
|
944
985
|
> **`--where` grammar (SQL `update` / `delete`)** — equality only: `col=val` or
|
|
@@ -948,6 +989,14 @@ dbcli update users --where "id=1" --set '{"name":"Bob"}' --plan --format json
|
|
|
948
989
|
> target primary keys first, then issue one `update` / `delete --where "id=<pk>"` per key.
|
|
949
990
|
> (MongoDB `--where` accepts a full JSON filter and is exempt.)
|
|
950
991
|
|
|
992
|
+
> **Tier two for structured writes (2.0.0)** — a `--where` that matches on no primary key
|
|
993
|
+
> and no unique index selects an unknown number of rows, so it is treated the same as a
|
|
994
|
+
> raw statement with no `WHERE`: the target table name must be typed at an interactive
|
|
995
|
+
> terminal, and a non-interactive run is refused with exit `1` and
|
|
996
|
+
> `reason=non_unique_where`. `--force` and `--dry-run` do not affect this; `--force` skips
|
|
997
|
+
> the ordinary confirmation only. Select the primary keys first and write one row at a
|
|
998
|
+
> time, or run it where a person can confirm it.
|
|
999
|
+
|
|
951
1000
|
### delete
|
|
952
1001
|
|
|
953
1002
|
Delete data from a table.
|
|
@@ -959,7 +1008,7 @@ dbcli delete users --where "id=1" --force
|
|
|
959
1008
|
dbcli delete users --where "id=1" --plan --format json # risk analysis only; no DB connection
|
|
960
1009
|
```
|
|
961
1010
|
|
|
962
|
-
**Options:** `--where <condition>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
1011
|
+
**Options:** `--where <condition>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
963
1012
|
**Permission:** data-admin+
|
|
964
1013
|
|
|
965
1014
|
### export
|
|
@@ -2540,6 +2589,8 @@ dbcli migrate add-enum status active inactive suspended
|
|
|
2540
2589
|
dbcli migrate alter-enum status --add-value archived
|
|
2541
2590
|
dbcli migrate drop-enum status --execute --force
|
|
2542
2591
|
```
|
|
2592
|
+
A destructive `migrate` action (`drop`, `drop-column`, `drop-index`, `drop-enum`) asks for confirmation on stderr before it runs, and reports `status: "cancelled"` if you decline — not `success`, which it used to claim with the cancellation buried in `warnings`. `--force` skips the question; a non-interactive run that omits it cannot answer and therefore cancels.
|
|
2593
|
+
|
|
2543
2594
|
|
|
2544
2595
|
**Column spec format:** `name:type[:modifier[:modifier...]]`
|
|
2545
2596
|
- Modifiers: `pk`, `not-null`, `unique`, `auto-increment`, `default=<value>`, `references=<table>.<column>`
|
package/skills/dbcli/SKILL.md
CHANGED
|
@@ -43,6 +43,18 @@ condition, first `query` / `export` the target rows' primary keys, then run one
|
|
|
43
43
|
`update` / `delete --where "id=<pk>"` per key — or escalate to a human. (MongoDB `--where`
|
|
44
44
|
takes a full JSON filter and is exempt.)
|
|
45
45
|
|
|
46
|
+
**Write gate (2.0.0) — the rule that will refuse you.** Every write is classified into two
|
|
47
|
+
tiers. Ordinary writes (`INSERT`, `UPDATE` / `DELETE` with a `WHERE`, `CREATE`, `ALTER`)
|
|
48
|
+
run unattended exactly as before; `--yes` skips the terminal prompt a human would see.
|
|
49
|
+
**Statements that are not limited to specific rows are refused outright when nobody can
|
|
50
|
+
answer a prompt** — `UPDATE` / `DELETE` with no `WHERE`, `DROP`, `TRUNCATE`, a statement
|
|
51
|
+
the SQL parser cannot read, several statements in one string, and `update` / `delete --where` matching on no primary key and
|
|
52
|
+
no unique index. The process exits `1` with `reason=no_where`, `reason=ddl_destruction`,
|
|
53
|
+
`reason=unparseable` or `reason=non_unique_where`, and **nothing reaches the database**.
|
|
54
|
+
**No flag bypasses this** — not `--yes`, not `--force`. To write every row on purpose, put
|
|
55
|
+
the intent in the SQL itself: add `WHERE 1=1` or a `LIMIT`. `DROP` / `TRUNCATE` have no
|
|
56
|
+
unattended route at all; escalate to a human.
|
|
57
|
+
|
|
46
58
|
> `report` and `guide` already embed an `inspect` snapshot — you do **not** need to run
|
|
47
59
|
> `dbcli inspect` first. Run `dbcli inspect --for-agent` manually only when you want the
|
|
48
60
|
> audit-recent context or to diagnose a connection problem.
|
|
@@ -449,8 +461,13 @@ changing the default. `--recovery` is honoured by `query`, `q`, `insert`, `updat
|
|
|
449
461
|
(no `>=`, `!=`, `LIKE`, `OR`). MongoDB `--where` takes a full JSON filter
|
|
450
462
|
(`'{"status":"pending"}'`), falling back to `col=val` when it is not valid JSON.
|
|
451
463
|
- `--dry-run` prints the parameterized SQL (with `$1` / `?` placeholders, not real values)
|
|
452
|
-
and `rows_affected: 0
|
|
453
|
-
intended `--where` / `--set`.
|
|
464
|
+
and `status:"dry_run"` with `rows_affected: 0` — never `success`, which now means the
|
|
465
|
+
write really ran. Proceed once the SQL shape matches the intended `--where` / `--set`.
|
|
466
|
+
Declining at the confirmation prompt reports `status:"cancelled"`, also not `success`.
|
|
467
|
+
MongoDB prints a shell-style preview.
|
|
468
|
+
- `--force` skips the confirmation prompt. Every `insert` / `update` / `delete` asks
|
|
469
|
+
first — SQL, MongoDB and Redis alike — and a non-interactive run cannot answer, so an
|
|
470
|
+
unattended write without `--force` ends as `status:"cancelled"` having changed nothing.
|
|
454
471
|
- `--recovery` is recommended for automated agent pipelines (enables `dbcli recover --apply`
|
|
455
472
|
after a failure); optional for one-off manual writes.
|
|
456
473
|
|
|
@@ -334,9 +334,50 @@ dbcli query "SELECT day, dau FROM dau_daily" --ui # open in brows
|
|
|
334
334
|
dbcli query "SELECT * FROM orders" --format html > orders.html # pipe to stdout
|
|
335
335
|
```
|
|
336
336
|
|
|
337
|
-
**Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--recovery`
|
|
337
|
+
**Options:** `--format <table|json|csv|html>`, `--ui` (open the dashboard in the system browser; implies `--format html`), `--limit <number>`, `--no-limit`, `--collection <name>` (MongoDB / Elasticsearch), `--index <name>` (Elasticsearch alias for `--collection`), `--fields <list>`, `--truncate <number>` / `--no-truncate`, `-f, --query-file <path>`, `--use <name[,name]>`, `--slow-ms <number>`, `--yes`, `--recovery`
|
|
338
338
|
**Permission:** query-only+ (Redis: per-command; Elasticsearch: per HTTP method/path)
|
|
339
339
|
|
|
340
|
+
#### Write confirmation gate (2.0.0)
|
|
341
|
+
|
|
342
|
+
A SQL write passes through a two-tier gate before the connection is opened. The gate is
|
|
343
|
+
separate from the permission axis: permission says what the connection may do, the gate
|
|
344
|
+
says whether this particular statement may run right now.
|
|
345
|
+
|
|
346
|
+
| Tier | Statements | Interactive terminal | Non-interactive (or `--format json`) |
|
|
347
|
+
| :--- | :--- | :--- | :--- |
|
|
348
|
+
| One | `INSERT`, `UPDATE` / `DELETE` **with** a `WHERE` or `LIMIT`, `CREATE`, `ALTER` | Summary + `y/N`; `--yes` skips it | Runs, exactly as before |
|
|
349
|
+
| Two | `UPDATE` / `DELETE` with **no** `WHERE`, `DROP`, `TRUNCATE`, unparseable statements, several statements in one string | Type the target table name; **no flag skips it** | **Refused**: exit `1`, nothing sent to the database |
|
|
350
|
+
|
|
351
|
+
A refusal message names a machine-readable reason — `reason=no_where`,
|
|
352
|
+
`reason=ddl_destruction`, `reason=unparseable`, `reason=multiple_statements` — so a caller can tell it apart from a
|
|
353
|
+
connection failure or a permission denial.
|
|
354
|
+
|
|
355
|
+
**Escape routes.** For a statement that accepts a `WHERE`, put the intent in the SQL:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
dbcli query "UPDATE users SET banned = 1" # refused, reason=no_where
|
|
359
|
+
dbcli query "UPDATE users SET banned = 1 WHERE 1=1" # runs — intent is explicit
|
|
360
|
+
dbcli query "DELETE FROM sessions LIMIT 1000" # runs — damage is bounded
|
|
361
|
+
dbcli query "UPDATE users SET banned = 1 WHERE id = 3" --yes # tier one, question skipped
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
This is deliberately not a flag. `WHERE 1=1` appended to a statement that already has a
|
|
365
|
+
`WHERE` is a syntax error, so a blanket "always add it" habit breaks on the first ordinary
|
|
366
|
+
statement; a flag would be harmless everywhere and therefore added everywhere. For `DROP`
|
|
367
|
+
and `TRUNCATE` there is no clause to add — the connection's `permission` level decides
|
|
368
|
+
whether they are possible at all, and the typed confirmation must come from a person.
|
|
369
|
+
|
|
370
|
+
Every tier-two evaluation is written to the audit log with
|
|
371
|
+
`metadata.write_gate_outcome` (`allowed` / `declined` / `refused`) and
|
|
372
|
+
`metadata.write_gate_reason`.
|
|
373
|
+
|
|
374
|
+
> **Elasticsearch tiers by scope.** A read (`GET` / `HEAD`, plus `POST _search` and
|
|
375
|
+
> `POST _count`) is query-only. Indexing or updating a document is read-write.
|
|
376
|
+
> `DELETE /<index>/_doc/<id>` — one document — is data-admin. Everything that removes or
|
|
377
|
+
> reshapes a container is **admin**: `DELETE /<index>`, a wildcard or `_all` delete,
|
|
378
|
+
> templates and aliases, and `PUT` against `_mapping` or `_settings`. A request whose
|
|
379
|
+
> scope cannot be established is treated as admin rather than guessed downward.
|
|
380
|
+
|
|
340
381
|
> **Server-side scripts are rejected on every path.** MongoDB `$where`,
|
|
341
382
|
> `$function`, and `$accumulator`, and Elasticsearch `script` / `script_fields`,
|
|
342
383
|
> execute code on the database server. The adapters reject them anywhere in a
|
|
@@ -925,7 +966,7 @@ dbcli insert users --data '{"name":"Alice"}' --force
|
|
|
925
966
|
dbcli insert users --data '{"name":"Alice"}' --plan --format json # risk analysis only; no DB connection
|
|
926
967
|
```
|
|
927
968
|
|
|
928
|
-
**Options:** `--data <json>`, `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
969
|
+
**Options:** `--data <json>`, `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
929
970
|
**Permission:** read-write+
|
|
930
971
|
|
|
931
972
|
### update
|
|
@@ -938,7 +979,7 @@ dbcli update users --where "id=1" --set '{"name":"Bob"}' --dry-run
|
|
|
938
979
|
dbcli update users --where "id=1" --set '{"name":"Bob"}' --plan --format json # risk analysis only; no DB connection
|
|
939
980
|
```
|
|
940
981
|
|
|
941
|
-
**Options:** `--where <condition>` (required), `--set <json>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
982
|
+
**Options:** `--where <condition>` (required), `--set <json>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
942
983
|
**Permission:** read-write+
|
|
943
984
|
|
|
944
985
|
> **`--where` grammar (SQL `update` / `delete`)** — equality only: `col=val` or
|
|
@@ -948,6 +989,14 @@ dbcli update users --where "id=1" --set '{"name":"Bob"}' --plan --format json
|
|
|
948
989
|
> target primary keys first, then issue one `update` / `delete --where "id=<pk>"` per key.
|
|
949
990
|
> (MongoDB `--where` accepts a full JSON filter and is exempt.)
|
|
950
991
|
|
|
992
|
+
> **Tier two for structured writes (2.0.0)** — a `--where` that matches on no primary key
|
|
993
|
+
> and no unique index selects an unknown number of rows, so it is treated the same as a
|
|
994
|
+
> raw statement with no `WHERE`: the target table name must be typed at an interactive
|
|
995
|
+
> terminal, and a non-interactive run is refused with exit `1` and
|
|
996
|
+
> `reason=non_unique_where`. `--force` and `--dry-run` do not affect this; `--force` skips
|
|
997
|
+
> the ordinary confirmation only. Select the primary keys first and write one row at a
|
|
998
|
+
> time, or run it where a person can confirm it.
|
|
999
|
+
|
|
951
1000
|
### delete
|
|
952
1001
|
|
|
953
1002
|
Delete data from a table.
|
|
@@ -959,7 +1008,7 @@ dbcli delete users --where "id=1" --force
|
|
|
959
1008
|
dbcli delete users --where "id=1" --plan --format json # risk analysis only; no DB connection
|
|
960
1009
|
```
|
|
961
1010
|
|
|
962
|
-
**Options:** `--where <condition>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output), `--recovery`
|
|
1011
|
+
**Options:** `--where <condition>` (required), `--dry-run`, `--force`, `--plan` (analyze risk without connecting or executing), `--format <text|json>` (`--plan` output; `json` also keeps the result envelope instead of prose in a terminal), `--recovery`
|
|
963
1012
|
**Permission:** data-admin+
|
|
964
1013
|
|
|
965
1014
|
### export
|
|
@@ -2540,6 +2589,8 @@ dbcli migrate add-enum status active inactive suspended
|
|
|
2540
2589
|
dbcli migrate alter-enum status --add-value archived
|
|
2541
2590
|
dbcli migrate drop-enum status --execute --force
|
|
2542
2591
|
```
|
|
2592
|
+
A destructive `migrate` action (`drop`, `drop-column`, `drop-index`, `drop-enum`) asks for confirmation on stderr before it runs, and reports `status: "cancelled"` if you decline — not `success`, which it used to claim with the cancellation buried in `warnings`. `--force` skips the question; a non-interactive run that omits it cannot answer and therefore cancels.
|
|
2593
|
+
|
|
2543
2594
|
|
|
2544
2595
|
**Column spec format:** `name:type[:modifier[:modifier...]]`
|
|
2545
2596
|
- Modifiers: `pk`, `not-null`, `unique`, `auto-increment`, `default=<value>`, `references=<table>.<column>`
|