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  }