CONCEPT Cited by 1 source
MySQL spatial binary (WKB) vs Postgres Point¶
MySQL and Postgres both declare a POINT column type.
The declaration is identical, but the wire
representation at read time is not. MySQL returns a
Well-Known Binary (WKB) hex blob; Postgres returns a
human-readable (x, y) coordinate pair.
This is the canonical example of a representational equivalence break — the type declaration audit is clean, and the migration still fails at read time because application code expected a string.
The behaviour gap¶
In Postgres:
In MySQL, the same column + the same INSERT:
SELECT location FROM customers;
-- 1 | Robert California | 123 Sunny St, AZ | 0x00000000010100000027DFC4AEA43441403416326E6AEB5BC0
(Source: sources/2026-04-21-planetscale-migrating-from-postgres-to-mysql.)
To recover human-readable coordinates in MySQL, wrap the
column in ST_asText():
SELECT id, full_name, address, ST_asText(location) FROM customers;
-- 1 | Robert California | 123 Sunny St, AZ | POINT(34.41, -111.68)
MySQL internally stores spatial data in one of several encodings; the default representation returned to the client is WKB. Postgres's geometric types return text by default.
Migration options¶
Two paths, with different trade-offs:
Option A — keep POINT, wrap reads in ST_asText().
- Application-side change: every read path touching the
column needs to be rewritten.
- ORM layer typically has a point-specific adapter that
handles the unwrap.
- Keeps MySQL's spatial index + function family available
(ST_Distance, ST_Within, ST_Intersects, etc.).
Option B — split into location_latitude DECIMAL +
location_longitude DECIMAL.
- No application read changes — just two numeric columns.
- Loses MySQL's spatial functions entirely; any radius
queries, geofences, etc. become application-side math.
- Better option if the application never used
Postgres's spatial functions either.
Why this matters for migration planning¶
This is the subtle case in a migration audit: a column that
passes the column-type declarational audit (both engines
have POINT) and fails at read time because the
representation is different. A cross-engine audit has to
include a representation check, not just a type-name check.