# Letting an LLM Write SQL Against Your Warehouse Safely

> For approved business answers, the safest SQL is SQL the model never writes. MLX separates typed governed Twin queries from tightly bounded exploratory access.

Published: 2025-11-25
Updated: 2026-07-24
Author: Archie Norman (Founder, MLX)
Category: Architecture
Tags: twin, warehouse, sql, safety, llm, data-access, finance-ai
Canonical URL: https://mlx.systems/blog/warehouse-sql-safety

## TL;DR

- The governed Twin path does not accept SQL from the model: it validates a typed query intent and mechanically compiles it against one activated published relation.
- Exploratory access is a separate surface where the model can author SQL; the Postgres broker restricts it to one read-only SELECT or WITH query, validates tenant scope, applies schema controls, limits rows, and sets a timeout.
- Prompt instructions are not the safety boundary. Product contracts, typed tools, SQL parsing, organisation-scope validation, read-only transactions, and deployment-specific database privileges are.

---

“The agent can query your warehouse” can describe two very different products.

In one, the model decides which tables exist, invents a join, writes SQL, and hopes the result matches the business question.

In the other, the organisation has already approved a Twin product. The model can ask for a bounded selection from that product, but it cannot choose another relation or author the SQL that reaches the database.

MLX supports both governed and exploratory work, but it does not pretend they have the same risk profile.

## Two Query Surfaces, Two Jobs

| Surface | Intended use | What the model supplies | What the runtime controls |
| --- | --- | --- | --- |
| Governed Twin query | Answers from an approved business product | Product slug and typed record or aggregate intent | Activated relation, allowed fields, organisation scope, SQL compilation, row limits, evidence |
| Exploratory warehouse query | Explicit schema discovery and analysis outside the direct governed path | Read-only SQL after list/describe discovery | Statement class, referenced relations, schema visibility, organisation scope, timeout, row limit, read-only transaction |

The first is the normal destination for recurring business questions. The second is useful for discovery and genuinely exploratory analysis, but it deserves tighter scrutiny because the model authors query text.

## The Governed Path: No Model-Authored SQL

A published Twin product includes a stable relation, product contract, version, query policy, freshness evidence, and generated policy/routing snapshot.

When that product is activated for a direct governed run, the agent receives one tool: `mlx_governed_product_query`. It does not receive the generic list, describe, and SQL tools.

The governed tool accepts a typed request:

- the activated product slug;
- one or more record or aggregate queries;
- approved fields and measures;
- filters;
- grouping and ordering;
- a bounded limit.

The runtime then:

1. verifies that the requested product is activated;
2. loads its machine query policy;
3. checks the organisation against the runtime scope;
4. rejects fields, measures, filters, or ordering outside the contract;
5. injects the organisation predicate;
6. compiles SQL mechanically against the exact published relation;
7. executes through the read-only warehouse broker;
8. produces governed execution evidence for the run.

The model never supplies a relation name or SQL string.

This removes whole classes of failure. Prompt injection cannot persuade the model to add `DROP TABLE`, scan a payroll relation, or join an unapproved source because those concepts are absent from the tool schema and compiler.

The product can still be wrong. A human may have approved a bad definition, source data may be stale, or the requested analysis may not be represented by the exposed fields. Governance is not mechanical truth. It is a reviewable contract with explicit evidence and limits.

## The Exploratory Path: Treat SQL as Untrusted Input

Some tasks begin before a suitable product exists. For those tasks, the generic warehouse plugin exposes three operations:

1. list relations visible to the tenant scope;
2. describe an identified relation;
3. run a read-only SQL query.

The active warehouse skill tells the agent to discover first, describe unfamiliar relations, keep projections narrow, include organisation scope, and refuse to invent schemas. Source-specific skills can add query guardrails for a particular relation or semantic rule.

Those instructions improve behaviour. They are not the hard safety boundary.

The Postgres broker treats the generated SQL as untrusted and applies deterministic checks.

### One read-only statement

The broker accepts only a single `SELECT` or `WITH ... SELECT` query. It strips trailing semicolons and rejects mutation, DDL, session changes, and multi-statement payloads.

The same restriction is applied again by the database transaction: execution begins with `BEGIN READ ONLY`.

### Parsed relation scope

The broker parses every referenced table and fails closed if it cannot establish the complete relation set. Configured schema allow-lists are enforced, and runtime-hidden or candidate-only relations have additional visibility rules.

An allow-list must still be configured and verified for the deployment. Code support for `WAREHOUSE_ALLOWED_SCHEMAS` is not evidence that a particular target set it correctly.

### Organisation-scope validation

Warehouse access requires at least one allowed organisation ID. The tenant-scope validator checks the query against column metadata and rejects a base-table query that cannot be proven to apply the required organisation boundary.

That is stronger than a tool description saying “remember the organisation filter.”

### Bounded execution

Interactive results are wrapped in an outer row limit, capped at the configured broker maximum. The current defaults are 50 rows with a maximum of 200 for interactive queries. The broker also sets a statement timeout inside the transaction; the current default is 15 seconds.

These controls bound returned data and query duration. They are not a universal byte-scan or warehouse-cost guarantee. Any engine-specific resource controls must be configured and proven separately.

### Result sanitation and evidence

The broker sanitises returned PostgreSQL values and reports the executed query, row count, duration, columns, engine version, and referenced tables. Governed flows add execution-certificate evidence around the query result.

That is useful runtime evidence, but it should not be inflated into a promise that every data point can be replayed forever. Retention, log export, and auditor access remain deployment concerns.

## What the Current Controls Do Not Prove

Application guardrails are only one layer. Before approving exploratory SQL in a production target, establish:

- the actual database role used by `WAREHOUSE_DATABASE_URL`;
- its grants and lack of write privileges;
- the configured schema allow-list;
- the configured row and timeout limits;
- engine-side workload and cost controls;
- network paths available to the database role;
- runtime event and log retention;
- which users and sessions may request warehouse access.

The broker's read-only transaction is a meaningful defence. It does not remove the need for a least-privilege database role. If the connection is over-privileged, that is a deployment defect even if the application gate rejects writes.

## When to Publish a Product Instead

Exploratory SQL is appropriate when the question itself is still being discovered. It should not become the permanent route for a recurring board, finance, sales, or operations metric.

Promote the work into a Twin product when:

- the question recurs;
- the definition affects more than one team;
- joins or filters encode business policy;
- freshness matters to interpretation;
- the answer needs a named owner;
- regression evaluation is valuable;
- agents should use one approved meaning rather than rediscover it.

Publication moves the risk boundary. Instead of asking “did the model write safe SQL this time?”, the organisation reviews the proposed mapping and contract once, validates the published relation, and lets the runtime compile each bounded query.

The exploratory path helps discover the product. It should not impersonate the product.

## Warehouse Reads Are Not Writeback

The warehouse broker is read-only. A request to post a journal, update a CRM record, or import workspace records belongs to a different tool and capability surface.

Safe writeback requires more than adding `INSERT` to the SQL gate:

- a typed operation against a specific destination;
- explicit user and tenant authority;
- connector-specific permissions;
- approval where required;
- idempotency and duplicate handling;
- audit and reconciliation evidence.

A task skill may guide the workflow, but it cannot grant the capability or turn a read-only connector into a write-capable one.

## What to Ask a Vendor

1. Does the governed path accept model-authored SQL at all?
2. What exact product relation and contract constrain a governed answer?
3. Which tool schema is exposed to the model?
4. How are fields, measures, filters, row scope, and limits enforced?
5. For exploratory SQL, how are statement type and referenced tables parsed?
6. What happens when relation scope or column metadata cannot be established?
7. Which row, timeout, schema, and engine-cost controls are active on this target?
8. What database role is used in production?
9. Which query and product evidence is retained?
10. How does an exploratory definition become a reviewed and published product?

The bar should be higher than “the model usually writes good SQL.”

For governed finance answers, the strongest design is to prevent it from writing SQL at all.

---

**Product context: *[Why Your Operational Reporting Is Lying to You](/blog/operational-reporting-disconnect).*** See how fragmented evidence becomes an approved Twin product before the agent queries it.

**Operational context: *[Skills, Not Prompts](/blog/skills-not-prompts).*** Why task guidance and deterministic tool enforcement belong in different layers.
