Skip to content

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:

SELECT location FROM customers;
-- 1 | Robert California | 123 Sunny St, AZ | (34.41, -111.68)

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.

Last updated · 378 distilled / 1,213 read