Skip to content

Commit fc0cfd3

Browse files
authored
fix: improve DDL parsing for CHECK/CONSTRAINT/FOREIGN KEY (#207)
* fix: improve DDL parsing for CHECK/CONSTRAINT/FOREIGN KEY * fix: add support for UNIQUE without CONSTRAINT
1 parent 6f07b51 commit fc0cfd3

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

ddlmod.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import (
1313

1414
var (
1515
sqliteSeparator = "`|\"|'|\t"
16-
uniqueRegexp = regexp.MustCompile(fmt.Sprintf(`^CONSTRAINT [%v]?[\w-]+[%v]? UNIQUE (.*)$`, sqliteSeparator, sqliteSeparator))
16+
sqliteColumnQuote = "`"
17+
uniqueRegexp = regexp.MustCompile(fmt.Sprintf(`^(?:CONSTRAINT [%v]?[\w-]+[%v]? )?UNIQUE (.*)$`, sqliteSeparator, sqliteSeparator))
1718
indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]?(?s:.*?)ON (.*)$`, sqliteSeparator, sqliteSeparator))
1819
tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator))
20+
checkRegexp = regexp.MustCompile(`^(?i)CHECK[\s]*\(`)
21+
constraintRegexp = regexp.MustCompile(fmt.Sprintf(`^(?i)CONSTRAINT\s+%[1]s[\w\d_]+%[1]s[\s]+`, sqliteColumnQuote))
1922
separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator))
2023
columnRegexp = regexp.MustCompile(fmt.Sprintf(`^[%v]?([\w\d]+)[%v]?\s+([\w\(\)\d]+)(.*)$`, sqliteSeparator, sqliteSeparator))
2124
defaultValueRegexp = regexp.MustCompile(`(?i) DEFAULT \(?(.+)?\)?( |COLLATE|GENERATED|$)`)
@@ -92,11 +95,10 @@ func parseDDL(strs ...string) (*ddl, error) {
9295

9396
for _, f := range result.fields {
9497
fUpper := strings.ToUpper(f)
95-
if strings.HasPrefix(fUpper, "CHECK") {
98+
if checkRegexp.MatchString(f) || strings.HasPrefix(fUpper, "FOREIGN KEY") {
9699
continue
97100
}
98-
if strings.HasPrefix(fUpper, "CONSTRAINT") {
99-
matches := uniqueRegexp.FindStringSubmatch(f)
101+
if matches := uniqueRegexp.FindStringSubmatch(f); matches != nil {
100102
if len(matches) > 0 {
101103
cols, err := parseAllColumns(matches[1])
102104
if err == nil && len(cols) == 1 {
@@ -111,6 +113,9 @@ func parseDDL(strs ...string) (*ddl, error) {
111113
}
112114
continue
113115
}
116+
if constraintRegexp.MatchString(f) {
117+
continue
118+
}
114119
if strings.HasPrefix(fUpper, "PRIMARY KEY") {
115120
cols, err := parseAllColumns(f)
116121
if err == nil {
@@ -255,12 +260,15 @@ func (d *ddl) getColumns() []string {
255260
for _, f := range d.fields {
256261
fUpper := strings.ToUpper(f)
257262
if strings.HasPrefix(fUpper, "PRIMARY KEY") ||
258-
strings.HasPrefix(fUpper, "CHECK") ||
259-
strings.HasPrefix(fUpper, "CONSTRAINT") ||
263+
strings.HasPrefix(fUpper, "FOREIGN KEY") ||
260264
strings.Contains(fUpper, "GENERATED ALWAYS AS") {
261265
continue
262266
}
263267

268+
if checkRegexp.MatchString(f) || constraintRegexp.MatchString(f) || uniqueRegexp.MatchString(f) {
269+
continue
270+
}
271+
264272
reg := regexp.MustCompile("^[\"`']?([\\w\\d]+)[\"`']?")
265273
match := reg.FindStringSubmatch(f)
266274

ddlmod_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,26 @@ func TestParseDDL(t *testing.T) {
142142
},
143143
},
144144
},
145+
{"with a check-like column", []string{"CREATE TABLE Docs (ID int NOT NULL,Checksum text NOT NULL)"}, 2, []migrator.ColumnType{
146+
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
147+
{NameValue: sql.NullString{String: "Checksum", Valid: true}, DataTypeValue: sql.NullString{String: "text", Valid: true}, ColumnTypeValue: sql.NullString{String: "text", Valid: true}, NullableValue: sql.NullBool{Bool: false, Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
148+
}},
149+
{"with a constraint-like column", []string{"CREATE TABLE Docs (ID int NOT NULL,constraints text NOT NULL)"}, 2, []migrator.ColumnType{
150+
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
151+
{NameValue: sql.NullString{String: "constraints", Valid: true}, DataTypeValue: sql.NullString{String: "text", Valid: true}, ColumnTypeValue: sql.NullString{String: "text", Valid: true}, NullableValue: sql.NullBool{Bool: false, Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
152+
}},
153+
{"with a unique-like column", []string{"CREATE TABLE Docs (ID int NOT NULL,unique_code text NOT NULL)"}, 2, []migrator.ColumnType{
154+
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
155+
{NameValue: sql.NullString{String: "unique_code", Valid: true}, DataTypeValue: sql.NullString{String: "text", Valid: true}, ColumnTypeValue: sql.NullString{String: "text", Valid: true}, NullableValue: sql.NullBool{Bool: false, Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
156+
}},
157+
{"with_fk_no_constraint", []string{"CREATE TABLE Docs (ID int NOT NULL,UserID int NOT NULL,FOREIGN KEY (UserID) REFERENCES Users(ID))"}, 3, []migrator.ColumnType{
158+
{NameValue: sql.NullString{String: "ID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
159+
{NameValue: sql.NullString{String: "UserID", Valid: true}, DataTypeValue: sql.NullString{String: "int", Valid: true}, ColumnTypeValue: sql.NullString{String: "int", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
160+
}},
161+
{"with unique without constraint", []string{"CREATE TABLE `users` (`id` text NOT NULL,`email` text NOT NULL,PRIMARY KEY (`id`),UNIQUE (`email`))"}, 4, []migrator.ColumnType{
162+
{NameValue: sql.NullString{String: "id", Valid: true}, DataTypeValue: sql.NullString{String: "text", Valid: true}, ColumnTypeValue: sql.NullString{String: "text", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true}, PrimaryKeyValue: sql.NullBool{Valid: true, Bool: true}},
163+
{NameValue: sql.NullString{String: "email", Valid: true}, DataTypeValue: sql.NullString{String: "text", Valid: true}, ColumnTypeValue: sql.NullString{String: "text", Valid: true}, NullableValue: sql.NullBool{Valid: true}, DefaultValueValue: sql.NullString{Valid: false}, UniqueValue: sql.NullBool{Valid: true, Bool: true}, PrimaryKeyValue: sql.NullBool{Valid: true}},
164+
}},
145165
}
146166

147167
for _, p := range params {

0 commit comments

Comments
 (0)