github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/from-sql.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "regexp" 6 "strings" 7 "time" 8 9 "github.com/easysoft/zendata/internal/pkg/domain" 10 "github.com/easysoft/zendata/internal/pkg/helper" 11 "github.com/easysoft/zendata/internal/pkg/model" 12 fileUtils "github.com/easysoft/zendata/pkg/utils/file" 13 i118Utils "github.com/easysoft/zendata/pkg/utils/i118" 14 logUtils "github.com/easysoft/zendata/pkg/utils/log" 15 "github.com/easysoft/zendata/pkg/utils/vari" 16 "gopkg.in/yaml.v2" 17 ) 18 19 type SqlParseService struct { 20 } 21 22 func (s *SqlParseService) GenYamlFromSql(file string) { 23 startTime := time.Now().Unix() 24 25 sql := fileUtils.ReadFile(file) 26 statementMap, pkMap, fkMap := s.getCreateStatement(sql) 27 28 // gen key yaml files 29 inst := model.ZdInstances{Title: "keys", Desc: "automated export"} 30 31 for tableName, keyCol := range pkMap { 32 item := model.ZdInstancesItem{} 33 34 item.Instance = fmt.Sprintf("%s_%s", tableName, keyCol) 35 item.Range = "1-100000" 36 37 inst.Instances = append(inst.Instances, item) 38 } 39 40 bytes, _ := yaml.Marshal(&inst) 41 content := strings.ReplaceAll(string(bytes), "'-'", "\"\"") 42 43 if vari.GlobalVars.Output != "" { 44 vari.GlobalVars.Output = fileUtils.AddSepIfNeeded(vari.GlobalVars.Output) 45 outFile := vari.GlobalVars.Output + "keys.yaml" 46 fileUtils.WriteFile(outFile, content) 47 } else { 48 logUtils.PrintTo(content) 49 } 50 51 // gen table yaml files 52 for tableName, statement := range statementMap { 53 createSql := statement 54 55 columns, types := s.getColumnsFromCreateStatement(createSql, nil) 56 57 def := domain.DefSimple{} 58 def.Init(tableName, "automated export", "", "1.0") 59 60 for _, col := range columns { 61 field := domain.FieldSimple{Field: col} 62 63 // pk 64 isPk := col == pkMap[tableName] 65 fkInfo, isFk := fkMap[col] 66 67 if isPk { 68 field.From = "keys.yaml" 69 field.Use = fmt.Sprintf("%s_%s", tableName, col) 70 } else if isFk { 71 field.From = "keys.yaml" 72 field.Use = fmt.Sprintf("%s_%s{:1}", fkInfo[0], fkInfo[1]) 73 } else { 74 field.Range = types[col].Rang 75 76 field.Type = types[col].Type 77 field.Format = types[col].Format 78 field.From = types[col].From 79 field.Use = types[col].Use 80 field.From = types[col].From 81 field.Select = types[col].From 82 field.Prefix = types[col].Prefix 83 84 field.Note = types[col].Note 85 } 86 87 def.Fields = append(def.Fields, field) 88 } 89 90 bytes, _ := yaml.Marshal(&def) 91 content := strings.ReplaceAll(string(bytes), "'", "") 92 if vari.GlobalVars.Output != "" { 93 vari.GlobalVars.Output = fileUtils.AddSepIfNeeded(vari.GlobalVars.Output) 94 outFile := vari.GlobalVars.Output + tableName + ".yaml" 95 fileUtils.WriteFile(outFile, content) 96 } else { 97 logUtils.PrintTo(content) 98 } 99 } 100 101 entTime := time.Now().Unix() 102 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("generate_yaml", len(statementMap), vari.GlobalVars.Output, entTime-startTime)) 103 } 104 105 func (s *SqlParseService) getCreateStatement(content string) (statementMap map[string]string, pkMap map[string]string, fkMap map[string][2]string) { 106 statementMap = map[string]string{} 107 pkMap = map[string]string{} 108 fkMap = map[string][2]string{} 109 110 re := regexp.MustCompile("(?siU)(CREATE TABLE.*;)") 111 arr := re.FindAllString(string(content), -1) 112 for _, item := range arr { 113 re := regexp.MustCompile("(?i)CREATE TABLE.*\\s+(\\S+)\\s+\\(") // get table name 114 firstLine := strings.Split(item, "\n")[0] 115 arr2 := re.FindAllStringSubmatch(firstLine, -1) 116 117 if len(arr2) > 0 && len(arr2[0]) > 1 { 118 tableName := arr2[0][1] 119 tableName = strings.ReplaceAll(tableName, "`", "") 120 statementMap[tableName] = item 121 122 re3 := regexp.MustCompile("(?i)PRIMARY KEY\\s+\\((\\S+)\\)") 123 arr3 := re3.FindAllStringSubmatch(item, -1) 124 if len(arr3) > 0 { 125 for _, childArr := range arr3 { 126 pkMap[tableName] = strings.ReplaceAll(childArr[1], "`", "") 127 } 128 } 129 130 re4 := regexp.MustCompile("(?i)FOREIGN KEY\\s+\\((\\S+)\\) REFERENCES (\\S+) \\((\\S+)\\)") 131 arr4 := re4.FindAllStringSubmatch(item, -1) 132 if len(arr4) > 0 { 133 for _, childArr := range arr4 { 134 col := strings.ReplaceAll(childArr[1], "`", "") 135 toTable := strings.ReplaceAll(childArr[2], "`", "") 136 toCol := strings.ReplaceAll(childArr[3], "`", "") 137 138 fkMap[col] = [2]string{toTable, toCol} 139 } 140 } 141 142 } 143 } 144 145 return 146 } 147 148 func (s *SqlParseService) getColumnsFromCreateStatement(ddl string, recordsMap map[string][]interface{}) ( 149 fieldLines []string, fieldInfo map[string]helper.FieldTypeInfo) { 150 fieldInfo = map[string]helper.FieldTypeInfo{} 151 152 re := regexp.MustCompile("(?iU)\\s*(\\S+)\\s.*\n") 153 results := re.FindAllStringSubmatch(ddl, -1) 154 155 for _, items := range results { 156 temp := strings.ToLower(items[0]) 157 if strings.Contains(temp, " table ") || strings.Contains(temp, " key ") { 158 continue 159 } 160 161 typ, name, param := s.getFieldData(items) 162 fieldInfo[name] = helper.GenerateFieldDefByMetadata(typ, param, name, recordsMap[name]) 163 164 fieldLines = append(fieldLines, name) 165 166 } 167 168 return fieldLines, fieldInfo 169 } 170 171 func (s *SqlParseService) getFieldData(item []string) (typ, name string, param string) { 172 colName := item[1] 173 name = strings.ReplaceAll(colName, "`", "") 174 175 myExp := regexp.MustCompile(colName + `\s([a-zA-Z]+)\((?U:(.*))\)`) 176 result := myExp.FindStringSubmatch(item[0]) 177 178 if result != nil { // type with length like int(10) 179 typ = result[1] 180 param = result[2] 181 } else { 182 typ = strings.Split(strings.Fields(item[0])[1], "(")[0] 183 } 184 185 typ = strings.TrimSuffix(typ, ",") 186 187 typ = strings.ToLower(typ) 188 name = strings.ToLower(name) 189 190 return 191 } 192 193 func (s *SqlParseService) genKeysYaml(pkMap map[string]string) { 194 // gen key yaml files 195 inst := model.ZdInstances{Title: "keys", Desc: "automated export"} 196 197 for tableName, keyCol := range pkMap { 198 item := model.ZdInstancesItem{} 199 200 item.Instance = fmt.Sprintf("%s_%s", tableName, keyCol) 201 item.Range = "1-100000" 202 203 inst.Instances = append(inst.Instances, item) 204 } 205 206 bytes, _ := yaml.Marshal(&inst) 207 content := strings.ReplaceAll(string(bytes), "'-'", "\"\"") 208 209 if vari.GlobalVars.Output != "" { 210 vari.GlobalVars.Output = fileUtils.AddSepIfNeeded(vari.GlobalVars.Output) 211 outFile := vari.GlobalVars.Output + "keys.yaml" 212 fileUtils.WriteFile(outFile, content) 213 214 } else { 215 logUtils.PrintTo(content) 216 } 217 } 218 219 func (s *SqlParseService) genTablesYaml(statementMap map[string]string, 220 pkMap map[string]string, fkMap map[string][2]string, recordsMap map[string]map[string][]interface{}) { 221 for tableName, statement := range statementMap { 222 createStr := statement 223 224 columns, types := s.getColumnsFromCreateStatement(createStr, recordsMap[tableName]) 225 226 def := domain.DefSimple{} 227 def.Init(tableName, "automated export", "", "1.0") 228 229 for _, col := range columns { 230 field := domain.FieldSimple{Field: col} 231 232 // pk 233 isPk := col == pkMap[tableName] 234 fkInfo, isFk := fkMap[col] 235 236 if isPk { 237 field.From = "keys.yaml" 238 field.Use = fmt.Sprintf("%s_%s", tableName, col) 239 } else if isFk { 240 field.From = "keys.yaml" 241 field.Use = fmt.Sprintf("%s_%s{:1}", fkInfo[0], fkInfo[1]) 242 } else { 243 field.Range = types[col].Rang 244 245 field.Type = types[col].Type 246 field.Loop = types[col].Loop 247 field.Format = types[col].Format 248 field.From = types[col].From 249 field.Use = types[col].Use 250 field.From = types[col].From 251 field.Select = types[col].Select 252 field.Prefix = types[col].Prefix 253 254 field.Note = types[col].Note 255 } 256 257 def.Fields = append(def.Fields, field) 258 } 259 260 bytes, _ := yaml.Marshal(&def) 261 content := strings.ReplaceAll(string(bytes), "'", "") 262 if vari.GlobalVars.Output != "" { 263 vari.GlobalVars.Output = fileUtils.AddSepIfNeeded(vari.GlobalVars.Output) 264 outFile := vari.GlobalVars.Output + tableName + ".yaml" 265 fileUtils.WriteFile(outFile, content) 266 } else { 267 logUtils.PrintTo(content) 268 } 269 } 270 } 271 272 type TableInfo struct { 273 Field string 274 Type string 275 Null string 276 Key string 277 Default string 278 Extra string 279 }