Skip to content

parser/model/catalog/diff: round-trip SRID on spatial columns#100

Merged
winebarrel merged 2 commits into
mainfrom
feat-spatial-srid
May 6, 2026
Merged

parser/model/catalog/diff: round-trip SRID on spatial columns#100
winebarrel merged 2 commits into
mainfrom
feat-spatial-srid

Conversation

@winebarrel

Copy link
Copy Markdown
Owner

Summary

TODO "Medium — silent diffs / fidelity gaps" item 2: `SRID ` on spatial columns was silently dropped. vitess parsed `cd.Type.Options.SRID` but `parseColumnDef` ignored it; the catalog never read `SRS_ID`. A hand-written desired schema with `SRID 4326` constants drifted forever — `dump → plan` happened to look clean only because dump round-trips through the same gap.

Changes

Wire SRID through every layer:

  • `model.Column.SRID *uint32` — `*0` is a real declaration distinct from "unset" (catalog stores `SRS_ID=0` for `SRID 0`, NULL for no clause; this is verified by probing `information_schema.ST_GEOMETRY_COLUMNS`).
  • `parser.parseColumnDef` reads `cd.Type.Options.SRID` (vitess `*Literal` whose `Val` is the integer text), parses to `uint32`.
  • `model.columnDefSQL` emits `SRID N` between `NOT NULL` and `DEFAULT` to match MySQL's `SHOW CREATE TABLE` column-attribute order. Plain `SRID N` — no `/*!80003 ... */` version-gated comment, since myschema requires 8.0+ already.
  • `catalog.loadSpatialSRIDs` queries `information_schema.ST_GEOMETRY_COLUMNS` and populates `Column.SRID` where `SRS_ID` is non-NULL.
  • `diff.columnEqual` adds an SRID comparison so a desired-side SRID change fires `MODIFY COLUMN`.

Tests

  • `parser`: `TestParseColumnSRID` covers `SRID 4326`, `SRID 0`, no SRID.
  • `model`: `TestColumnDefSQLSRID` (nil → no emit, `*0` → emit, slot pin); `TestColumnDefSQLFullOrdering` extended with SRID between `NOT NULL` and `DEFAULT`.
  • `catalog`: `TestSpatialSRIDRoundTrip` (4326 / 0 / unset).
  • `diff`: `TestColumnEqual` sub-tests for value-differs, set-vs-unset, `SRID 0` vs unset.
  • `testdata/apply/spatial_srid_round_trip.yml`: end-to-end fixture with all three states under `verify_no_drift`.

Coverage on touched code: `columnEqual` / `columnDefSQL` 100%; `loadSpatialSRIDs` 82.4% (defensive query/scan-error paths uncovered); `parseColumnDef` 94.9% (SRID branch fully covered).

Test plan

  • `make lint` clean.
  • `make test` all 6 packages green.
  • `make test-scenario` 5/5 passing.

🤖 Generated with Claude Code

vitess parsed `cd.Type.Options.SRID` but `parseColumnDef`
ignored it; the catalog never read `SRS_ID`. A desired schema
with `SRID 4326` constants drifted forever — `dump → plan` was
clean, but a hand-written desired had no path to reach the
catalog's SRID.

Wire SRID through every layer:

- `model.Column.SRID *uint32` — *0 is a real declaration distinct
  from "unset" (catalog stores SRS_ID=0 for `SRID 0`, NULL for
  no clause).
- `parser.parseColumnDef` reads `cd.Type.Options.SRID` (vitess
  *Literal whose Val is the integer text), parses to uint32.
- `model.columnDefSQL` emits `SRID N` between NOT NULL and
  DEFAULT to match MySQL's SHOW CREATE TABLE column-attribute
  order. Plain `SRID N` (no `/*!80003 ... */` version-gated
  comment — myschema requires 8.0+ already).
- `catalog.loadSpatialSRIDs` queries
  `information_schema.ST_GEOMETRY_COLUMNS`, populating
  Column.SRID for rows where SRS_ID is not NULL.
- `diff.columnEqual` adds an SRID comparison so a desired-side
  SRID change fires a MODIFY COLUMN.

Tests:
- parser: TestParseColumnSRID (SRID 4326, SRID 0, unset).
- model: TestColumnDefSQLSRID (nil → no emit, *0 → emit, slot
  pinning) plus TestColumnDefSQLFullOrdering extended with
  SRID between NOT NULL and DEFAULT.
- catalog: TestSpatialSRIDRoundTrip (4326 / 0 / unset).
- diff: TestColumnEqual sub-tests for value-differs, set-vs-unset,
  0-vs-unset.
- testdata/apply/spatial_srid_round_trip.yml: end-to-end fixture
  with all three states under verify_no_drift.

Coverage on touched code: columnEqual / columnDefSQL 100%;
loadSpatialSRIDs 82.4% (defensive error paths uncovered);
parseColumnDef 94.9% (SRID branch fully covered).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 6, 2026 06:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR closes a schema round-trip fidelity gap for MySQL 8.0+ spatial columns by preserving and diffing SRID <n> attributes end-to-end (desired SQL ↔ model ↔ catalog ↔ dump/plan), so SRID declarations no longer silently disappear and drift forever.

Changes:

  • Add model.Column.SRID *uint32 to represent SRID N, preserving the important distinction between “unset” (nil) and “explicit SRID 0” (*uint32(0)).
  • Parse SRID from Vitess (cd.Type.Options.SRID) into the model, emit it in columnDefSQL, and include it in diff.columnEqual so SRID changes trigger MODIFY COLUMN.
  • Populate SRID from information_schema.ST_GEOMETRY_COLUMNS.SRS_ID in the catalog, with unit/integration coverage plus an end-to-end apply fixture.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
TODO.md Removes the resolved TODO item about SRID being dropped.
testdata/apply/spatial_srid_round_trip.yml End-to-end apply fixture covering SRID=4326, SRID=0, and unset.
parser/parser.go Parses cd.Type.Options.SRID into Column.SRID with uint32 validation.
parser/parser_test.go Adds TestParseColumnSRID to pin parser behavior for SRID states.
model/column.go Introduces Column.SRID *uint32 with semantics documented (nil vs explicit 0).
model/table.go Emits SRID N in column definitions in MySQL-compatible attribute order.
model/table_test.go Adds SRID emission tests and pins SRID’s ordering relative to other attributes.
diff/tables.go Compares SRID as part of columnEqual so diffs surface SRID drift.
diff/tables_test.go Adds SRID-focused ColumnEqual subtests (differs, set/unset, 0/unset).
catalog/tables.go Loads SRID from information_schema.ST_GEOMETRY_COLUMNS into Column.SRID.
catalog/tables_test.go Adds catalog round-trip test for SRID=4326, SRID=0, and unset.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented May 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.77778% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.86%. Comparing base (6dddceb) to head (ac2eefb).

Files with missing lines Patch % Lines
catalog/tables.go 69.23% 4 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #100      +/-   ##
==========================================
- Coverage   86.96%   86.86%   -0.10%     
==========================================
  Files          30       30              
  Lines        3321     3357      +36     
==========================================
+ Hits         2888     2916      +28     
- Misses        266      270       +4     
- Partials      167      171       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

CI codecov/patch failed at 72.22% (target 75%). The biggest
uncovered block in the patch was the SRID overflow error path
in parseColumnDef — reachable from valid SQL (vitess accepts
arbitrary integer text but Column.SRID is *uint32, so values
> 4294967295 overflow at parse time).

- TestParseColumnSRIDOverflow exercises the overflow path with
  SRID 4294967296 (uint32 max + 1).
- Drop the redundant "column %s: " prefix from the error
  format. parseCreateTable already wraps with table+column
  context, so the message double-prefixed as
  "column g: column g: SRID..." before. Now the wrapped form
  reads "table app.t: column g: SRID..." cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@winebarrel winebarrel merged commit d4a7cdc into main May 6, 2026
5 checks passed
@winebarrel winebarrel deleted the feat-spatial-srid branch May 6, 2026 06:16
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.

2 participants