A fast, type-safe MSSQL to MySQL database migration tool written in Rust.
Features • Quick Start • Installation • Configuration • Usage • Type Mappings
$ db-migrator --format --constraints --verbose
Migrating table: user_accounts
Table user_accounts migrated, rows: 12840, took: 1.23s
Migrating table: orders
Table orders migrated, rows: 58201, took: 3.47s
Migration finished, total rows: 71041, total time took: 4.82s
- Schema + data migration — transfers table structures, column types, constraints, and all rows
- 29 MSSQL types mapped — built-in defaults for every type, zero config needed
- Concurrent — tables migrate in parallel with configurable parallelism
- Batch inserts — rows streamed and batched to respect MySQL
max_allowed_packet - Constraints — primary keys and foreign keys carried over automatically
- Snake case — optionally convert
PascalCasenames tosnake_case - Customizable — override any type mapping with a simple config file
# 1. Configure your databases
cp config.example.toml config.toml # edit connection details
# 2. Run
cargo run --releaseThat's it. All type mappings are built in — no extra config needed unless you want to customize them.
Download the latest binary for your platform from Releases.
git clone https://github.com/bitalizer/db-migrator.git
cd db-migrator
cargo build --releaseRequires Rust 1.85+ (edition 2024).
Create config.toml from the example:
[mssql_database]
host = "localhost" # optional, defaults shown
port = 1433
username = "db_user"
password = "db_pass"
database = "source_db"
[mysql_database]
host = "localhost" # optional, defaults shown
port = 3306
username = "db_user"
password = "db_pass"
database = "target_db"
[settings]
max_packet_bytes = 1048576
whitelisted_tables = ["Users", "Orders", "Products"]Character sets: text is decoded from SQL Server to UTF-8 during extraction, and created tables inherit the target database's default charset. Create the target database with
utf8mb4(CREATE DATABASE output CHARACTER SET utf8mb4) so all characters survive. Collation semantics are not translated: data from a case-sensitive SQL Server column can collide on MySQL's default case-insensitive unique indexes.
db-migrator [FLAGS] [OPTIONS]
| Flag | Description |
|---|---|
-c, --constraints |
Create primary key and foreign key constraints |
-d, --drop |
Drop target tables before migration |
-f, --format |
Convert table and column names to snake_case |
-v, --verbose |
Verbose logging |
-q, --quiet |
Suppress output |
| Option | Description |
|---|---|
--source <URL> |
Source database URL, mssql://user:pass@host:1433/database |
--target <URL> |
Target database URL, mysql://user:pass@host:3306/database |
--tables <T1,T2> |
Comma-separated tables to migrate (CLI mode) |
-p, --parallelism <N> |
Max concurrent table migrations (default: CPU cores) |
--max-packet-bytes <N> |
Max INSERT batch size in bytes (default: 1048576; overrides config.toml) |
Passing any of --source/--target/--tables skips config.toml entirely, so a
migration is a single copy-pasteable command. All three are required in this mode;
the port defaults to the engine port (1433/3306) when omitted from a URL.
db-migrator \
--source "mssql://sa:pass@10.0.0.1:1433/input" \
--target "mysql://root:pass@localhost:3306/output" \
--tables Users,Orders,Products \
--format --constraintsCredentials passed on the command line are visible in shell history and process lists. Percent-encode special characters in passwords (
p@ssbecomesp%40ss).mappings.tomltype overrides apply in both modes.
# Basic migration
db-migrator
# Full migration with snake_case and constraints
db-migrator --format --constraints
# Verbose with custom parallelism
db-migrator -v -p 4
# Drop and recreate all tables
db-migrator --drop --constraints --formatAll 29 MSSQL types are mapped by default. No configuration required.
View full type mapping table
| MSSQL | MySQL | Notes |
|---|---|---|
bit |
tinyint(1) |
Boolean equivalent |
tinyint |
tinyint unsigned |
|
smallint |
smallint |
|
int |
int |
|
bigint |
bigint |
|
decimal / numeric |
decimal |
Precision and scale carried over |
money |
decimal(19, 4) |
|
smallmoney |
decimal(10, 2) |
|
float |
float |
|
real |
real |
|
char / nchar |
char |
Length carried from source |
varchar |
varchar |
Length carried, default 255 |
nvarchar |
longtext |
Unicode safe |
text / ntext |
longtext |
|
binary |
binary |
Length carried from source |
varbinary / image |
longblob |
|
date |
date |
|
datetime / datetime2 / smalldatetime |
datetime |
|
datetimeoffset |
datetime |
Offset stripped |
time |
time |
|
uniqueidentifier |
char(36) |
UUID as string |
timestamp |
bigint unsigned |
Row version counter, not a time value |
xml |
longtext |
To override any default mapping, create a mappings.toml file in the project root:
[mappings]
nvarchar = "varchar(500)"
money = "decimal(10, 2)"
float = "float(53)"
xml = "longtext"Supports three formats: "type", "type(length)", and "type(precision, scale)",
each optionally followed by the modifiers unsigned and/or zerofill
(numeric types only), e.g. "int unsigned" or "decimal(19, 4) unsigned".
Type-wide overrides apply to every column of that source type. When only
specific columns need different output (e.g. a legacy schema expecting
int unsigned IDs), scope the override to "Table.Column" source names:
[mappings.columns]
"Orders.ID" = "int unsigned"
"Orders.Notes" = "text"Column overrides take precedence over type-wide overrides, which take precedence over the built-in defaults. Names are matched case-insensitively against the source (SQL Server) table and column names.
- Source: MSSQL Server
- Target: MySQL 5.7+ / MariaDB 10.3+
- Build: Rust 1.85+
Dependencies
| Crate | Purpose |
|---|---|
| tokio | Async runtime |
| tiberius | MSSQL driver |
| sqlx | MySQL driver |
| bb8 | Connection pooling |
| clap | CLI parsing |
| anyhow | Error handling |
| chrono | Date/time |
| toml | Config parsing |
See LICENSE for details.