A database client should make the scary things hard to do by accident. In DBCatz every connection has a safety mode, every statement is sorted into read, write or destructive before it runs, and structure changes are checked against the table first. Curiosity is fine; dropping production on a Friday is not.
The three safety modes
Choose the mode under Safety in the connection form:
| Mode | In the form | What it does |
|---|---|---|
| Normal | Edit freely; risky changes still ask. | Reads and writes run. Destructive statements ask with one click. |
| Production | Asks before every write; marked PROD. | Every write asks. Destructive statements need the connection’s name typed. A PROD badge on the tab and in the sidebar. |
| Read-only | Only reads; the server session is read-only too. | Every write is refused before it is sent. A READ-ONLY badge on the tab and in the sidebar. |
DevCatz services are always Normal.
What asks first in the Query tab
Before a statement runs, DBCatz sorts it:
| Normal | Production | Read-only | |
|---|---|---|---|
Read (SELECT, SHOW, GET …) |
Runs | Runs | Runs |
Write (INSERT, UPDATE … WHERE, most ALTER …) |
Runs | Asks: Write to <name>? (one click) | Refused |
| Destructive (list below) | Asks: Run this? (one click) | Asks: Destructive on <name>? — type the connection’s name | Refused |
A refused statement never reaches the server: Read-only connection: this statement changes data or structure.
The dialog (Ask before a write) shows a title, what will happen, the statements themselves and — when a word is needed — Type <word> to go ahead.
Edits in the grid and the JSON editor
Saving a cell, inserting or deleting rows, and saving a JSON value are writes too. On a Production connection each one asks with one click (<action> on <name>?). On Read-only the grid is not editable at all.
What counts as destructive
SQL (MySQL, MariaDB, PostgreSQL, SQLite)
DROP …(table, database, index, view …)TRUNCATEUPDATEorDELETEwithout aWHEREALTER … DROP …ALTERthat changes a column’s type (MODIFY,CHANGE,ALTER COLUMN … TYPE)RENAMEGRANT/REVOKESET GLOBAL/SET PERSISTFLUSH,RESET,PURGE,KILL,SHUTDOWN
Any other ALTER — adding a column or an index, say — is a plain write: it runs on Normal and asks with one click on
Production.
A few statements that look like reads count as writes: SELECT … INTO OUTFILE, SELECT … FOR UPDATE,
EXPLAIN ANALYZE (it really runs the statement) and a PRAGMA with =.
Redis and Valkey
FLUSHALL, FLUSHDB, CONFIG, SHUTDOWN, DEBUG, MIGRATE, REPLICAOF / SLAVEOF, SWAPDB, MOVE,
RESTORE, SCRIPT, FUNCTION and ACL.
MongoDB
drop(), deleteMany({}) and updateMany({}) with an empty filter, and the commands drop, dropDatabase and
dropIndexes. An aggregate with $out or $merge counts as a write.
Elasticsearch
Any DELETE request, and _delete_by_query.
The second lock on Read-only connections
For MySQL, MariaDB and PostgreSQL, a Read-only connection also opens the server session read-only, so even a statement DBCatz misjudged would be stopped by the server:
| Engine | Session setting |
|---|---|
| MySQL / MariaDB | SET SESSION TRANSACTION READ ONLY |
| PostgreSQL | default_transaction_read_only=on |
SQLite, Redis / Valkey, MongoDB and Elasticsearch have no such session switch. There DBCatz refuses writes itself — once in the window, and again in the app’s main process before anything is sent.
Checks before a structure change
Changing a column, adding an index or dropping one goes through a check of the table first. Each finding has a level:
| Level | What happens |
|---|---|
| Blocked | It cannot be done from here: Can’t <action> from here, with the reason and what to do instead. |
| Risk | You type the name to go ahead. |
| Note | One click. |
What DBCatz checks for:
- dropping or changing the primary key, a primary key that would allow
NULL, auto increment turned off; - foreign keys — types, character set and collation must match the referenced column; columns another table points at; an index a foreign key needs;
- dropping the last column of a table;
- existing values that would not fit — longer than the new length, not numbers, with decimals for an integer
type, not in the new
ENUMlist, orNULLs in a column that becomesNOT NULL; - duplicates before a
UNIQUE, andUNIQUEon a newNOT NULLcolumn; - type and collation changes, and text turned into another type;
- building an index on a large table, and a large table rebuild;
- renaming — Apps using the old name break;
- dropping an index — Queries may get slower.
The main process checks again before the ALTER is sent, so the window cannot skip it.
Dropping and emptying
| Action | What you confirm | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| Drop one table | Drop
|