Migration guides
MariaDB to SQLite migration guide
Convert a MariaDB dump into a SQLite script with the same tables, keys and data: how to dump, convert, load, and every rule the converter applies.
IntoSQL reads a MariaDB dump and writes one SQLite script with the same tables, columns, keys, indexes, foreign keys, defaults, counters, sequences, comments and rows. It reads the dump itself, without a database server, and lists in the report every value SQLite can't hold as it is and every object it doesn't carry. The script loads on SQLite 3.31 and later.
Convert in the browser: MariaDB to SQLite opens the converter set up for this pair.
Dump for the best result
Any dump from mariadb-dump, mysqldump, phpMyAdmin or Adminer from MariaDB 10.3 on works, as .sql or .sql.gz. These options keep more of your data:
mariadb-dump --default-character-set=utf8mb4 --hex-blob \
--routines --triggers --events --single-transaction \
shop | gzip > shop.sql.gz--default-character-set=utf8mb4keeps emoji and other 4-byte characters. A dump made withutf8mb3has already lost them, and the report says so (dump_lost_four_byte_chars).--hex-blobwrites binary columns andVECTORvalues as hexadecimal, which survives copying between systems.--routines --triggers --eventsadd those objects to the dump, so the report can list them by name.
A dump made with --no-create-info still converts: the types are read from the values, and the report says the schema was missing (no_schema). MariaDB's JSON columns, which the dump writes as longtext with a json_valid check, are read as JSON again.
Convert
In the browser, open MariaDB to SQLite or the converter on the home page, drop the dump and choose SQLite. It reads the file from your disk in pieces, twice, and writes the script as it goes, so the dump is never uploaded and its size isn't limited by memory. The desktop app does the same with the file on disk.
With the command line:
intosql inspect shop.sql.gz
intosql convert shop.sql.gz --to sqlite --output shop.sqlite.sql --report shop.report.jsoninspect lists the tables, their rows and what won't be converted, without writing anything. convert writes the script and, with --report, the report as JSON.
Through the API, a dump converts like any other file: open a conversion with its name ending in .sql or .sql.gz and "output": {"dialect": "sqlite"}.
Load into SQLite
sqlite3 -bail shop.sqlite < shop.sqlite.sqlThe script creates each table with its keys, copies the rows and adds indexes, counters and triggers, all in one transaction with syncing off, so it runs at disk speed and a load that stops leaves no partial tables. Foreign keys are declared in each table and checked once the rows are in: the script ends with PRAGMA foreign_key_check, which lists any row that points to a missing row instead of failing the load. Turn foreign keys on in each connection that writes (PRAGMA foreign_keys = ON), as SQLite expects.
Or load it straight in
intosql load and the desktop app load the dump into SQLite without a script, with the same tables, keys and indexes the script creates:
intosql load shop.sql.gz --into shop.dbIt stops before writing anything when one of the dump's tables already exists. It loads in one transaction, and a file the load created is removed if it fails. Loading into SQLite is free. See Loading.
What isn't converted
Views, triggers, stored procedures, functions and events are written in MariaDB's own language. They are listed in the report by kind and name, never dropped silently, so you can recreate them in SQLite.
Data types
MariaDB to SQLite data types lists each type that changes in the script: what it becomes, why, and the report line.
Every other rule
Generated from the engine, where each rule is decided.
| In MariaDB | In SQLite | Why | Report line |
|---|---|---|---|
| NOT NULL columns with values that become NULL | NOT NULL dropped | A column whose values had to become NULL can't keep NOT NULL, or the load would stop. | not_null_relaxed |
| text decoded twice (ñ) | copied as it is | The dump holds exactly these characters, so they are copied; the report points them out. | mojibake_suspected |
| ON UPDATE CURRENT_TIMESTAMP | an AFTER UPDATE trigger | The trigger sets the column to the current time when a row changes and the update didn't set it, as MySQL does. | on_update_trigger |
| generated columns | GENERATED ALWAYS AS, VIRTUAL or STORED as in the source | SQLite 3.31 and later compute generated columns the same two ways. | None |
| generated columns with functions SQLite lacks | a plain column | A formula that can't be written for SQLite leaves a plain column and a report line. | generated_not_translated |
| INVISIBLE columns | regular columns | SQLite has no invisible columns. | invisible_column |
| CHECK constraints | translated, inline in CREATE TABLE | SQLite only takes checks in CREATE TABLE. | None |
| CHECK constraints with functions SQLite lacks | not created | A check that can't be written for SQLite is left out and reported. | check_skipped |
| CHECK ... NOT ENFORCED | not created | MySQL keeps these checks without applying them, so the rows may break them. | check_not_enforced |
| defaults with functions SQLite lacks | no default | A default that can't be written for SQLite is left out and reported. | default_not_translated |
| keys and indexes on a prefix, c(20) | an index on substr(c, 1, 20) | SQLite indexes expressions, so the same prefix is indexed and a unique prefix stays unique. | prefix_unique |
| FULLTEXT indexes | not created | SQLite searches text with separate FTS5 tables, which need their own queries. | fulltext_not_created |
| SPATIAL indexes | not created | SQLite has no spatial index on a column. | spatial_index_not_created |
| functional indexes SQLite can't write | not created | An index on an expression that can't be written for SQLite is left out and reported. | index_skipped |
| foreign keys | in CREATE TABLE, loaded unchecked, then listed by PRAGMA foreign_key_check | SQLite takes foreign keys only in CREATE TABLE and checks them only with PRAGMA foreign_keys on. The rows load unchecked, as from MySQL, and the script ends listing any row without a parent. | None |
| foreign keys to columns without a unique key | not created | SQLite needs a unique key on the referenced columns; InnoDB doesn't. | foreign_key_skipped |
| table, index and column names | as written; renamed when they differ only in case or an index name repeats | SQLite names ignore case, and index names share one namespace per database. | identifier_renamed |
| PARTITION BY | one table | The rows are the same. | partitions_dropped |
| WITH SYSTEM VERSIONING (MariaDB) | a table with the current rows | A dump holds the current rows only. | system_versioning_dropped |
| MyISAM, MEMORY and other engines | a regular table | SQLite has one table engine. | engine_dropped |
| dumps of several databases | one file, each table named after its database first | A SQLite file is one database, so the database name keeps tables with the same name apart. | databases_merged |
| views, triggers, procedures, functions and events | listed in the report | Their code is written in MySQL's language, so they are listed for you to recreate. | not_converted_objects |
| dumps without CREATE TABLE | types read from the values | A dump made with --no-create-info only has rows, so the types come from the values. | no_schema |
| _ci collations | exact comparison | SQLite compares text exactly unless told otherwise, and its NOCASE only folds ASCII letters. | case_insensitive_collation |
| SET NAMES utf8 from utf8mb4 tables | converted, with a warning | MySQL 5.x mysqldump writes 3-byte utf8 by default and replaces every emoji with a question mark before any converter sees the file. | dump_lost_four_byte_chars |
| dumps without SET NAMES whose text isn't UTF-8 | read as Latin-1 (Windows-1252) | MySQL's latin1 is Windows-1252, the usual encoding of such dumps. | charset_guessed |
| sjis, cp932, gbk, big5 dumps | read as UTF-8, with a warning | These encodings are not decoded yet. | unsupported_charset |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | evaluated | Some dump tools write values as these calls; they are read as the values they produce, with geographic WKT turned to longitude first. | None |
| other expressions in rows | NULL | A converter that doesn't run a database can't evaluate them. | expression_value |
| DEFAULT in VALUES | the column's default value | A literal default is copied into the row. | default_keyword_filled |
| DEFAULT in VALUES without a literal default | NULL | There is no value to copy. | default_keyword_null |
| REPLACE and INSERT IGNORE | plain rows | A dump has one row per key, so the rows are the same. | replace_as_insert |
| CREATE SEQUENCE (MariaDB) | not created | SQLite has no sequences. | sequence_not_converted |