Skip to content

Add varbinary-to-inet transform for MySQL VARBINARY IP addresses (#1757) - #1760

Merged
dimitri merged 4 commits into
mainfrom
fix/mysql-varbinary-to-inet
Jul 15, 2026
Merged

dimitri merged 4 commits into
mainfrom
fix/mysql-varbinary-to-inet

Conversation

@dimitri

@dimitri dimitri commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Closes #1757.

Problem

MySQL stores IPv4 addresses as VARBINARY(4) and IPv6 as VARBINARY(16) raw binary. Without a transform pgloader v4 emits the hex-escaped form (e.g. X7fffffff) which PostgreSQL rejects when the target column type is inet.

Solution

Add a varbinary-to-inet transform function to both v3 and v4.

v4 (Clojure — clojure/src/pgloader/transforms.clj)

Accepts the "X" + lowercase-hex string that convert-mysql-value already produces for byte[] columns, strips the prefix, parses the bytes into a byte[], and calls java.net.InetAddress/getByAddress — which handles both 4-byte (IPv4) and 16-byte (IPv6) natively. Returns nil for null or empty input.

v3 (Common Lisp — src/utils/transforms.lisp)

Accepts the byte vector that qmynd returns directly. Maps 4 bytes → dotted-decimal string, 16 bytes → colon-notation, 0 bytes / nil → nil.

Usage

LOAD DATABASE
  FROM mysql://...
  INTO postgresql://...
CAST
  column ip_table.ip_raw to inet using varbinary-to-inet;

Test coverage

  • Unit tests (v4): transforms_test.clj — nil, empty ("X"), IPv4 cases, IPv6 case.
  • End-to-end (mytest suite): new ip_addresses table with IPv4, IPv6, and NULL rows; cast rule in mytest.load; verification query in sql/16-varbinary-to-inet.sql.

dimitri added 4 commits July 15, 2026 17:13
MySQL stores IPv4 as 4-byte and IPv6 as 16-byte VARBINARY columns.
Without a transform, pgloader v4 emits the raw hex string (e.g.
"X7fffffff") which PostgreSQL rejects when the target column is inet.

v4 (Clojure):
- Add varbinary-to-inet to transforms.clj; accepts the "X"+hex string
  that convert-mysql-value produces for binary columns, strips the prefix,
  parses the bytes, and delegates to java.net.InetAddress/getByAddress
  which handles both 4-byte (IPv4) and 16-byte (IPv6) inputs natively.
- Add unit tests covering nil, empty ("X"), IPv4, and IPv6 cases.

v3 (Common Lisp):
- Add varbinary-to-inet to transforms.lisp; accepts a byte vector
  directly from qmynd (4 bytes → dotted decimal, 16 bytes → colon
  notation, 0 bytes / nil → nil).

mytest end-to-end coverage:
- New ip_addresses table in mytest.sql with IPv4, IPv6, and NULL rows.
- Cast rule in mytest.load: column ip_addresses.ip_raw to inet using
  varbinary-to-inet.
- Verification query in sql/16-varbinary-to-inet.sql.
Two string literals in transforms.clj contained raw \x00 bytes that
caused git to classify the file as binary. Replace with the Clojure
unicode escape \u0000 — semantically identical, ASCII-safe.
…emod; update expected

The varbinary-to-inet transform function was implemented in transforms.clj
(commit 8b2ac12) but was never registered in cast.clj's registry map.
apply-cast() looks up transform keywords in the registry; without the entry
it silently passed the raw X-hex string through to PostgreSQL, causing:

  ERROR: invalid input syntax for type inet: "X7f000001"

Fixes:
- cast.clj: add varbinary-to-inet function + :varbinary-to-inet registry entry
- cast_test.clj: unit tests for the function, apply-cast, and resolve-specs
- mytest.load: add 'drop typemod' to the column cast rule (VARBINARY(16)
  typemod would otherwise become inet(16) which PostgreSQL rejects)
- build.clj: exclude META-INF/LICENSE directory entry from uber JAR (fixes
  build failure when azure-identity JARs include it as a directory)
- expected/: regenerated 01-tables, 03-indexes, 12-pkeys for new ip_addresses
  table; add 16-varbinary-to-inet.out with all 5 rows including IPv4 and IPv6
The v3 regression variant (--variant v3) checks *.v3.out files
separately from the v4 *.out files. These three were not updated when
the ip_addresses table was added in the main varbinary-to-inet commit.

- 01-tables.v3.out: add ip_addresses, 33→34 rows
- 03-indexes.v3.out: add one idx_{oid}_PRIMARY, 39→40 rows
- 12-pkeys.v3.out: pk_constraints 27→28, total_indexes 39→40
@dimitri
dimitri merged commit c4296f8 into main Jul 15, 2026
37 of 38 checks passed
@dimitri
dimitri deleted the fix/mysql-varbinary-to-inet branch July 15, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lack of transformation of mysql varbinary stored ipv4/ipv6 addresses in pgloader:v4

1 participant