<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashish sinha</title>
    <description>The latest articles on DEV Community by Ashish sinha (@ashish_sinha_5241c7673d93).</description>
    <link>https://dev.to/ashish_sinha_5241c7673d93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114397%2F3524a3c8-07e7-4ac0-a016-fd931fa4e63d.jpg</url>
      <title>DEV Community: Ashish sinha</title>
      <link>https://dev.to/ashish_sinha_5241c7673d93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashish_sinha_5241c7673d93"/>
    <language>en</language>
    <item>
      <title>Your text-to-SQL agent picks tables before security runs. Here’s the fix.</title>
      <dc:creator>Ashish sinha</dc:creator>
      <pubDate>Mon, 07 Sep 2026 18:22:11 +0000</pubDate>
      <link>https://dev.to/ashish_sinha_5241c7673d93/your-text-to-sql-agent-picks-tables-before-security-runs-heres-the-fix-11bb</link>
      <guid>https://dev.to/ashish_sinha_5241c7673d93/your-text-to-sql-agent-picks-tables-before-security-runs-heres-the-fix-11bb</guid>
      <description>&lt;p&gt;I build text-to-SQL agents on Oracle and Postgres for a living. Every one of them had the same bug, and it wasn’t in my code. It was in the order of operations.&lt;br&gt;
The bug&lt;br&gt;
The schema goes into the prompt before the query runs. Row-level security runs when the query runs. So the model sees a table the user can’t read, writes perfectly valid SQL against it, the database returns zero rows, and the agent says “no records found”. A wrong answer, delivered with confidence. Vanna (23k stars, archived March 2026) applied identity exactly there: at execution, after the model had seen everything.&lt;br&gt;
The fix&lt;br&gt;
Apply identity at selection. Decide which tables the model is shown, per caller, before any SQL exists. A restricted table isn’t ranked low — it’s absent.&lt;br&gt;
from schemagate import Catalog, Principal&lt;/p&gt;

&lt;p&gt;cat = Catalog().bootstrap("postgresql://localhost/app")&lt;br&gt;
cat.restrict("hr_compensation", roles=["payroll"])&lt;/p&gt;

&lt;p&gt;analyst = Principal("okta:jdoe", roles={"analyst"})&lt;br&gt;
cat.select("salary by employee", principal=analyst).table_names   # no hr_compensation&lt;/p&gt;

&lt;p&gt;pip install schemagate — one dependency, no API key, any SQLAlchemy database.&lt;br&gt;
The side effect that pays for it&lt;br&gt;
You’re now sending ~6 tables instead of the schema dump. Measured on the test schemas: 65–79% fewer prompt tokens on small ones, 97% on a 260-object one (16,095 → 444 per question). The selector never calls a model — BM25 plus a hashed embedder, offline, milliseconds.&lt;br&gt;
What broke while building it&lt;br&gt;
Six invented schemas found ten bugs before release. My favourite: a three-column orders_bkp outranked the real orders table, because short documents win cosine similarity. Backup and staging copies now rank below the object they shadow. The full list is in TESTING.md.&lt;br&gt;
Where it plugs in&lt;br&gt;
MCP server for Claude Desktop and Cursor, a LangChain retriever, a native Oracle 23ai VECTOR store, and a browser demo that needs no install: &lt;a href="https://ashishsinha1602.github.io/schemagate/" rel="noopener noreferrer"&gt;https://ashishsinha1602.github.io/schemagate/&lt;/a&gt;&lt;br&gt;
Repo: &lt;a href="https://github.com/ashishsinha1602/schemagate" rel="noopener noreferrer"&gt;https://github.com/ashishsinha1602/schemagate&lt;/a&gt; — tell me where it breaks on your schema.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>sql</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
