sluice

BIGINT UNSIGNED overflows both bigint and int64

A MySQL BIGINT UNSIGNED reaches 2⁶⁴−1, past Postgres bigint's 2⁶³−1 — and past Go's int64, so above that boundary the driver hands the value back as a uint64 a []byte/string-only decoder can't route. The type mismatch is known; the driver-representation switch is the sharp edge.

Landed in sluice · 2026-06-08 · MySQL & Vitess

Observed — migrating a MySQL BIGINT UNSIGNED column holding values above 263−1 to Postgres. A value-fidelity finding from the test rig.

What happened #

A MySQL BIGINT UNSIGNED column carrying values above 2^63-1 had no working migration path to Postgres — and, worse, the loud error's recommended recovery didn't function either. This never silently lost data (it failed loudly), but it blocked a common migration and then lied about how to unblock it.

Why (the mechanism) #

Three boundaries stack up at the same value:

What sluice does about it #

Add uint64/int64 cases to the decimal and string decoders that carry the exact decimal text via strconv.FormatUint/FormatInt, so a BIGINT UNSIGNED migrates as an exact Postgres numeric or text value — no precision lost. And correct the remediation hint at every site it's surfaced (the notice, the schema-preview output, the doc comments) to the token that actually parses, with enough digits for the value.

The transferable lesson #

An unsigned 64-bit integer is a triple boundary: it overflows the target's signed bigint, it overflows the language's int64, and at that second overflow the driver quietly changes the Go type it hands you (uint64, not the []byte/string your decoder was built for). A migration that maps the type but never sees the over-int64max representation fails on exactly the values that justified making the column unsigned. And the meta-lesson: a loud-failure remedy is code too. If the error names a flag token that doesn't exist or a type too narrow for the value, "we refuse loudly and tell you the fix" degrades into "we refuse loudly and mislead you" — so test your remediation strings through the real parser, the same way you'd test a feature. (This is one of a family of integer-boundary hazards where the number outgrows the pipe carrying it.)

Primary sources #