github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/orm/cmd.go (about) 1 // The original package is migrated from beego and modified, you can find orignal from following link: 2 // "github.com/beego/beego/" 3 // 4 // Copyright 2023 IAC. All Rights Reserved. 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package orm 19 20 import ( 21 "context" 22 "flag" 23 "fmt" 24 "os" 25 "strings" 26 ) 27 28 type commander interface { 29 Parse([]string) 30 Run() error 31 } 32 33 var commands = make(map[string]commander) 34 35 // print help. 36 func printHelp(errs ...string) { 37 content := `orm command usage: 38 39 syncdb - auto create tables 40 sqlall - print sql of create tables 41 help - print this help 42 ` 43 44 if len(errs) > 0 { 45 fmt.Println(errs[0]) 46 } 47 fmt.Println(content) 48 os.Exit(2) 49 } 50 51 // RunCommand listens for orm command and runs if command arguments have been passed. 52 func RunCommand() { 53 if len(os.Args) < 2 || os.Args[1] != "orm" { 54 return 55 } 56 57 BootStrap() 58 59 args := argString(os.Args[2:]) 60 name := args.Get(0) 61 62 if name == "help" { 63 printHelp() 64 } 65 66 if cmd, ok := commands[name]; ok { 67 cmd.Parse(os.Args[3:]) 68 cmd.Run() 69 os.Exit(0) 70 } else { 71 if name == "" { 72 printHelp() 73 } else { 74 printHelp(fmt.Sprintf("unknown command %s", name)) 75 } 76 } 77 } 78 79 // sync database struct command interface. 80 type commandSyncDb struct { 81 al *alias 82 force bool 83 verbose bool 84 noInfo bool 85 rtOnError bool 86 } 87 88 // Parse the orm command line arguments. 89 func (d *commandSyncDb) Parse(args []string) { 90 var name string 91 92 flagSet := flag.NewFlagSet("orm command: syncdb", flag.ExitOnError) 93 flagSet.StringVar(&name, "db", "default", "DataBase alias name") 94 flagSet.BoolVar(&d.force, "force", false, "drop tables before create") 95 flagSet.BoolVar(&d.verbose, "v", false, "verbose info") 96 flagSet.Parse(args) 97 98 d.al = getDbAlias(name) 99 } 100 101 // Run orm line command. 102 func (d *commandSyncDb) Run() error { 103 var drops []string 104 var err error 105 if d.force { 106 drops, err = defaultModelCache.getDbDropSQL(d.al) 107 if err != nil { 108 return err 109 } 110 } 111 112 db := d.al.DB 113 114 if d.force && len(drops) > 0 { 115 for i, mi := range defaultModelCache.allOrdered() { 116 query := drops[i] 117 if !d.noInfo { 118 fmt.Printf("drop table `%s`\n", mi.table) 119 } 120 _, err := db.Exec(query) 121 if d.verbose { 122 fmt.Printf(" %s\n\n", query) 123 } 124 if err != nil { 125 if d.rtOnError { 126 return err 127 } 128 fmt.Printf(" %s\n", err.Error()) 129 } 130 } 131 } 132 133 createQueries, indexes, err := defaultModelCache.getDbCreateSQL(d.al) 134 if err != nil { 135 return err 136 } 137 138 tables, err := d.al.DbBaser.GetTables(db) 139 if err != nil { 140 if d.rtOnError { 141 return err 142 } 143 fmt.Printf(" %s\n", err.Error()) 144 } 145 146 ctx := context.Background() 147 for i, mi := range defaultModelCache.allOrdered() { 148 149 if !isApplicableTableForDB(mi.addrField, d.al.Name) { 150 fmt.Printf("table `%s` is not applicable to database '%s'\n", mi.table, d.al.Name) 151 continue 152 } 153 154 if tables[mi.table] { 155 if !d.noInfo { 156 fmt.Printf("table `%s` already exists, skip\n", mi.table) 157 } 158 159 var fields []*fieldInfo 160 columns, err := d.al.DbBaser.GetColumns(ctx, db, mi.table) 161 if err != nil { 162 if d.rtOnError { 163 return err 164 } 165 fmt.Printf(" %s\n", err.Error()) 166 } 167 168 for _, fi := range mi.fields.fieldsDB { 169 if _, ok := columns[fi.column]; !ok { 170 fields = append(fields, fi) 171 } 172 } 173 174 for _, fi := range fields { 175 query := getColumnAddQuery(d.al, fi) 176 177 if !d.noInfo { 178 fmt.Printf("add column `%s` for table `%s`\n", fi.fullName, mi.table) 179 } 180 181 _, err := db.Exec(query) 182 if d.verbose { 183 fmt.Printf(" %s\n", query) 184 } 185 if err != nil { 186 if d.rtOnError { 187 return err 188 } 189 fmt.Printf(" %s\n", err.Error()) 190 } 191 } 192 193 for _, idx := range indexes[mi.table] { 194 if !d.al.DbBaser.IndexExists(ctx, db, idx.Table, idx.Name) { 195 if !d.noInfo { 196 fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table) 197 } 198 199 query := idx.SQL 200 _, err := db.Exec(query) 201 if d.verbose { 202 fmt.Printf(" %s\n", query) 203 } 204 if err != nil { 205 if d.rtOnError { 206 return err 207 } 208 fmt.Printf(" %s\n", err.Error()) 209 } 210 } 211 } 212 213 continue 214 } 215 216 if !d.noInfo { 217 fmt.Printf("create table `%s` \n", mi.table) 218 } 219 220 queries := []string{createQueries[i]} 221 for _, idx := range indexes[mi.table] { 222 queries = append(queries, idx.SQL) 223 } 224 225 for _, query := range queries { 226 _, err := db.Exec(query) 227 if d.verbose { 228 query = " " + strings.Join(strings.Split(query, "\n"), "\n ") 229 fmt.Println(query) 230 } 231 if err != nil { 232 if d.rtOnError { 233 return err 234 } 235 fmt.Printf(" %s\n", err.Error()) 236 } 237 } 238 if d.verbose { 239 fmt.Println("") 240 } 241 } 242 243 return nil 244 } 245 246 // database creation commander interface implement. 247 type commandSQLAll struct { 248 al *alias 249 } 250 251 // Parse orm command line arguments. 252 func (d *commandSQLAll) Parse(args []string) { 253 var name string 254 255 flagSet := flag.NewFlagSet("orm command: sqlall", flag.ExitOnError) 256 flagSet.StringVar(&name, "db", "default", "DataBase alias name") 257 flagSet.Parse(args) 258 259 d.al = getDbAlias(name) 260 } 261 262 // Run orm line command. 263 func (d *commandSQLAll) Run() error { 264 createQueries, indexes, err := defaultModelCache.getDbCreateSQL(d.al) 265 if err != nil { 266 return err 267 } 268 var all []string 269 for i, mi := range defaultModelCache.allOrdered() { 270 queries := []string{createQueries[i]} 271 for _, idx := range indexes[mi.table] { 272 queries = append(queries, idx.SQL) 273 } 274 sql := strings.Join(queries, "\n") 275 all = append(all, sql) 276 } 277 fmt.Println(strings.Join(all, "\n\n")) 278 279 return nil 280 } 281 282 func init() { 283 commands["syncdb"] = new(commandSyncDb) 284 commands["sqlall"] = new(commandSQLAll) 285 } 286 287 // RunSyncdb run syncdb command line. 288 // name: Table's alias name (default is "default") 289 // force: Run the next sql command even if the current gave an error 290 // verbose: Print all information, useful for debugging 291 func RunSyncdb(name string, force bool, verbose bool) error { 292 BootStrap() 293 294 al := getDbAlias(name) 295 cmd := new(commandSyncDb) 296 cmd.al = al 297 cmd.force = force 298 cmd.noInfo = !verbose 299 cmd.verbose = verbose 300 cmd.rtOnError = true 301 return cmd.Run() 302 }