<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Teaching an LLM Your Schema's Rules: Inside Jailer's AI Subsetting Assistant]]></title><description><![CDATA[Teaching an LLM Your Schema's Rules: Inside Jailer's AI Subsetting Assistant]]></description><link>https://wisser.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Teaching an LLM Your Schema&apos;s Rules: Inside Jailer&apos;s AI Subsetting Assistant</title><link>https://wisser.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 19:30:44 GMT</lastBuildDate><atom:link href="https://wisser.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Teaching an LLM Your Schema's Rules: Inside Jailer's AI Subsetting Assistant]]></title><description><![CDATA[Jailer is an open-source tool for referentially-intact database subsetting and relational data browsing. Source: github.com/Wisser/Jailer
Anyone who has tried to build a small, realistic test database]]></description><link>https://wisser.hashnode.dev/teaching-an-llm-your-schema-s-rules-inside-jailer-s-ai-subsetting-assistant</link><guid isPermaLink="true">https://wisser.hashnode.dev/teaching-an-llm-your-schema-s-rules-inside-jailer-s-ai-subsetting-assistant</guid><category><![CDATA[Databases]]></category><category><![CDATA[AI]]></category><category><![CDATA[subsetting]]></category><dc:creator><![CDATA[Wisser]]></dc:creator><pubDate>Thu, 09 Jul 2026 09:36:49 GMT</pubDate><content:encoded><![CDATA[<p><em>Jailer is an open-source tool for referentially-intact database subsetting and relational data browsing. Source:</em> <a href="https://github.com/Wisser/Jailer"><em>github.com/Wisser/Jailer</em></a></p>
<p>Anyone who has tried to build a small, realistic test database from a production schema knows the drill: you don't just want "all orders." You want a customer's orders, their order items, the products they reference — but not the entire payment history, not every audit log row, not the internal reporting tables three joins away. Every table you pull in for one reason drags in three more you didn't ask for, because your schema is a graph, not a list.</p>
<p>The obvious alternatives don't hold up. A full backup-and-restore is usually too big and too slow for a local dev loop, and in plenty of organizations it isn't even allowed to leave the production network. A hand-written <code>SELECT</code> is fast to write and just as fast to break — the moment it misses a foreign key, you're left with orphaned rows, or an import that fails on a constraint violation.</p>
<p>Jailer solves the mechanical half of this problem: give it a starting table, a condition, and a set of rules for which relationships to follow, and it will walk the foreign-key graph and hand you back a consistent, referentially valid slice of the database. What it didn't solve, until recently, was the tedious half — sitting down and deciding, association by association, what to include and what to cut off. On a schema with a few hundred tables, that's not a five-minute job.</p>
<p>The output isn't limited to one format, either: topologically-sorted SQL inserts, DbUnit datasets, or JSON/YAML/XML, so the same extraction model can feed a CI test-data seed script, a local debugging copy, or an archival export that prunes old rows out of a production table without leaving dangling references behind. Jailer also ships a bidirectional Data Browser and SQL console for exploring the schema by following those same relationships interactively — usually how the associations get built up in the first place, before an extraction gets automated around them.</p>
<p>This is where Jailer's <strong>AI Subsetting Assistant</strong> comes in. It's a narrower, more interesting problem than "generate me some SQL" — and the way it's built is a decent case study in what it takes to let an LLM safely edit a structured, rule-based model instead of just emitting text.</p>
<h2>What an Extraction Model Actually Is</h2>
<p>Before the AI part makes sense, the underlying model needs to be clear, because it's not just "a query."</p>
<p>A Jailer extraction model has three parts:</p>
<ul>
<li><p>A <strong>subject table</strong> — the table you start from.</p>
</li>
<li><p>A <strong>condition</strong> — a WHERE clause that picks the starting rows out of that table (aliased <code>T</code>).</p>
</li>
<li><p>A set of per-<strong>association</strong> <strong>restrictions</strong> — an association being a foreign-key relationship (or a user-defined one) between two tables.</p>
</li>
</ul>
<p>The part that trips people up is what happens by default: starting from the subject rows, Jailer automatically follows <em>every</em> association in the data model, recursively, until nothing new is reachable. That's the whole point — it's how you get a referentially consistent snapshot instead of a set of orphaned rows. But it also means the default behavior is to over-include. If you don't explicitly tell Jailer to stop at the payments table, it won't stop on its own; it doesn't know your intent, only the graph.</p>
<p>A restriction on an association is one of three things: <code>false</code> (don't follow it — exclude that branch entirely), a SQL predicate (follow it, but only for rows matching the predicate, with <code>A</code>/<code>B</code> aliasing the association's source/destination tables), or empty (follow it, no filtering — the default). Building a correct extraction model is really the exercise of walking every association reachable from your subject table and deciding which of these three it needs.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a4f654282e8e4966f961510/d59cd0ca-7406-4d0f-9ff4-8e12857c5b8c.png" alt="ORDER is the subject table with condition T.CUSTOMER_ID = 42; it includes ORDER_ITEM and PRODUCT, and excludes PAYMENT via a false restriction." style="display:block;margin:0 auto" />

<p><em>The extraction model for "orders of customer 42, with items and products, no payments": one subject table and condition, plus one restriction decision per association.</em></p>
<h2>From Prose to a Reviewable Model, Not Just SQL</h2>
<p>The AI Subsetting Assistant lives in the Extraction Model Editor — reachable from the toolbar's "AI" button, the "AI Subsetting Assistant…" menu item (<code>Ctrl+Shift+A</code>), or directly from the startup wizard when you're creating a model from scratch. You type a description, for example:</p>
<blockquote>
<p>"All orders for customer 42, with order items and products, but without payment history."</p>
</blockquote>
<p>and the assistant doesn't hand back a SQL query. It hands back a structured proposal for the extraction model itself:</p>
<pre><code class="language-json">{
  "subject": "ORDER",
  "condition": "T.CUSTOMER_ID = 42",
  "restrictions": [
    {"association": "payments",    "condition": "false"},
    {"association": "order_items", "condition": ""},
    {"association": "products",    "condition": ""}
  ],
  "explanation": "Extracts orders of customer 42. Order items and
                   products are included. Payment history is
                   explicitly excluded."
}
</code></pre>
<p>That JSON shape is the actual response contract, not a simplification — subject table, subject condition, a restriction decision for every named association, and a plain-language explanation. The system prompt that produces this is worth a closer look, because it's essentially a compressed spec of Jailer's own traversal semantics, written for an audience that has never seen the tool before: it explains that Jailer follows every association by default, that restrictions are the only way to stop it, what the <code>A</code>/<code>B</code> aliases mean in a restriction predicate, and where a filter belongs — on the subject condition versus on a restriction — depending on which table it constrains. Getting an LLM to produce a valid, minimal restriction set for an arbitrary schema depends entirely on it understanding that asymmetry between "included by default" and "excluded by default," and the prompt exists specifically to correct for the fact that most LLM training data assumes the opposite.</p>
<h2>Why This Is Safe to Point at a Real Schema</h2>
<p>Handing an LLM the power to add or remove restrictions on a data model — the same model that determines what a production extraction pulls out of your database — is not something you want to do on blind trust. A few things here are deliberate, not incidental:</p>
<blockquote>
<p>⚠️ <strong>Nothing is applied automatically.</strong> The proposal is rendered in a preview pane — subject, condition, and a per-association list of "exclude" / "restrict: <code>&lt;sql&gt;</code>" / default — before you touch anything. Only clicking <strong>Apply to Editor</strong> commits it, and the whole change (subject, condition, and every restriction) is grouped into a single undo step, so one <code>Ctrl+Z</code> reverts it completely.</p>
</blockquote>
<p>Beyond the review step, the dialog runs two sanity filters over the model's own response before it's even shown to you:</p>
<ul>
<li><p>It strips any proposed restriction on an association where the destination has to be inserted before the source (a dependent/parent relationship) — restricting those would silently break referential integrity, so the assistant refuses to let the model do it regardless of what it proposed.</p>
</li>
<li><p>It drops restrictions on associations that aren't even reachable from the proposed subject table's closure — harmless, but noise that would clutter the review with decisions that don't matter.</p>
</li>
</ul>
<p>The effect is that the LLM's output is treated the way you'd treat a junior engineer's pull request: useful, plausible, worth reviewing — but not trusted to bypass the tool's own consistency rules or to go live without a look.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a4f654282e8e4966f961510/d9900f3e-1195-42f1-ba20-a9619b7257a3.png" alt="Diagram: request goes to the AI Subsetting Assistant, producing a JSON proposal, which passes through hard-coded sanity filters into a review pane, then either gets applied to the editor with a grouped undo, or the dialog is closed and the proposal is discarded." style="display:block;margin:0 auto" />

<p><em>End-to-end flow of a request through the AI Subsetting Assistant — the two sanity filters run automatically before you ever see the proposal.</em></p>
<h2>Scaling to Real Schemas</h2>
<p>A schema description that includes every table, column, type, and foreign key gets expensive fast once you're past a few dozen tables — both in latency and in the literal risk of blowing past a model's context window. The assistant has two independent levers for this:</p>
<ul>
<li><p><strong>Reduced Schema</strong> mode splits the work into two calls. A cheap first pass sends just the list of table names and asks the model to pick the single best subject table for the request. From that table, Jailer does a breadth-first traversal of the association graph up to a configurable table limit, and only that reduced neighborhood — not the full schema — goes into the second call that actually produces the restrictions.</p>
</li>
<li><p><strong>Omit Column Types</strong> trims the per-table description further, keeping table and column names, primary keys, and foreign keys, but dropping type information that the model rarely needs to decide on a restriction anyway.</p>
</li>
</ul>
<p>The dialog also estimates the request size in tokens before sending it and flags — in the status line, not with a blocking error — when the estimate is creeping past roughly 60% of the target model's known context window, which is a small but honest thing to surface rather than let you discover as an opaque API failure.</p>
<h2>Bring Your Own Model</h2>
<p>The assistant isn't tied to one vendor. It shares its provider plumbing with Jailer's other AI features, supporting Anthropic, any OpenAI-compatible endpoint (OpenAI itself, Azure OpenAI, Groq, and similar), OpenRouter (which includes several free models), and Ollama for models running entirely on your own machine. For anyone whose schema descriptions — table and column names, sometimes revealing plenty about a business on their own — shouldn't leave the building, Ollama's no-API-key, nothing-sent-externally mode is the relevant option, and it's a first-class citizen here, not an afterthought.</p>
<p>Both system prompts used by the assistant — the main extraction-model instructions and the lightweight subject-table-detection prompt used in Reduced Schema mode — are user-editable, with a reset-to-default button, and persist across sessions. If your schema has naming conventions or domain quirks the default prompt doesn't account for, that's the place to teach it.</p>
<h2>A Sibling, Not a Duplicate</h2>
<p>Jailer's AI Assistant dialog, reachable from the SQL Console, does something related but distinct: it generates and refactors ad-hoc SQL from natural language, with an Advisor mode for explaining and rewriting existing queries. It's built on the same request/response infrastructure as the Subsetting Assistant, but it never touches the extraction model — it writes into the SQL editor for you to review and run yourself. The two features solve different problems (querying versus configuring a repeatable extraction), and the separation is deliberate rather than a gap.</p>
<hr />
<p>What makes the AI Subsetting Assistant a more interesting case than "yet another natural-language-to-SQL box" is that it isn't generating disposable output — it's proposing an edit to a persistent, rule-based model that later runs unattended against a real database. That constraint shapes everything: the strict JSON contract instead of free text, the system prompt that front-loads the tool's actual semantics instead of assuming the model already knows them, the two hard-coded sanity filters that override the model's own suggestions when they'd violate referential integrity, and the fact that every single proposal ends at a review screen with a one-keystroke undo. For anyone building an LLM feature that edits structured application state rather than just chatting, that pattern — teach the model your domain's actual rules, then don't fully trust it anyway — is the part worth stealing.</p>
<p>Jailer is open source under the Apache 2.0 license: <a href="https://github.com/Wisser/Jailer">github.com/Wisser/Jailer</a>.</p>
]]></content:encoded></item></channel></rss>