Skip to content

Type Mapping

pgferry defaults to conservative, mostly lossless mappings. The main default exception is JSON, which becomes PostgreSQL jsonb because that is usually the more useful target type.

MySQL / MariaDB typeDefault PG typeAlternate mappingFlag
tinyint(1)smallintbooleantinyint1_as_boolean
tinyintsmallint
smallintsmallint or integer if unsigned
mediumintinteger
intinteger or bigint if unsigned
bigintbigint or numeric(20) if unsigned
floatreal
doubledouble precision
decimal(p,s)numeric(p,s)
varchar(n) / char(n)varchar(n)textvarchar_as_text
text familytext
jsonjsonbjsonjson_as_jsonb = false
enum(...)text with CHECKplain text, native enumenum_mode
set(...)texttext[], text[] + CHECKset_mode
timestamptimestamptz
datetimetimestamptimestamptzdatetime_as_timestamptz
datedate
timetimetext, intervaltime_mode
bit(n)byteabit(n), varbitbit_mode
binary(16)byteauuidbinary16_as_uuid
uuiduuidMariaDB native type
char(36) / varchar(36)varchar(36)uuidstring_uuid_as_uuid
binary / varbinary / blob familybytea
Spatial typesunsupportedgeometry, bytea, text[postgis].enabled, spatial_mode

Unknown MySQL and MariaDB types error by default. Set unknown_as_text = true to coerce them to text instead.

  • widen_unsigned_integers = true preserves MySQL-family unsigned ranges by widening the PostgreSQL target type.
  • add_unsigned_checks = true adds PostgreSQL CHECK constraints after load.
  • binary16_uuid_mode = "mysql_uuid_to_bin_swap" reverses MySQL-family UUID_TO_BIN(uuid, 1) byte swaps.
  • zero_date_mode = "null" converts MySQL-family zero dates to NULL. error aborts instead.
  • collation_mode = "auto" emits PostgreSQL COLLATE clauses when pgferry can map the source collation.
  • ci_as_citext = true maps _ci text columns to PostgreSQL citext unless a collation_map entry overrides that choice.
  • MariaDB JSON aliases detected through JSON_VALID(...) checks preserve JSON semantics instead of degrading to text.

SQLite uses type affinity, so pgferry keeps the mapping conservative.

SQLite typePG typeNotes
INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINTbigintSQLite integers are up to 64-bit.
REAL, DOUBLE, FLOATdouble precision
TEXT, VARCHAR(N), CHAR(N), CLOBtextSQLite does not enforce declared length.
BLOBbytea
NUMERICnumeric
NUMERIC(P,S) / DECIMAL(P,S)numeric(P,S)Precision and scale are preserved when declared.
BOOLEANboolean
DATETIME, TIMESTAMPtimestampWith datetime_as_timestamptz = true, maps to timestamptz. plan emits a temporal warning: SQLite values have no timezone, so PostgreSQL session timezone is load-bearing.
DATEdate
JSONjsonbjson when json_as_jsonb = false

SQLite accepts datetime_as_timestamptz for DATETIME / TIMESTAMP. Other MySQL-family-only and MSSQL-only type-mapping options are rejected during config validation.

MSSQL typeDefault PG typeAlternate mappingFlag
intinteger
bigintbigint
smallintsmallint
tinyintsmallint
bitboolean
decimal(p,s) / numeric(p,s)numeric(p,s)
floatdouble precision
realreal
moneynumeric(19,4)textmoney_as_numeric = false
smallmoneynumeric(10,4)textmoney_as_numeric = false
char(n)char(n)
varchar(n)varchar(n)
varchar(max)text
nchar(n) / nvarchar(n)char(n) / varchar(n)textnvarchar_as_text
nvarchar(max)text
text / ntexttext
binary / varbinary / imagebytea
datedate
timetime
datetime, datetime2, smalldatetimetimestamptimestamptzdatetime_as_timestamptz
datetimeoffsettimestamptz
uniqueidentifieruuid
xmlxmltextxml_as_text
jsonjsonbjsonjson_as_jsonb = false
sql_variant, hierarchyidtext
geography, geometryunsupportedbytea, textspatial_mode
rowversion / timestampbyteaMSSQL timestamp is not a datetime type.
  • datetime2 and time carry fractional-second scale from sys.columns; pgferry emits PostgreSQL timestamp(n), timestamptz(n), or time(n) when scale > 0, with n clamped to 6 (SQL Server allows scale 7).
  • source_snapshot_mode = "single_tx" uses SNAPSHOT isolation and requires ALLOW_SNAPSHOT_ISOLATION ON on the source database.
  • uniqueidentifier values are byte-reordered into standard UUID order during copy.
  • nvarchar and nchar lengths are divided by two because MSSQL reports byte length, not character length.
  • (max) types map to PostgreSQL text or bytea.
  • Computed columns are copied as materialized values and reported for manual semantic recreation.
[type_mapping]
json_as_jsonb = true
sanitize_json_null_bytes = true
unknown_as_text = false
datetime_as_timestamptz = false
spatial_mode = "off"
ModeBehavior
checkStore as text and add a CHECK constraint with the allowed values.
textStore as unconstrained text.
nativeCreate a PostgreSQL enum type and reuse it for identical value sets.

Use check when MySQL or MariaDB enum ordering matters and you do not want PostgreSQL native enum ordering semantics.

ModeBehavior
textKeep the original comma-separated string.
text_arraySplit the set into text[].
text_array_checkStore as text[] and add a CHECK constraint limiting allowed members.
ChoiceUse when
[postgis].enabled = trueYou want real geometry columns and spatial index recreation for MySQL spatial data. MariaDB is intentionally excluded from this path.
spatial_mode = "wkb_bytea"You want raw binary preservation without PostGIS.
spatial_mode = "wkt_text"You want readable text output without PostGIS.
spatial_mode = "off"You prefer pgferry to stop and report spatial columns instead of guessing.
  • Leave most type mapping at defaults for the first rehearsal.
  • Turn on semantic remaps like tinyint1_as_boolean, binary16_as_uuid, or string_uuid_as_uuid only when you know the source data actually follows that convention.
  • Prefer unknown_as_text = false for production rehearsals so pgferry surfaces unsupported types early instead of hiding them behind generic text columns.