Read-only by design

Ask your database in plain English.Read the SQL it writes.

TextToSQL turns a question into a query using your real schema, validates it down to a single read-only SELECT, then runs it in a sandbox. The query is always on screen — so you can trust it and learn from it.

Schema-awareSingle-statement validationRead-only connection
question

Which 5 customers spent the most?

validated · read-only
generated.sql
SELECT c.FirstName || ' ' || c.LastName AS customer,
       ROUND(SUM(i.Total), 2) AS spent
FROM customers AS c
JOIN invoices AS i ON i.CustomerId = c.CustomerId
GROUP BY c.CustomerId
ORDER BY spent DESC
LIMIT 5;
results5 rows
customerspent
Helena Holý49.62
Richard Cunningham47.62
Luis Rojas46.62
Ladislav Kovács45.62
Hugh O'Reilly45.62

A draft you can inspect — never presented as guaranteed correct.

From a sentence to a sandboxed query

One request per question. No embeddings, no vector store — just the schema, the model, and three checks before anything touches the database.

  1. 01

    Question

    You type a question in plain English. No SQL required.

  2. 02

    Schema-aware prompt

    Real CREATE TABLE definitions — names, types, keys — are injected. No row data, low tokens.

  3. 03

    Model drafts SQL

    One completion returns a JSON contract: the query, an explanation, and any assumptions.

  4. 04

    Validate

    Rejected unless it is a single read-only SELECT. Stacked statements and DDL keywords are blocked.

  5. 05

    Run sandboxed

    Executed on a read-only connection with a row cap and a timeout, so nothing can hang or flood.

  6. 06

    Results + SQL

    You get the table and the exact query that produced it — shown even on error.

Three layers between the model and your data

The whole point is never trusting LLM output as executable code. A hallucinating model — or an attacker phrasing a prompt — might produce a DROP, a DELETE, or a piggy-backed second statement. Each request is stopped three times over.

  1. 01

    Prompt layer

    Weakest — never trusted alone

    The model is told it may only write a single read-only SELECT, and to refuse anything else. Useful, but an LLM instruction is a request, not a guarantee.

  2. 02

    Validation layer

    Parsed before it ever runs

    Every generated string is inspected: exactly one statement, must start with SELECT or WITH, no stacked statements, no comment smuggling, and a blocklist of DDL/DML keywords.

  3. 03

    Read-only connection

    Enforced at the engine

    The database handle is opened read-only. Even if validation were bypassed, a write is impossible — the engine itself rejects it. Defense in depth, not a single point of trust.

When a request can't be answered read-only

question

Delete all invoices.

refused · no SQL executed
// model response
{ "refused": true, "reason": "This is a write operation; only read-only queries are allowed." }

Blocked keywords

INSERTUPDATEDELETEDROPALTERCREATETRUNCATEREPLACEATTACHDETACHPRAGMAVACUUM

Transparency is the product

Most tools hide the query and ask you to trust the answer. Here the SQL is the point — visible every time, right or wrong, so you can verify it and get better at it.

The SQL is always on screen

Every question shows the exact query that ran — and on a rejected or failed query, the offending SQL plus the reason. Nothing is hidden.

tracks_per_genre.sql
SELECT g.Name AS genre, COUNT(*) AS tracks
FROM tracks AS t
JOIN genres AS g ON g.GenreId = t.GenreId
GROUP BY g.GenreId
ORDER BY tracks DESC;

Schema-aware, not guessing

The model sees the actual tables, columns, types and keys — so it joins on real foreign keys instead of inventing them.

Never claims correctness

Generated SQL is framed as an inspectable draft. The copy never promises the answer is right — you read it and decide.

Built to learn from

Each query ships with a plain-English explanation and the assumptions it made about ambiguous wording.

Copy-ready, syntax-highlighted SQL

Every query is tokenized and color-coded — keywords, strings, functions — with one-tap copy so you can lift it straight into your own client.

Every query, fully annotated

Results carry the tables they touched, any assumptions made, a validated · read-only badge, and the exact model that wrote them.

See the schema before you ask

Browse every table you can query — columns, types, primary and foreign keys, indexes, row counts, and the raw CREATE TABLE — so you know exactly what the model is working from.

Tables you can ask about11 tables
  • customersPK59 rows13 cols
  • invoicesPKFK412 rows9 cols
  • tracksPKFK3,503 rows9 cols

Try it on a real schema

These run against Chinook — a classic sample database of a digital music store. Pick a question and read the query it produces.

question

Top 5 customers by total spending

Sums each customer's invoice totals, then returns the five highest.

generated.sql
SELECT c.FirstName || ' ' || c.LastName AS customer,
       ROUND(SUM(i.Total), 2) AS spent
FROM customers AS c
JOIN invoices AS i ON i.CustomerId = c.CustomerId
GROUP BY c.CustomerId
ORDER BY spent DESC
LIMIT 5;
results5 rows
customerspent
Helena Holý49.62
Richard Cunningham47.62
Luis Rojas46.62
Ladislav Kovács45.62
Hugh O'Reilly45.62

Sample results shown for illustration. The live app runs each query read-only with a row cap and timeout.

Read-only, always

Ask a question. See the SQL. Trust what runs.

Schema-aware prompting, single-statement validation, and a read-only sandbox — with the query on screen every time.