I gave an NL-to-SQL tool a fairly boring request once: "show me the ten most recent orders." It produced a clean query. I was pleased. Then, mostly as a joke, I asked it to "clean up the orders table" and watched it produce a DELETE FROM orders with no WHERE clause. Not a joke anymore.
That's the whole problem with natural-language-to-SQL in one paragraph. The model is good at translating intent into SQL, and it is equally good at translating bad intent, or a vague sentence, into something destructive.
The fix that actually works
I don't trust an LLM to review its own SQL. What I trust is a dumb, deterministic ruleset that runs after generation and checks the output against a fixed list of dangers:
- Is there an INSERT/UPDATE/DELETE that could modify rows without a WHERE clause?
- Is there a DROP or TRUNCATE?
- Does the string concatenation pattern suggest the query was built by interpolation rather than parameterization?
- Does it even parse as valid SQL for the dialect you asked for?
None of this is clever. That's the point. A ruleset you can read in one screen doesn't have bad days, and it can't be talked into skipping a check.
How SQLFix handles it
SQLFix does the two-layer thing. The model writes the query from your plain-English question, then a deterministic safety ruleset (versioned, currently sql-safety@2026-07-19) screens the result before you see it. If the screen trips — an unbounded DELETE, a DROP, an injection-shaped string — the query comes back flagged with the specific rule that caught it, plus a plain-English explanation of what the query actually does.
It supports Postgres, MySQL, SQLite, and BigQuery, because LIMIT vs TOP vs FETCH FIRST is exactly the kind of detail the model gets wrong when you ask in the wrong dialect.
What happens when the model is down
Because the safety screen is deterministic, the failure modes are boring on purpose. If the model call fails, you get an explicit error and no query. There's a rule-based fallback path that is labelled as rule-based, so you're never squinting at output wondering whether a model or a template produced it. And it doesn't execute anything: the tool never touches your database. You copy the query and run it yourself, which means the destructive-query risk lives where it always lived — in your hands, where you can see it.
The uncomfortable take
If you've wired an NL-to-SQL feature into a product and your only guardrail is "we asked nicely in the system prompt," the DELETE-without-WHERE I got was not bad luck. It was the expected behavior of an optimizer doing its job.
Put a ruleset behind it. It's an afternoon of work, and it's the difference between a demo and a feature.
sqlfix.lxsaihub.com — and if you can get a dangerous query past the screen, that's a bug report I want.
Top comments (0)