01. What Text-to-SQL Solves
Turning a plain language question into an SQL query is a core challenge in data access. Modern large language models make it practical because they can generate candidate SQL code automatically. But their first attempt is often wrong. So systems add an error-driven correction loop. They execute the candidate query against the database. If they get a syntax error or an empty result set, they feed that real feedback back to the model. The model then rewrites the query. This repair cycle boosts accuracy far more than just asking the model to double-check itself. The loop is limited to two repair attempts to keep costs low. Safety is equally important. You cannot trust a raw model output to run directly on production data. The code might drop tables or access other tenants. So engineers enforce a read-only gate. They parse the query into an abstract syntax tree, using a parser like sqlglot. This confirms the statement is only a select query. No insert, delete, or drop is allowed. String matching is too easy to bypass with comments or nesting. The trade-off is between expressiveness and safety. Some systems constrain what users can ask before errors happen. Others let users type freely and fix errors afterward. Which approach yields higher user satisfaction is still an open question. The field is new, with very recent papers and no prior work on Cloudflare D1. Each design choice remains unsettled.
The self-healing loop and SELECT-only gate define how text-to-SQL turns plain questions into corrected, safe queries.
_MAX_REPAIR_ATTEMPTS = 2
_MAX_ROWS = 50
# validate_sql → {execute_sql, repair_sql, END}
# execute_sql → {repair_sql, summarize}
# repair_sql → validate_sql
# summarize → END
# validate_sql runs a SELECT-only gate via sqlglot parse + statement check.
# If it fails (not SELECT), route to END.
# If it passes, route to execute_sql.
# On error from execute_sql, if repair_attempts < _MAX_REPAIR_ATTEMPTS, route to repair_sql.
# repair_sql receives failed SQL + error text, regenerates corrected SELECT.
# Early-accept: successful execution (even empty rows) ends loop.