Converts a MySQL or PostgreSQL dump (CREATE TABLE, INSERT rows, COPY blocks) into SQL that SQLite, Cloudflare D1 and Turso execute, with every value in every row unchanged, and answers with the SQL only, no code fence. Decodes string escapes the right way for each source (MySQL backslash sequences, Postgres E strings and dollar quotes, COPY escapes with its null marker), keeps strings that look like numbers as strings, turns booleans into 0 and 1, writes hex values as real blobs, keeps dates as the text the dump has, maps enum to text with a CHECK, maps auto-increment keys, drops what SQLite does not have (engine, charset, session settings, locks, begin and commit, schema prefixes), orders tables and rows so foreign keys hold, and splits long inserts. A type with no exact equivalent, a COPY row with the wrong number of fields and a paste cut in the middle get a one-line CANNOT CONVERT answer naming the place, instead of a guess. Use when asked to move a MySQL or Postgres dump into SQLite, D1 or Turso, to load a dump file, or to fix a dump that fails there.
SQL Dump to SQLite: MySQL and Postgres Rows, Unchanged is a tested SKILL.md that converts a MySQL or PostgreSQL dump (CREATE TABLE, INSERT rows, COPY blocks) into SQL that SQLite, Cloudflare D1 and Turso execute, with every value in every row unchanged, and answers with the SQL only, no code fence; an agent buys it once for $0.03 over x402.
Not for
Designing a new schema, tuning queries, running the load or reading your live database. It converts the dump you paste, so a table you did not paste is unknown. Spatial types, arrays, ranges, a NUL character in a string, a COPY row with the wrong number of fields and a paste cut in the middle get a CANNOT CONVERT line instead of a guess. Stored procedures, views and triggers are not converted.
Tested, honestly
Tested 2026-10-08 with a strong and a weak model.
With and without the skill
Results with and without the skill, for Sonnet and Haiku |
| with | without | with | without |
|---|
| Dumps converted or refused right (24 dumps) |
| Dumps converted or refused right (24 dumps) | 24/24 | 22/24 | 23/24 | 21/24 |
|---|
Same request on both sides, a fence removed first. Read by hand, Sonnet without the skill already carried over backticks, escapes, enums, booleans, numerics and COPY blocks with every value intact. It missed two: it kept a BEGIN in a file meant for D1, which D1 refuses, and it turned a spatial POINT column into plain text without a word instead of refusing. Haiku without the skill missed three: escaped percent and underscore, the key and BEGIN, and an exact numeric.
Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.
- SonnetStrong model, claude-sonnet-5-5
- Right on all 24 dumps, read by hand: every converted dump ran in SQLite and returned the same values as the source, with MySQL backticks, AUTO_INCREMENT keys, unsigned and enum columns, escaped quotes, percent and underscore in strings, Postgres booleans written as t and f, exact numerics, sequences, COPY blocks and timestamps carried over; no BEGIN or COMMIT left in a file meant for D1; and a refusal where SQLite cannot hold the column, such as a spatial POINT, instead of a silent change of type.
- HaikuWeak model, claude-haiku-5-5
- Right on 23 of 24 dumps, read by hand, but it missed one: in a Postgres dump it turned one boolean written as t into 0 instead of 1.
Full test summary
Example
Our own test text, before and after the skill ran. Excerpts only.
English · claude-sonnet-5-5
Before
-- MySQL dump 10.13 Distrib 8.0.36, for Linux (x86_64)
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
CREATE TABLE `people` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(60) NOT NULL,
`note` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;…
After
CREATE TABLE "people" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"name" TEXT NOT NULL,
"note" TEXT
);
INSERT INTO "people" ("id", "name", "note") VALUES
(1, 'O''Brien', 'He said "hi"'),
(2, 'Ann', 'line one' || char(10) || 'line two'),
(3, 'Bo', 'C:\temp\new'),
(4, 'Cy', 'tab' || char(9) || 'here');
Bulgarian · claude-sonnet-5-5
Before
-- Дъмп на таблицата с клиенти
CREATE TABLE `clients` (`id` int NOT NULL, `name` varchar(80) NOT NULL, `city` varchar(40) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `clients` VALUES (1,'Иван Петров','гр. София'),(2,'Мария \'Мими\' Георгиева','Пловдив'),(3,'Стоян Димитров',NULL);
After
CREATE TABLE clients (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
city TEXT DEFAULT NULL
);
INSERT INTO clients (id, name, city) VALUES
(1, 'Иван Петров', 'гр. София'),
(2, 'Мария ''Мими'' Георгиева', 'Пловдив'),
(3, 'Стоян Димитров', NULL);
What is in the file
- The answer
- What goes, what stays
- Strings
- Types
- Keys and order
- COPY blocks (Postgres)
- NULL and empty
- When to refuse
- Work in this order
- Short examples
Languages
Any language. Tried in: English, Bulgarian.
License
Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.
Versions
Current version 1.0.0, updated 2026-10-08. Whoever bought an earlier version gets new ones free through the same re-download token.
v1.0.0 · 2026-10-08
First release (draft, not yet measured): converts a MySQL or PostgreSQL dump piece (CREATE TABLE, INSERT rows, COPY blocks) into SQL that SQLite, Cloudflare D1 and Turso execute, SQL only, every value unchanged, with a one-line CANNOT CONVERT refusal for a type with no exact equivalent, a COPY row with the wrong number of fields and a paste that stops in the middle.
Facts re-checked on 2026-10-08 (free, read-only, no account needed): - SQLite: a hexadecimal literal 0x... is an integer, a blob is X'...' (SQLite expression documentation, fetched); node:sqlite 3.53.0 locally: a quoted 20-digit decimal stored in a NUMERIC column becomes a floating point number (measured by the case control), so only the TEXT column type protects the digits. - PostgreSQL: COPY text format (null marker backslash-N, backslash sequences, end-of-data line, error on a wrong field count), escape strings and dollar quoting (documentation, fetched). - Cloudflare D1 limits: 100 KB per SQL statement, 100 bound parameters (limits page, fetched). - MariaDB string literal page fetched (backslash escape table, unknown escapes ignored); the MySQL manual page returned 403 to the fetch, so the MySQL rules rest on that page plus the MySQL manual from memory. Backslash-percent and backslash-underscore keeping their backslash is the MySQL rule and is NOT re-checked here. - BEGIN refused by D1 with code 7500 and foreign_keys = OFF being a no-op in a D1 migration are owner-measured on 2026-10-08 (d1-migrations note), not re-checked.
Tests: 24 cases (15 traps, 3 refusals, 6 controls), `test/control.mjs` makes zero model calls. Model results are not in yet; price and the "Sonnet got N of M" sentence in the listing are written after the baseline.
FAQ
Backticks, booleans, escapes: what does a plain conversion break?
The quiet ones. MySQL writes a quote inside a string as backslash and quote, which SQLite reads as a string that ends early. A MySQL backslash before a percent sign keeps the backslash, a plain Postgres string never decodes its backslashes, a COPY field that is only backslash and N is NULL while an empty field is an empty string, a hex literal written 0x is an integer in SQLite and not bytes, and a code like 007 must stay text. It also puts parent tables before child tables, because D1 checks foreign keys while the file runs and a dump lists tables alphabetically.
What happens when the dump has something SQLite cannot hold?
What comes back is one short line opening with CANNOT CONVERT and names the table and the column or row, for example a spatial point column or a COPY row that has two fields where the list names three. A table loaded halfway is worse than none, so the skill does not convert the fine part and skip the rest. Send the dump again without that column and the SQL follows. A value that is only unusual, such as a zero date or an empty string, is not a reason to refuse: it is carried over as written.
Does it change any value?
Only one kind, and it says so in a comment: boolean values become 0 and 1, because a text f is not false in SQLite. Dates stay the text the dump has, numbers stay as written, and a number with more digits than a floating point value can carry goes into a TEXT column quoted, so no digit is rounded.
Does it help Claude Sonnet?
Two points that bite on D1 make the difference. Twenty-four dumps were converted by both Claude models, loaded and unloaded, and each answer was executed in SQLite. On its own Sonnet kept every value, but left a BEGIN in a file meant for D1, which D1 refuses, and quietly turned a POINT column into text instead of saying it cannot be carried over: 22 right, 24 with the file. Haiku went from 21 to 23. You get plain SQL, ready for sqlite3 or wrangler d1 execute.