From 56065820843c40064a40c27e80224608b70a3431 Mon Sep 17 00:00:00 2001 From: Alexandre Boucey Date: Sun, 19 Jan 2025 17:32:39 +0100 Subject: [PATCH] fix: cover more constraint syntax SQLite stores the DDL "as-is" in `sqlite_master`. This new regex covers a broader range of valid syntax for constraints. --- ddlmod.go | 10 +++++++--- ddlmod_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/ddlmod.go b/ddlmod.go index c839cd7..e7e5859 100644 --- a/ddlmod.go +++ b/ddlmod.go @@ -209,8 +209,12 @@ func (d *ddl) renameTable(dst, src string) error { return nil } +func compileConstraintRegexp(name string) *regexp.Regexp { + return regexp.MustCompile("^(?i:CONSTRAINT)\\s+[\"`]?" + regexp.QuoteMeta(name) + "[\"`\\s]") +} + func (d *ddl) addConstraint(name string, sql string) { - reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + reg := compileConstraintRegexp(name) for i := 0; i < len(d.fields); i++ { if reg.MatchString(d.fields[i]) { @@ -223,7 +227,7 @@ func (d *ddl) addConstraint(name string, sql string) { } func (d *ddl) removeConstraint(name string) bool { - reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + reg := compileConstraintRegexp(name) for i := 0; i < len(d.fields); i++ { if reg.MatchString(d.fields[i]) { @@ -235,7 +239,7 @@ func (d *ddl) removeConstraint(name string) bool { } func (d *ddl) hasConstraint(name string) bool { - reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + reg := compileConstraintRegexp(name) for _, f := range d.fields { if reg.MatchString(f) { diff --git a/ddlmod_test.go b/ddlmod_test.go index 6e81e2b..a1122eb 100644 --- a/ddlmod_test.go +++ b/ddlmod_test.go @@ -313,6 +313,41 @@ func TestRemoveConstraint(t *testing.T) { success: true, expect: []string{"`id` integer NOT NULL"}, }, + { + name: "lowercase", + fields: []string{"`id` integer NOT NULL", "constraint `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"}, + cName: "fk_users_notes", + success: true, + expect: []string{"`id` integer NOT NULL"}, + }, + { + name: "mixed_case", + fields: []string{"`id` integer NOT NULL", "cOnsTraiNT `fk_users_notes` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"}, + cName: "fk_users_notes", + success: true, + expect: []string{"`id` integer NOT NULL"}, + }, + { + name: "newline", + fields: []string{"`id` integer NOT NULL", "CONSTRAINT `fk_users_notes`\nFOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"}, + cName: "fk_users_notes", + success: true, + expect: []string{"`id` integer NOT NULL"}, + }, + { + name: "lots_of_newlines", + fields: []string{"`id` integer NOT NULL", "constraint \n fk_users_notes \n FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"}, + cName: "fk_users_notes", + success: true, + expect: []string{"`id` integer NOT NULL"}, + }, + { + name: "no_backtick", + fields: []string{"`id` integer NOT NULL", "CONSTRAINT fk_users_notes FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))"}, + cName: "fk_users_notes", + success: true, + expect: []string{"`id` integer NOT NULL"}, + }, { name: "check", fields: []string{"CONSTRAINT `name_checker` CHECK (`name` <> 'thetadev')", "`id` integer NOT NULL"},