Repository navigation
Conversation
[Medium impact] Fixes CHECK constraint normalization for LIKE patterns. Fix the quoted-pattern replacement before merging; it can change the rules enforced by generated constraints. Findings
SummaryThis PR rewrites LIKE expressions in place so CHECK constraints keep their trailing conditions.
Reviews (1) · Last reviewed commit: "fix: keep the conditions after LIKE in C..." · Reviewed by Greptile |
| clause = likeLiteralRe.ReplaceAllString(clause, " LIKE $1") | ||
| clause = strings.ReplaceAll(clause, " ~~ ", " LIKE ") |
There was a problem hiding this comment.
The global replacement changes ~~ inside quoted text, not just LIKE operators. For example, CHECK (name LIKE 'a ~~ b') arrives as name ~~ 'a ~~ b'::text and now becomes name LIKE 'a LIKE b'. The old split left this expression unchanged because it contained two occurrences. Dumps and generated constraints therefore check a different value.
Rewrite only operators outside strings and quoted identifiers, and add a regression test for this pattern.
Knowledge Base Used: Schema normalization and identity
There was a problem hiding this comment.
🟡 Changes recommended
The blanket replacement can modify quoted pattern contents and silently change constraint semantics.
1 open finding
What changed in this PR
Fixes #648 by preserving conditions after LIKE patterns when normalizing PostgreSQL CHECK constraints.
Changes:
- Replaces truncating normalization with in-place rewriting.
- Adds unit tests for compound conditions and escaped quotes.
- Adds migration fixtures showing detection of a strengthened constraint.
| File | Description |
|---|---|
ir/normalize.go |
Rewrites LIKE operands without truncating trailing conditions. |
ir/normalize_test.go |
Adds LIKE normalization regression tests. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/old.sql |
Defines the original constraint. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/new.sql |
Adds the required partner condition. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/diff.sql |
Records expected constraint replacement. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/plan.json |
Records structured migration steps. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/plan.sql |
Records executable migration SQL. |
testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions/plan.txt |
Records human-readable migration output. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } | ||
| return fmt.Sprintf("%s LIKE %s", columnName, pattern) | ||
| clause = likeLiteralRe.ReplaceAllString(clause, " LIKE $1") | ||
| clause = strings.ReplaceAll(clause, " ~~ ", " LIKE ") |
|
Close in favor of #649 |

Summary
Fixes #648.
applyLegacyCheckNormalizationsturnedcol ~~ 'pat'::textintocol LIKE 'pat'by splitting the clause on" ~~ "and cutting the right-hand side at its first::. If anything followed the pattern, it was dropped, e.g.) AND (b IS NOT NULL). Both sides of a diff go through the same normalisation, soplancouldn't see the loss.dumpandapplyto an empty schema then wrote the weaker constraint.The fix rewrites each
~~ '<literal>'::text|varcharoperand in place and leaves the rest of the expression as it is. After the rewrite, one redundant pair of outer parentheses is stripped, so a simpleCHECK (col LIKE 'x')normalises exactly as before. The existing dump fixtures (e.g.bytebase) don't change.Tests
ir/normalize_test.go: a LIKE on its own, a LIKE followed byAND, two LIKEs, and a pattern with an escaped quote.testdata/diff/create_table/issue_648_check_like_keeps_trailing_conditions: the old schema hasCHECK (id IS NULL OR id LIKE 'p-%')and the new one addsAND partner_id IS NOT NULL. Without the fix the plan is empty; with it the constraint is dropped and re-added.Ran locally:
go test ./ir/ ./internal/diff/ ./cmd/dump/, plusTestPlanAndApplyfiltered to the new case.