TODO: record empty-string DEFAULT round-trip drift#32
Conversation
A column declared with `NOT NULL DEFAULT ''` apply-loops because the catalog reads it back as a bareword empty string and the default normaliser doesn't wrap it the way it wraps non-empty barewords for ENUM / SET / CHAR / temporal types. Parser stores `''` (quoted), so they never compare equal and every post-apply plan re-emits `MODIFY COLUMN … DEFAULT ''`. Surfaced while writing the evolution scenario in PR #31; worked around there by dropping the explicit DEFAULT. Filed under "High — correctness bugs" because it produces a permanent drift loop that doesn't self-resolve. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #32 +/- ##
=======================================
Coverage 73.37% 73.37%
=======================================
Files 27 27
Lines 2141 2141
=======================================
Hits 1571 1571
Misses 410 410
Partials 160 160 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds a high-priority TODO item documenting an empty-string column default round-trip drift (NOT NULL DEFAULT '') that can cause perpetual “MODIFY COLUMN … DEFAULT ''” plans after apply, capturing a catalog vs parser normalization mismatch for future fixing.
Changes:
- Document the empty-string
DEFAULT ''drift loop as a “High — correctness bugs” TODO item. - Capture the suspected root cause and where the eventual fix should be applied (catalog default normalization).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Two Copilot nits on the entry I just added in PR #32: - normalizeColumnDefault lives in catalog/tables.go, not catalog/catalog.go. (Copilot #1.) - The function isn't ENUM/SET/CHAR/temporal-specific — it ignores typeName entirely and uses vitess to classify any value as bareword-vs-non-bareword. The bug is the `def == ""` short-circuit at the top of the function, which returns "" before the bareword check runs. The parser side stores `''` (quoted), so they never compare equal. (Copilot #2.) Note also expands the suggested fix: either wrap the empty-string case for column types where MySQL preserves it, or simply drop the short-circuit and let vitess classify "". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Copilot correctly noted the second fix path I'd suggested ("drop the
short-circuit and let vitess classify") doesn't work — vitess can't
parse `SELECT ` and returns a syntax error. The empty-string case
needs an explicit mapping, and since the right behaviour depends on
the column type (varchar/char/text/enum/set preserve `''`; numeric
types don't take a literal empty string at all), the fix has to
start consuming `typeName`, which the current implementation ignores.
Verified with a one-liner go run that `SELECT ''` parses fine but
`SELECT ` (trailing-space empty expression) errors out.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Copilot review: TEXT (and BLOB) cannot take a literal DEFAULT in MySQL, so listing `text` in the suggested-fix type set documents an impossible case. Narrow to CHAR / VARCHAR / BINARY / VARBINARY / ENUM / SET — the types that actually accept `DEFAULT ''` as the bareword empty string we'd need to round-trip — and explicitly call out that TEXT / BLOB are excluded because MySQL forbids a literal default on those. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Copilot review #4 style nit: the long line with the parenthetical aside broke the rest of TODO.md's wrapping convention, and inline `SELECT ` (with trailing space) is brittle in renderers that collapse trailing whitespace. Rewrapped at the standard width and turned the parenthetical into its own sentence; the inline code is now `SELECT` followed by the prose "with an empty expression". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
File the catalog default-normalisation bug surfaced in PR #31's evolution scenario.
A column declared with
NOT NULL DEFAULT ''apply-loops:COLUMN_DEFAULTas the bareword empty stringcatalog.normalizeColumnDefaultonly wraps non-empty barewords for ENUM / SET / CHAR / temporal types''(quoted)So they never compare equal and every post-apply plan re-emits
MODIFY COLUMN … DEFAULT ''.Filed under "High — correctness bugs" because it produces a permanent drift loop that doesn't self-resolve.
Test plan
🤖 Generated with Claude Code