1package sqlite 2 3import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "strings" 8) 9 10type ddl struct { 11 head string 12 fields []string 13} 14 15func parseDDL(sql string) (*ddl, error) { 16 reg := regexp.MustCompile("(?i)(CREATE TABLE [\"`]?[\\w\\d]+[\"`]?)(?: \\((.*)\\))?") 17 sections := reg.FindStringSubmatch(sql) 18 19 if sections == nil { 20 return nil, errors.New("invalid DDL") 21 } 22 23 ddlHead := sections[1] 24 ddlBody := sections[2] 25 ddlBodyRunes := []rune(ddlBody) 26 fields := []string{} 27 28 bracketLevel := 0 29 var quote rune = 0 30 buf := "" 31 32 for i := 0; i < len(ddlBodyRunes); i++ { 33 c := ddlBodyRunes[i] 34 var next rune = 0 35 if i+1 < len(ddlBodyRunes) { 36 next = ddlBodyRunes[i+1] 37 } 38 39 if c == '\'' || c == '"' || c == '`' { 40 if c == next { 41 // Skip escaped quote 42 buf += string(c) 43 i++ 44 } else if quote > 0 { 45 quote = 0 46 } else { 47 quote = c 48 } 49 } else if quote == 0 { 50 if c == '(' { 51 bracketLevel++ 52 } else if c == ')' { 53 bracketLevel-- 54 } else if bracketLevel == 0 { 55 if c == ',' { 56 fields = append(fields, strings.TrimSpace(buf)) 57 buf = "" 58 continue 59 } 60 } 61 } 62 63 if bracketLevel < 0 { 64 return nil, errors.New("invalid DDL, unbalanced brackets") 65 } 66 67 buf += string(c) 68 } 69 70 if bracketLevel != 0 { 71 return nil, errors.New("invalid DDL, unbalanced brackets") 72 } 73 74 if buf != "" { 75 fields = append(fields, strings.TrimSpace(buf)) 76 } 77 78 return &ddl{head: ddlHead, fields: fields}, nil 79} 80 81func (d *ddl) compile() string { 82 if len(d.fields) == 0 { 83 return d.head 84 } 85 86 return fmt.Sprintf("%s (%s)", d.head, strings.Join(d.fields, ",")) 87} 88 89func (d *ddl) addConstraint(name string, sql string) { 90 reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") 91 92 for i := 0; i < len(d.fields); i++ { 93 if reg.MatchString(d.fields[i]) { 94 d.fields[i] = sql 95 return 96 } 97 } 98 99 d.fields = append(d.fields, sql) 100} 101 102func (d *ddl) removeConstraint(name string) bool { 103 reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") 104 105 for i := 0; i < len(d.fields); i++ { 106 if reg.MatchString(d.fields[i]) { 107 d.fields = append(d.fields[:i], d.fields[i+1:]...) 108 return true 109 } 110 } 111 return false 112} 113 114func (d *ddl) hasConstraint(name string) bool { 115 reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") 116 117 for _, f := range d.fields { 118 if reg.MatchString(f) { 119 return true 120 } 121 } 122 return false 123} 124 125func (d *ddl) getColumns() []string { 126 res := []string{} 127 128 for _, f := range d.fields { 129 fUpper := strings.ToUpper(f) 130 if strings.HasPrefix(fUpper, "PRIMARY KEY") || 131 strings.HasPrefix(fUpper, "CHECK") || 132 strings.HasPrefix(fUpper, "CONSTRAINT") { 133 continue 134 } 135 136 reg := regexp.MustCompile("^[\"`]?([\\w\\d]+)[\"`]?") 137 match := reg.FindStringSubmatch(f) 138 139 if match != nil { 140 res = append(res, "`"+match[1]+"`") 141 } 142 } 143 return res 144} 145