QueryProxy
Documentation menu

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.
  • LIMIT above the hard cap (10000)? It is clamped down.
  • LIMIT ALL (PostgreSQL) counts as unlimited and is clamped.
  • Offsets are preserved: LIMIT 10, 20000 becomes LIMIT 10, 10000, LIMIT 99999 OFFSET 4 becomes LIMIT 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 SCHEMA
  • GRANT, REVOKE
  • CREATE USER / ALTER USER / DROP USER (and ROLE / LOGIN variants)
  • SET GLOBAL
  • SHUTDOWN

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.

Arrow keys to move, Enter to open.