Resurrecting from MySQL into PostgreSQL with PGLoader
Published: 2021-03-04 13:50:20

---
title: Resurrecting from MySQL into PostgreSQL with PGLoader
published: true
date: 2021-03-02 00:00:00 UTC
tags: 
canonical_url: https://datawookie.dev/blog/2021/03/resurrecting-a-mysql-database/
---

![](https://datawookie.dev/img/headers/banner-pgloader.png)

I’ve been hosting a MySQL database on a DigitalOcean server for a few of years. The project has been on hold for a while. Entropy kicked in and the server became unreachable. Fortunately I was still able to access the server via a recovery console to export the database using `mysqldump` and download the resulting SQL dump file.

Now I want to resurrect the database locally but I also want to migrate it to PostgreSQL.

## MySQL

I’m going to spin up a local MySQL server and, for consistency, it’d be preferable to use the same version that was running on the original server.

```
mysql --version

mysql Ver 14.14 Distrib 5.7.32, for Linux (x86_64) using EditLine wrapper
```

Aha! So I need MySQL 5.7.32.

### MySQL Docker Container

Pull a Docker image which matches the version of MySQL.

```
docker pull mysql:5.7.32
```

Set a password for the MySQL `root` user.

```
MYSQL_ROOT_PASSWORD=PhwPzjP39KmMI73GTXz98Z3n
```

Then create a Docker container running MySQL.

```
docker run -d --rm --name mysql -e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD mysql:5.7.32
```

### Connecting to the MySQL Container

Connect to MySQL using the `mysql` client in the running container.

```
docker exec -it mysql mysql -uroot -p
```

Enter the password at the prompt and you’re in.

```
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.32 MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

If you have the MySQL client installed on your machine then you can also connect to MySQL directly. First you’ll need to find out the effective IP address of the container.

```
docker inspect mysql | grep IPAddress

"IPAddress": "172.17.0.2"
```

So MySQL is at 172.17.0.2 and using the default port (3306).

```
mysql -h 172.17.0.2 -P 3306 -uroot -p
```

You can also use whatever other database client you prefer. My personal preference is [DBeaver](https://dbeaver.io/).

### Create a Source Database

We’ll need a database into which the SQL dump will be loaded.

```
mysql> create database backup;
```

### Populate the Database

Now, back in the shell, load the SQL dump (in file `backup.sql`), specifying the name of the database which you’ve just created.

```
docker exec -i mysql mysql -uroot -p$MYSQL_ROOT_PASSWORD backup <backup.sql
```

Depending on the size of the dump this may take a little while to run. Ensure that you have sufficient resources to accommodate the data.

### Check Data on MySQL

Let’s take a look at the resulting database. Back into the `mysql` client.

```
mysql> use backup;
mysql> show tables;

+----------------------------------+
| Tables_in_backup |
+----------------------------------+
| athlete |
| athlete_note |
| club |
| club_pattern |
| club_type |
| event |
| license |
| province |
| race |
| race_distance |
| race_tag |
| race_tags |
| result |
| staging |
| substitution |
| substitution_contact |
+----------------------------------+
16 rows in set (0.00 sec)
```

Those are all of the tables I was expecting to see. Let’s crack one of them open.

```
mysql> select id, name from athlete limit 10;

+----------------------------------+------------------------+
| id | name |
+----------------------------------+------------------------+
| 00006f662c4e45c2a0285603865a8e3c | Derrick Majozi |
| 00009eab3ba64cefa6fae639ed413b6e | S Mbatha |
| 0000dc7e90014192baecb1ff6da3b210 | Jonathan van den Bergh |
| 0001051eebdb4d3b9c74c37d62fc8207 | Graham Swartz |
| 000143662d83435e9d5dce7944f62aa7 | Darren Moosa |
| 0001650aa646420590bb64c6b8b82bb8 | Rashaad Fortune |
| 000184586e81442db9051ca432c8ac6c | Jimmy Cochin |
| 0001b2104a6244729dd6fe4fc57e2703 | Thomas Dalais |
| 000201d90ae84595b7cd4e629890c803 | Never Mutzyula |
| 00020b43f0354d97ad1ebed8e15ae3d8 | R Mathebula |
+----------------------------------+------------------------+
10 rows in set (0.00 sec)
```

Looks good.

## PostgreSQL

The source MySQL database is ready. Now we need to prepare the target PostgreSQL database.

### PostgreSQL Docker Container

Again we’ll pull a specific image (you can choose a different version if you prefer).

```
docker pull postgres:9.6.16
```

Choose a password for the `postgres` user.

```
POSTGRES_PASSWORD=tuOtMvG219aYQJrCZcPe9wIC
```

And fire up a container.

```
docker run -d --rm --name postgres -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD postgres:9.6.16
```

### Connecting to the PostgreSQL Container

Connect to the PostgreSQL server using the `psql` client in the running container.

```
docker exec -it postgres psql -U postgres

psql (9.6.16)
Type "help" for help.

postgres=#
```

Alternatively, you can use `psql` (or any other suitable database client) installed directly on your machine. You’ll need to find out the effective IP address for the PostgreSQL server first though.

```
docker inspect postgres | grep IPAddress

"IPAddress": "172.17.0.3"
```

The PostgreSQL server is effectively at 172.17.0.3.

```
psql -h 172.17.0.3 -U postgres
```

### Create a Target Database

Now we need to create a database on the PostgreSQL server which will act as the target for the migration. Use the `createdb` utility in the running container to do this.

```
docker exec -it postgres createdb -U postgres backup
```

Both the source and target are now in place. We’re ready to migrate.

## Migration

[PGLoader](https://github.com/dimitri/pgloader) is a tool for moving data to PostgreSQL developed by [Dimitri Fontaine](https://github.com/dimitri), making the process both simple and robust.

### Installing PGLoader

Either install it directly.

```
sudo apt update
sudo apt install pgloader
```

Or use Docker.

```
docker pull dimitri/pgloader
```

Check on the version.

```
pgloader --version

pgloader version "3.6.1"
compiled with SBCL 1.4.15.debian
```

### Migrate Data from MySQL to PostgreSQL

We need to execute `pgloader`, providing the details of the source and target databases. The credentials, IP address and database for the source and target are specified by detailed `mysql` and `pgsql` URLs.

```
pgloader mysql://root:$MYSQL_ROOT_PASSWORD@172.17.0.2/backup \
         pgsql://postgres:$POSTGRES_PASSWORD@172.17.0.3/backup
```

When `pgloader` finishes, it presents a summary table of the migration process. Check the `errors` column to ensure that there were no problems.

### Check Data on PostgreSQL

Finally let’s verify that the migrated data are intact. Connect to the PostgreSQL server.

```
psql -h 172.17.0.3 -U postgres
```

Select the `backup` database and list its tables.

```
postgres=# \c backup
backup=# \dt

                      List of relations
 Schema | Name | Type | Owner   
--------+----------------------------------+-------+----------
 backup | athlete | table | postgres
 backup | athlete_note | table | postgres
 backup | club | table | postgres
 backup | club_pattern | table | postgres
 backup | club_type | table | postgres
 backup | event | table | postgres
 backup | license | table | postgres
 backup | province | table | postgres
 backup | race | table | postgres
 backup | race_distance | table | postgres
 backup | race_tag | table | postgres
 backup | race_tags | table | postgres
 backup | result | table | postgres
 backup | staging | table | postgres
 backup | substitution | table | postgres
 backup | substitution_contact | table | postgres
(16 rows)
```

Matches what we had on MySQL. What about the contents of a table?

```
backup=# select id, name from athlete limit 10;

                id | name          
----------------------------------+------------------------
 00006f662c4e45c2a0285603865a8e3c | Derrick Majozi
 00009eab3ba64cefa6fae639ed413b6e | S Mbatha
 0000dc7e90014192baecb1ff6da3b210 | Jonathan van den Bergh
 0001051eebdb4d3b9c74c37d62fc8207 | Graham Swartz
 000143662d83435e9d5dce7944f62aa7 | Darren Moosa
 0001650aa646420590bb64c6b8b82bb8 | Rashaad Fortune
 000184586e81442db9051ca432c8ac6c | Jimmy Cochin
 0001b2104a6244729dd6fe4fc57e2703 | Thomas Dalais
 000201d90ae84595b7cd4e629890c803 | Never Mutzyula
 00020b43f0354d97ad1ebed8e15ae3d8 | R Mathebula
(10 rows)
```

Looks like the data are intact. Perfect! That was a remarkably painless process thanks to an amazing set of Open Source tools.

PGLoader is not limited to migrating from MySQL. It handles CSV and fixed width files, MaxMind Geolite, dBase and SQLite too. A very versatile tool indeed. Learn more in the [excellent documentation](https://pgloader.readthedocs.io/en/latest/index.html).