github.com/dshekhar95/sub_dgraph@v0.0.0-20230424164411-6be28e40bbf1/dgraph/cmd/migrate/run.go (about) 1 /* 2 * Copyright 2022 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package migrate 18 19 import ( 20 "bufio" 21 "fmt" 22 "log" 23 "os" 24 "strings" 25 26 "github.com/pkg/errors" 27 "github.com/spf13/cobra" 28 "github.com/spf13/viper" 29 30 "github.com/dgraph-io/dgraph/x" 31 ) 32 33 var ( 34 logger = log.New(os.Stderr, "", 0) 35 // Migrate is the sub-command invoked when running "dgraph migrate". 36 Migrate x.SubCommand 37 quiet bool // enabling quiet mode would suppress the warning logs 38 ) 39 40 func init() { 41 Migrate.Cmd = &cobra.Command{ 42 Use: "migrate", 43 Short: "Run the Dgraph migration tool from a MySQL database to Dgraph", 44 Run: func(cmd *cobra.Command, args []string) { 45 if err := run(Migrate.Conf); err != nil { 46 logger.Fatalf("%v\n", err) 47 } 48 }, 49 Annotations: map[string]string{"group": "tool"}, 50 } 51 Migrate.EnvPrefix = "DGRAPH_MIGRATE" 52 Migrate.Cmd.SetHelpTemplate(x.NonRootTemplate) 53 54 flag := Migrate.Cmd.Flags() 55 flag.StringP("user", "", "", "The user for logging in") 56 flag.StringP("password", "", "", "The password used for logging in") 57 flag.StringP("db", "", "", "The database to import") 58 flag.StringP("tables", "", "", "The comma separated list of "+ 59 "tables to import, an empty string means importing all tables in the database") 60 flag.StringP("output_schema", "s", "schema.txt", "The schema output file") 61 flag.StringP("output_data", "o", "sql.rdf", "The data output file") 62 flag.StringP("separator", "p", ".", "The separator for constructing predicate names") 63 flag.BoolP("quiet", "q", false, "Enable quiet mode to suppress the warning logs") 64 flag.StringP("host", "", "localhost", "The hostname or IP address of the database server.") 65 flag.StringP("port", "", "3306", "The port of the database server.") 66 } 67 68 func run(conf *viper.Viper) error { 69 user := conf.GetString("user") 70 db := conf.GetString("db") 71 password := conf.GetString("password") 72 tables := conf.GetString("tables") 73 schemaOutput := conf.GetString("output_schema") 74 dataOutput := conf.GetString("output_data") 75 host := conf.GetString("host") 76 port := conf.GetString("port") 77 quiet = conf.GetBool("quiet") 78 separator = conf.GetString("separator") 79 80 switch { 81 case len(user) == 0: 82 logger.Fatalf("The user property should not be empty.") 83 case len(db) == 0: 84 logger.Fatalf("The db property should not be empty.") 85 case len(password) == 0: 86 logger.Fatalf("The password property should not be empty.") 87 case len(schemaOutput) == 0: 88 logger.Fatalf("Please use the --output_schema option to " + 89 "provide the schema output file.") 90 case len(dataOutput) == 0: 91 logger.Fatalf("Please use the --output_data option to provide the data output file.") 92 } 93 94 if err := checkFile(schemaOutput); err != nil { 95 return err 96 } 97 if err := checkFile(dataOutput); err != nil { 98 return err 99 } 100 101 initDataTypes() 102 103 pool, err := getPool(host, port, user, password, db) 104 if err != nil { 105 return err 106 } 107 defer pool.Close() 108 109 tablesToRead, err := showTables(pool, tables) 110 if err != nil { 111 return err 112 } 113 114 tableInfos := make(map[string]*sqlTable) 115 for _, table := range tablesToRead { 116 tableInfo, err := parseTables(pool, table, db) 117 if err != nil { 118 return err 119 } 120 tableInfos[tableInfo.tableName] = tableInfo 121 } 122 populateReferencedByColumns(tableInfos) 123 124 tableGuides := getTableGuides(tableInfos) 125 126 return generateSchemaAndData(&dumpMeta{ 127 tableInfos: tableInfos, 128 tableGuides: tableGuides, 129 sqlPool: pool, 130 }, schemaOutput, dataOutput) 131 } 132 133 // checkFile checks if the program is trying to output to an existing file. 134 // If so, we would need to ask the user whether we should overwrite the file or abort the program. 135 func checkFile(file string) error { 136 if _, err := os.Stat(file); err == nil { 137 // The file already exists. 138 reader := bufio.NewReader(os.Stdin) 139 for { 140 fmt.Printf("overwriting the file %s (y/N)? ", file) 141 text, err := reader.ReadString('\n') 142 if err != nil { 143 return err 144 } 145 text = strings.TrimSpace(text) 146 147 if len(text) == 0 || strings.ToLower(text) == "n" { 148 return errors.Errorf("not allowed to overwrite %s", file) 149 } 150 if strings.ToLower(text) == "y" { 151 return nil 152 } 153 fmt.Println("Please type y or n (hit enter to choose n)") 154 } 155 } 156 157 // The file does not exist. 158 return nil 159 } 160 161 // generateSchemaAndData opens the two files schemaOutput and dataOutput, 162 // then it dumps schema to the writer backed by schemaOutput, and data in RDF format 163 // to the writer backed by dataOutput 164 func generateSchemaAndData(dumpMeta *dumpMeta, schemaOutput string, dataOutput string) error { 165 schemaWriter, schemaCancelFunc, err := getFileWriter(schemaOutput) 166 if err != nil { 167 return err 168 } 169 defer schemaCancelFunc() 170 dataWriter, dataCancelFunc, err := getFileWriter(dataOutput) 171 if err != nil { 172 return err 173 } 174 defer dataCancelFunc() 175 176 dumpMeta.dataWriter = dataWriter 177 dumpMeta.schemaWriter = schemaWriter 178 179 if err := dumpMeta.dumpSchema(); err != nil { 180 return errors.Wrapf(err, "while writing schema file") 181 } 182 if err := dumpMeta.dumpTables(); err != nil { 183 return errors.Wrapf(err, "while writing data file") 184 } 185 return nil 186 }