In 2026 the phrase you hear most often about databases and language models is “never attach an LLM directly to production.” It became common wisdom the hard way — enough teams wired a model to a live connection with a broad prompt and a psql session, and enough of those sessions ran an UPDATE without a WHERE clause, that the industry backed away from the whole idea.
We think the conclusion was right and the framing was slightly off. The problem was never that a model wrote SQL. Models are genuinely good at turning “deactivate the trial accounts that never converted” into a query. The problem is what happened next: the query executed, immediately, against real data, with nobody in between. So that’s the part we removed. LeafSQL lets a model write SQL. It never lets that SQL run unreviewed.
The temptation, and the failure mode
Autonomous execution is seductive because it demos beautifully. You type a sentence, rows change, everyone nods. The failure mode only shows up later, and it shows up the same way every time: the model was confident, the SQL was plausible, and the blast radius was larger than the sentence implied. “Clean up the test orders” matches more rows than you meant. “Fix the status column” was an UPDATE across the whole table because the model couldn’t see which subset you had in your head.
The model isn’t reckless. It just has no stake in the outcome and no way to know what you’d consider a mistake. A confident wrong answer and a confident right answer look identical until the transaction commits. The only reliable place to catch the difference is before the write lands — with a human who does have a stake looking at exactly what will run.
The category language the market settled on says it well: agents propose, you approve. We built the write path so that proposing and approving are two separate, visible steps.
The guarded write loop
Every write request in LeafSQL moves through four stages: classify, confirm, execute, rollback. None of them is skippable.
Classify. Before anything touches your database, the request is classified by intent — is this a read, a write, or a schema change (DDL)? A “show me the churned accounts” question and a “delete the churned accounts” command take different paths from the first step. Reads never enter the write path at all, and writes and DDL are held to the confirmation gate below.
Confirm. For a write, the model generates the SQL, and LeafSQL previews the affected rows and stores the statement as a pending write — it does not execute. Nothing runs until you send an explicit confirmation for that specific pending write. The pending record has a short time-to-live, so an unconfirmed write expires rather than lingering as a loaded gun. You see the SQL and the rows it will touch, and you decide.
Execute. When you confirm, LeafSQL runs the write inside a transaction, capturing the rows as they were before and as they are after — using RETURNING on Postgres — and records an audit entry for the statement. The before/after capture is what makes the last stage possible.
Rollback. Because the change was captured, a write can be reversed. LeafSQL generates the inverse of the last write from the audit record, so a confirmed change that turned out wrong has an undo.
Here’s the shape of what execution looks like — the transaction, the RETURNING that captures the after-state, and the audit that captures the before:
begin;
-- The write you confirmed. RETURNING captures the AFTER rows;
-- LeafSQL recorded the BEFORE rows in the preview step.
update accounts
set plan = 'inactive'
where trial_ends_at < now()
and converted_at is null
returning id, plan, trial_ends_at;
-- Both states are written to a write_audit row so the change
-- can be inverted later. Then, and only then:
commit;
Nothing about that query is exotic. That’s the point. Safety here isn’t a clever model or a smarter prompt — it’s ordinary transactional discipline applied to a step that hype usually skips.
Reads are guarded too, differently
Writes get the confirmation gate. Reads get a different guard, because a read shouldn’t need a human in the loop but also shouldn’t be able to smuggle in a write. Before any generated read query runs, LeafSQL checks it: it must be a single statement, it must begin with SELECT or WITH, and anything carrying a write keyword is rejected outright. A query that fails the check never reaches your database.
We’ll be straight about what that guard is and isn’t. Today it’s an enforcement layer in front of the query, not a read-only role on the connection itself. Pushing that same guarantee down to the database session — so the connection is physically incapable of writing during a read — is the stronger version, and it’s where we’re headed. Naming the current boundary honestly is more useful to you than implying the model is sandboxed at a level it isn’t yet.
The honest limits
Rollback is single-step and best-effort. It reverses your most recent confirmed write from its audit record; it is not a general-purpose time machine across a long chain of operations, and some changes are genuinely hard to invert cleanly. We generate the inverse we can and tell you when we can’t, rather than promising an undo for everything. The confirmation gate is the primary safety mechanism; rollback is the backstop, and it’s honest about being a backstop.
That’s a deliberate posture. The whole design assumes the model can be wrong, the generated SQL can be wrong, and the safe place to be wrong is before commit, in front of a person, not after it in an incident channel.
Why this shape
Letting a model draft SQL and refusing to let it execute unreviewed isn’t a compromise between speed and safety — it’s the recognition that the drafting and the deciding are different jobs. The model is good at the first and has no business doing the second. If you want to see where the reviewed change goes next, it becomes a migration PR or a diff you approve. And the same “propose, don’t apply” instinct is why we surface schema drift as an alert instead of silently healing it.
Agents propose. You approve. On a production database, that ordering is the entire feature.