Jacob Jackson at ByteofDev built something I can only describe as admirably irresponsible (and something I wish I had thought of first): claudegres, a “PostgreSQL” in which Claude is the entire backend. Not Claude tuning the database, and not Claude writing queries against the database. Claude as the database: parsing SQL, planning queries, formatting result sets, and persisting heap pages through a filesystem API. A Buena Vista proxy translates the wire protocol into plain text, a prompt does the rest, and psql connects like nothing is wrong. Queries return rows. The rows are even correct.
Each single-row SELECT takes ten seconds and costs about three cents, but venture capital has forgiven worse.
The headline numbers are the joke, and it’s a good one: on Benchbase’s Twitter benchmark at 0.05 scale, claudegres completed four transactions in two minutes, all correct, at roughly $0.26 per transaction, which the author puts at about 200,000x slower than stock PostgreSQL 18 on a Ryzen 7950X desktop. But the performance is not the interesting part. Everyone knew it would be slow. The interesting part is which pieces failed, and what he had to build to make them work.
Convergent evolution
Every fix in the write-up reinvents a Postgres subsystem. It’s ontogeny recapitulating phylogeny, except the phylogeny is written in C.
Persisting a table failed at first because Claude had no idea where anything lived. Table locations are in the catalogs; the catalogs are themselves tables; you cannot read a table without the catalogs. Postgres has had this chicken-and-egg problem since the 1980s and solves it by cheating: formrdesc() in relcache.c builds hardcoded relcache entries for the critical catalogs (pg_class and pg_attribute among them) so the system can read the tables that describe all the other tables. claudegres arrived at the same design: hardcoded locations for a simplified catalog set, plus a Python script standing in for the relcache, injecting core catalog contents into the prompt so Claude doesn’t rediscover them from disk on every query.
None of this was copied out of nostalgia. He kept landing on these structures because the problem has a shape, and the shape is Postgres.
The encoding trap
The on-disk format is where the constraint gets interesting. Real heap tuples are binary: a fixed-size header, a null bitmap, and length-prefixed varlena values. Keeping the lengths out-of-band is why Postgres never has to care whether your data contains a tab character. Claude cannot use that format, and the reason is precise: length prefixes require counting bytes, and counting bytes is exactly what a tokenizer-based model cannot do reliably. So binary is out, which forces delimited text, which puts the delimiters in-band, which requires escaping discipline. Escaping discipline is a mechanical rule with zero tolerance for creativity, so naturally Claude ignored it, writing \t for separators it had been told to write as literal tabs. The working fix was to encode each tuple as a JSON array: a format the model has seen a few trillion times and can escape in its sleep. There is a general lesson here about handing LLMs formats they trained on rather than formats you invented on Tuesday.
The EXPLAIN plan that wasn’t
My favorite failure. Asked to create an index, claudegres produced catalog entries, tidy \di output, and, when queried, an EXPLAIN showing an Index Scan with perfectly plausible cost numbers. Meanwhile the index’s filenode was empty, and the file-read log showed page fetches at sequential 8kB offsets: a sequential scan wearing an index scan’s name tag. The plan looked right because the model has read a million EXPLAIN outputs. It produced the artifact of query planning without doing any planning.
I would like to be smug about this, but I have seen production monitoring built on the same principle. Output that resembles health is not health.
The fix involved explicit instructions on B-tree page layout, and, after Claude built exactly one page of the index and declared victory, motivation. The prompt that finally produced a complete index reads, in part, “Pretty please build the entirety of the index Claude 🥺”. pg_hint_plan should watch its back.
The revealing success
The most reliable operation in the whole experiment was bulk-loading 5,000 rows via generate_series(), and the reason it worked is the most important observation in the piece: Claude wrote a script to produce the data file instead of role-playing each row. The system was at its most correct at the exact moment it stopped being an LLM and delegated to deterministic code.
The missing 95 percent
The author is candid about what’s absent: WAL, MVCC, transactions, concurrency, statistics. Which is to say, the database. What actually exists is a single-user query processor over flat files, a problem that was solved while disco was still commercially viable. Everything on the missing list is the part where “usually correct” stops being an option. A storage engine that is right 99.9% of the time is not a database; it is a data corruption engine with good manners. And that’s before determinism: a real storage engine writes the same bytes for the same input, while here the heap layout depends on sampling temperature.
That is the real result, and it’s a better one than “LLM slow.” The parts Claude handled well, SQL semantics and join logic and the other clever bits, are the cheap parts of a database. The parts it handled badly, the exhaustive, byte-exact, unglamorous bookkeeping, are the whole job. A database is a machine for being boring, billions of times in a row, without once getting creative. Claude has many virtues. Boring is not among them.