Code & Engineering

Postgres or MySQL SQL to SQLite and D1

Converts PostgreSQL or MySQL schema statements, migrations and queries into SQLite that Cloudflare D1 and Turso run, so the converted version behaves like the original and not only parses. Knows the conversions that run without an error and then go wrong - a key written as BIGSERIAL or INT AUTO_INCREMENT that SQLite stores as NULL, a custom enum type or an inline enum that stops checking anything, a default now() or gen_random_uuid() that fails on the first insert, a unique email that was case-insensitive in MySQL, ON UPDATE CURRENT_TIMESTAMP that needs a trigger, a foreign key added later with ALTER TABLE, a schema prefix, DISTINCT ON, ILIKE, double-colon casts, date_trunc, interval arithmetic, DATEDIFF, GROUP_CONCAT with SEPARATOR, MySQL division that keeps the fraction, ON DUPLICATE KEY UPDATE, JSON operators. Answers with the SQLite SQL only, and says CANNOT CONVERT when part of the input has no equivalent in SQLite (a procedural function, row level security, notifications, a stored procedure) instead of dropping it. Use when asked to port, convert or migrate Postgres or MySQL SQL to SQLite, D1 or Turso.

Postgres or MySQL SQL to SQLite and D1 is a tested SKILL.md that converts PostgreSQL or MySQL schema statements, migrations and queries into SQLite that Cloudflare D1 and Turso run, so the converted version behaves like the original and not only parses; an agent buys it once for $0.01 over x402.

Tested 2026-10-09No code, no hidden instructionsv1.0.0 · 13.8 KB · perpetual license

Not for

Dumps of INSERT rows or COPY blocks (sql-dump-to-sqlite), query tuning, schema design or moving live data. Procedural functions, row level security and notifications get a CANNOT CONVERT line, not SQL. Some D1 rules are owner-measured on 2026-10-08.

Tested, honestly

Tested 2026-10-09 with a strong and a weak model.

With and without the skill

Results with and without the skill, for Sonnet and Haiku
SonnetHaiku
withwithoutwithwithout
Conversions right when run (24 cases)24/2424/2423/2421/24

Same request on both sides, a fence removed first. Sonnet without the skill was already right on all 24, so the skill adds no measured gain for it. Haiku without the skill missed three: it turned MySQL division into SQLite integer division, so 1001 / 2 lost its half, and twice it wrote the table next to the sentence saying a function or trigger could not be converted, while the request asked for no SQL in that case.

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, each reply run in SQLite: auto-number keys, an enum turned into a CHECK, a case-insensitive unique email, a pg_dump sequence with a foreign key added later, uuid and jsonb defaults, ILIKE and intervals, date_trunc and extract, MySQL division and DIV, GROUP_CONCAT with a separator, ON DUPLICATE KEY, DISTINCT ON, JSON operators, DATEDIFF and DATE_FORMAT, four refusals with no SQL beside them, and a planted note inside already valid SQLite ignored.
HaikuWeak model, claude-haiku-5-5
Right on 23 of 24, each reply run in SQLite, but on the file that was already SQLite it ignored the planted note and then added a sentence about it after the SQL, so the migration would not run.

Full test summary

Example

Our own test text, before and after the skill ran. Excerpts only.

English · claude-sonnet-5-5

Before

SQL to convert: CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() );

After

CREATE TABLE users ( id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP );

Bulgarian · claude-sonnet-5-5

Before

SQL to convert: -- Затваря месеца: маркира неплатените фактури като просрочени DELIMITER // CREATE PROCEDURE close_month() BEGIN DECLARE done INT DEFAULT 0; DECLARE inv INT; DECLARE cur CURSOR FOR SELECT id FROM invoices WHERE paid = 0; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO inv; IF done THEN LEAVE read_loop; END IF;…

After

CANNOT CONVERT: процедурата close_month() използва курсор, променливи и цикъл (DECLARE, OPEN, FETCH, LOOP), което SQLite не може да изрази.

What is in the file

  • The answer
  • When the answer is CANNOT CONVERT
  • Types and keys
  • Queries and write statements
  • 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-09. Whoever bought an earlier version gets new ones free through the same re-download token.

  1. v1.0.0 · 2026-10-09

    First draft, self-designed from the batch 4 table row (rank 12): the brief for this skill had not been written when the writer started. Converts Postgres or MySQL schema statements, migrations, queries and write statements to SQLite (D1, Turso); the whole answer is CANNOT CONVERT when any statement has no equivalent (procedural functions and procedures, row level security, notifications), never a half conversion. Fourteen silent-failure traps (BIGSERIAL and INT AUTO_INCREMENT keys, enum types, now() and uuid defaults, case-insensitive unique, ON UPDATE trigger, pg_dump sequences and late foreign keys, ILIKE and interval, date_trunc, MySQL division, GROUP_CONCAT, ON DUPLICATE KEY, DISTINCT ON, JSON operators, DATEDIFF), four refusals, six controls. Facts probed locally on 2026-10-08 (notes/facts-2026-10-08.md). Price (30000 micro) is a placeholder until the baseline run; class B.

FAQ

Which SQL comes back, and in what shape?

Only SQLite SQL: converted tables, indexes and triggers, or your query or write statement in the wrapper you ask for. A short line comment marks anything removed or changed in meaning. When a single statement cannot be expressed in SQLite, the whole reply is one line starting CANNOT CONVERT that names the statement and the reason, with no partial SQL beside it.

How can I trust that the converted SQL behaves like the original?

Each test pastes Postgres or MySQL SQL and then executes the reply in SQLite. A schema is built in an empty database, rows are inserted, rows that the original would refuse must be refused, and what is stored is compared. A query runs on small tables and the rows it returns must equal the rows the original would have returned on the same data. Many spellings of a correct conversion pass, such as a window function or a correlated subselect for DISTINCT ON, while a conversion that only parses fails, and so does one that keeps a word SQLite does not know. Refusal cases must contain no SQL at all.

Which conversions fail without an error message?

An auto-number key that SQLite stores as NULL, a custom enum type that checks nothing, a unique email that was case-insensitive in MySQL, a column that refreshed itself and now needs a trigger, a foreign key added later with ALTER TABLE, MySQL division that kept the fraction, and an upsert that replaces the row instead of adding to it.

Does it help Claude Sonnet?

One cent, because on our set Sonnet gained nothing. Twenty-four pieces of Postgres and MySQL went to both Claude models, guided by the file and bare, and every reply was run in SQLite. Sonnet got all 24 right unaided. Haiku moved from 21 to 23: bare, it let MySQL division lose its fraction and twice put a table next to its refusal. Our request was long and named the format and the refusal; a shorter request was not tested. Comments in your SQL stay in their own language.

Share

Read this page as Markdown: /skills/sql-dialect-to-sqlite.md.

  • SQL Dump to SQLite: MySQL and Postgres Rows, Unchanged

    Code & Engineering

    SKILL.md · v1.0.0 · 12.4 KB

    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.

    $0.03once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026

  • SQLite Query From a Question for D1

    Data & Analysis

    SKILL.md · v1.0.0 · 12.3 KB

    Writes the SQL query for SQLite or Cloudflare D1 that answers a question about the data, from the question and the table definitions you paste, so it returns the right rows and not only rows that look right. Knows where plain-language questions turn into wrong results on SQLite - NOT IN against a column that holds NULL, integer division in percentages and money, COUNT of a column that holds NULLs, a LEFT JOIN that a WHERE turns back into an inner join, two one-to-many joins that multiply each other, ties at a LIMIT, NULLs that sort first, dates stored as text in a format that does not sort, a month end that cuts off the last day's timestamps, pairs listed twice, "both A and B" and "top N of each group". Answers with the one statement and nothing else, and says CANNOT ANSWER when the tables do not hold what the question needs, instead of inventing a column. Use when asked to write, check or fix a SELECT for SQLite, D1 or Turso from a question, a report request or a schema.

    $0.03once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026

  • D1 Migration Writer: SQLite Migrations That Apply

    Code & Engineering

    SKILL.md · v1.0.1 · 9.8 KB

    Writes a SQL migration for Cloudflare D1 or SQLite from the current schema and the change wanted, so it applies on the first try and leaves the rows already in the database right. Knows what D1 refuses or breaks on, measured on a real D1 database - BEGIN and COMMIT in the file, foreign keys that cannot be switched off (a rebuild defers them, keeps the table name, and never closes with defer_foreign_keys = false, which on D1 silently skips the check), NOT NULL columns without a default, REFERENCES columns with a default, indexed columns dropped before their index. Backfills a new status or marker column so old rows are not picked up as pending work, fills a new counter from the existing rows, orders index columns for the query that needs them, and keeps upserts working against a partial unique index. Use when asked to write, check or fix a D1, SQLite or Turso migration, add or change a column, add an index, make a column unique or required, or rebuild a table.

    $0.07once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026