SQL guards
The exact rules the AST inspector enforces on every submission — WHERE requirements, LIMIT injection, transactions and blocked statements.
Updated:
Every submission is parsed and inspected before it can enter the approval queue. A request that violates a guard is rejected at submission with an explicit message — it never reaches a DBA, let alone a database.
The rules
UPDATE / DELETE require a WHERE
DELETE FROM users; -- ✗ rejected
DELETE FROM users WHERE id = 42; -- ✓ allowed (still needs approval)
This applies inside transactions too. There is no override — a full-table
write must be expressed explicitly (e.g. WHERE 1 = 1), so it can never happen
by accident.
SELECT gets a LIMIT
- No
LIMIT? The default (1000) is injected automatically. LIMITabove the hard cap (10000)? It is clamped down.LIMIT ALL(PostgreSQL) counts as unlimited and is clamped.- Offsets are preserved:
LIMIT 10, 20000becomesLIMIT 10, 10000,LIMIT 99999 OFFSET 4becomesLIMIT 10000 OFFSET 4. - Subquery LIMITs are left alone; only the outer query is guarded. Locking
clauses are respected — the injected LIMIT lands before
FOR UPDATE.
Both numbers are configurable — see Configuration. The prepared SQL (with the injected LIMIT) is shown to the developer before submission and to the DBA at review; what you approve is exactly what runs.
Multiple statements need an explicit transaction
UPDATE a SET x = 1 WHERE id = 1;
UPDATE b SET y = 2 WHERE id = 2; -- ✗ rejected: two bare statements
BEGIN;
UPDATE a SET x = 1 WHERE id = 1;
UPDATE b SET y = 2 WHERE id = 2;
COMMIT; -- ✓ allowed, runs atomically
The worker wraps the statements in a real transaction: if any statement fails,
everything rolls back. ROLLBACK in a submission is rejected — rollback on
failure is automatic, not something to hand-write. Nested transactions are not
supported.
Always blocked
Some statements are refused outright, whatever the role:
DROP DATABASE/DROP SCHEMAGRANT,REVOKECREATE USER/ALTER USER/DROP USER(andROLE/LOGINvariants)SET GLOBALSHUTDOWN
Database administration belongs in your infrastructure tooling, not in a query portal.
Dialects and the conservative fallback
The parser is MySQL-dialect-first. Statements it cannot fully parse (PostgreSQL
casts like ::jsonb, driver-specific operators) are still guarded
best-effort by keyword: an unparseable UPDATE without WHERE is still
rejected, an unparseable SELECT still gets a LIMIT appended — and anything
unclassifiable is treated as a write, which means full approval scrutiny.
When in doubt, QueryProxy errs on the strict side.