Turning Claude into Postgres so I can raise a Series A
Turning Claude into Postgres so I can raise a Series A
Sign up for email updates<br>Get interesting posts about web development and more programming straight<br>in your inbox!
Turning Claude into Postgres so I can raise a Series A<br>Claude Postgres Databases Experiments
By Jacob Jackson<br>August 1, 2026<br>Right now, it seems like nobody cares about databases aside from their AI opportunities. Databricks, PlanetScale, Supabase, even Andy Pavlo’s mysterious SYDHT all are adding AI optimization, vector storage, MCP servers, etc. And, of course, investors dump money on all of them. I want that money.
But, for all of their AI appendages, most databases’ core functions are still woefully boring and AI-less. Sure, an LLM might use your database, and maybe even tune database parameters, but the database itself only runs deterministic code. What if, instead, (almost) everything was AI? Claude might not be great at writing database code, but surely it can be a database itself when I hand it a filesystem.
It will be expensive and slow, but I am not a stranger to building shitty databases. Besides, it is a good way to display database internals. So, in the spirit of Keenan Feldspar, let’s build a trendy demo that performs terribly.
Scaffolding claudegres
The goal is to teach Claude to process and respond to queries like a real Postgres database would, including persisting data. Covering all of Postgres would take too long, so I focused on the TPC-C Twitter benchmark requirements.
I decided to use Sonnet 5 medium because it is relatively fast, has a larger context window, and is probably smart enough for query planning. Yeah, I probably should have used GLM-5.2 or GPT 5.6 Luna, but these didn’t exist when I started. More importantly, GPTgres sounds terrible.
Claude can only interact via text responses over HTTP, so I used Buena Vista, a Python Postgres proxy, to convert Postgres’s TCP-based wire protocol packets to simple SQL text.
I told Claude to respond with a structured textual format similar to the format Postgres uses for COPY, which is converted to response packets by Buena Vista. I added this to the prompt:
Reply with ONLY the result. Each part on<br>its own line:<br>STATUS: command tag><br>COLUMNS: [["col1>","type1>"],...] (omit if no result set)<br>DATA (omit if no result set)<br>one row per line, columns separated by a single TAB, in the text COPY format>
COLUMNS is a JSON array of [name, type] pairs. Each data row is its own line:<br>column values joined by a single tab character, in Postgres's text COPY<br>representation. Numbers are written bare, booleans as t or f, and SQL NULL as \\N<br>(an empty field is a zero-length string, which is distinct from NULL). Text is<br>written literally with no surrounding quotes; encode special characters with<br>backslash escapes (\\t for tab, \\n for newline, \\r for carriage return, \\\\ for<br>a literal backslash).
Example: SELECT id, name FROM users returning two rows:<br>STATUS: SELECT 2<br>COLUMNS: [["id","int4"],["name","text"]]<br>DATA<br>1 lietkynes<br>2 letoatreides
And… it works! Well, not really.
psql -h 127.0.0.1 -p 5433<br>psql (18.4, server 14.0 (BuenaVista/Claude))<br>Type "help" for help.
jacob=> CREATE TABLE users (users_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT UNIQUE NOT NULL, home_planet TEXT NOT NULL);<br>CREATE TABLE<br>jacob=> INSERT INTO users(email, home_planet) VALUES ('[email protected]','Arrakis');<br>42P01: relation "users" does not exist
Claude can respond, but it still has no way of persisting the internal database files.
Implementing storage and encoding
Claude needs to persist the data itself. Giving it access to a disk is easy enough. I created an API similar to Postgres’s fd.c (also kind of md.c) API, which normally handles data retrieval and storage in the form of pages (8kB chunks) on disk. I then passed those functions to Claude to call when needed. However, encoding the data in files is a little more complicated.
Normal Postgres uses a binary encoding format for rows, where fixed-length binary headers define the row’s metadata, and the length of all dynamically sized values (like TEXT) is determined by leading integers. This is much more efficient than any text encoding: Integers are more compact, and fixed size columns don’t require a terminator to show their end (like a comma would in CSV). This format also allows for simpler random access, so Postgres can query specific data within a page without reading the whole page. Unfortunately, Claude doesn’t play so well with it.
Claude doesn’t tokenize binary efficiently, and, more importantly, Claude doesn’t have a strong enough grasp of data size to use offset/length integers instead of terminators to find data. Besides, the JSON-based networking doesn’t encode binary efficiently. So, I need a text format.
I first tried using the same tab-based format for file storage, which seemed to work.
Unfortunately, Claude got a bit rebellious (foreshadowing?). I told it to...