| Keys and identity |
AUTO_INCREMENT |
PostgreSQL generates keys from a sequence rather than from a column attribute. |
The primary key is created as BIGSERIAL, so key generation moves to a PostgreSQL sequence that the table owns. |
| Numbers |
| Unsigned integers |
PostgreSQL has no unsigned types, so the upper half of the range has nowhere to go. |
Each type widens to one that still holds the values: TINYINT UNSIGNED → SMALLINT, BIGINT UNSIGNED → NUMERIC(20). |
TINYINT(1) |
MySQL uses the same declaration for a true boolean and for a small integer code, and only your data says which it is. |
Converted to boolean automatically. Override it to smallint in the review when the column stores codes rather than 0/1. |
| Date and time |
TIME |
A MySQL TIME is a signed duration that can run past 24 hours; a PostgreSQL time is a clock reading and cannot. |
Converted to interval, which keeps durations that a clock type would reject. |
DATETIME, YEAR |
The names exist on both sides but do not always mean the same thing, and PostgreSQL has no year type at all. |
DATETIME and TIMESTAMP both become timestamp, and YEAR becomes smallint. |
| Text |
ENUM and SET |
MySQL declares both inline on the column; PostgreSQL enum types are separate objects and it has no equivalent of SET at all. |
The values come across as text - ENUM becomes VARCHAR and SET becomes TEXT. A native PostgreSQL enum type is not created for you. |
JSON |
Both engines have a JSON type, but PostgreSQL has two - json keeps the document as written, jsonb parses and indexes it. |
Arrives as PostgreSQL json. Switch the column to jsonb afterwards if you want the indexing and the containment operators. |
| Structure |
| Identifier names |
PostgreSQL folds unquoted names to lower case and stops at 63 bytes, so mixed-case or long MySQL names do not arrive unchanged. |
Every destination name is an editable field in the review, and the header counts how many you have renamed before the run. |