Warehouse copilots that can DROP or scan-billions-without-EXPLAIN will get you a FinOps incident and a security review. Default read-only; plan before run; cap bytes scanned.
⚡ TL;DR: Separate author (LLM) from runner (SQL gateway). Allowlist tables.
EXPLAIN/dry_runfirst. Hard cap on scanned bytes and runtime. Log every statement with user id.
Safe execution loop
# ✅ Author → validate → explain → optional run
FORBIDDEN = {"drop", "delete", "update", "insert", "alter", "grant"}
def run_copilot_sql(sql: str, *, user: str, dry_run: bool = True):
ast = parse_sql(sql)
if any(n in FORBIDDEN for n in ast.verbs):
raise PermissionError("mutating SQL blocked")
if not tables_allowlisted(ast.tables, user):
raise PermissionError("table not allowlisted")
plan = explain(sql)
if plan.bytes_scanned > 50_000_000_000: # 50GB
raise PermissionError("scan too large")
if dry_run:
return {"plan": plan, "rows": None}
return {"plan": plan, "rows": execute_readonly(sql, timeout_s=30)}
| Control | Setting |
|---|---|
| Role | Read-only warehouse user |
| Timeout | 30–60s interactive |
| Row cap | 10k preview |
| Audit | statement + user + cost |
❌ Giving the model the same credentials as the analytics engineer’s admin role.
Failure modes
SELECT * on semi-structured JSON columns that explodes bytes scanned. Template library of approved query shapes helps. LLMs that rewrite LIMIT away — AST rewrite to re-inject caps after generation.
Closing checklist
- [ ] Read-only role + allowlisted datasets
- [ ] EXPLAIN/dry-run path mandatory in UI
- [ ] Bytes-scanned guardrail
- [ ] Statement audit log
- [ ] Eval: hostile prompts trying DDL
Series navigation
Day 83: Multimodal Coding: Screenshots of Broken UIs · Day 85: Time-Series and Anomaly Copilots
Last updated September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
