profiles/common.go (131 lines of code) (raw):

// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package profiles import ( "encoding/csv" "fmt" "os" "strconv" "strings" "github.com/GoogleCloudPlatform/spanner-migration-tool/common/utils" go_ora "github.com/sijms/go-ora/v2" ) // Parses input string `s` as a map of key-value pairs. It's expected that the // input string is of the form "key1=value1,key2=value2,..." etc. Return error // otherwise. func ParseMap(s string) (map[string]string, error) { params := make(map[string]string) if len(s) == 0 { return params, nil } // We use CSV reader to parse key=value pairs separated by a comma to // handle the case where a value may contain a comma within a quote. We // expect exactly one record to be returned. r := csv.NewReader(strings.NewReader(s)) r.Comma = ',' r.TrimLeadingSpace = true records, err := r.ReadAll() if err != nil { return params, err } if len(records) > 1 { return params, fmt.Errorf("contains invalid newline characters") } for _, kv := range records[0] { s := strings.Split(strings.TrimSpace(kv), "=") if len(s) != 2 { return params, fmt.Errorf("invalid key=value pair (expected format: key1=value1): %v", kv) } if _, ok := params[s[0]]; ok { return params, fmt.Errorf("duplicate key found: %v", s[0]) } params[s[0]] = s[1] } return params, nil } func ParseList(s string)([]string, error) { if (len(s) == 0) { return nil, nil } r := csv.NewReader(strings.NewReader(s)) r.Comma = ',' r.TrimLeadingSpace = true records, err := r.ReadAll() if err != nil { return nil, err } if len(records) > 1 { return nil, fmt.Errorf("contains invalid newline characters") } return records[0], nil } func GetSQLConnectionStr(sourceProfile SourceProfile) string { sqlConnectionStr := "" if sourceProfile.Ty == SourceProfileTypeConnection { switch sourceProfile.Conn.Ty { case SourceProfileConnectionTypeMySQL: connParams := sourceProfile.Conn.Mysql return getMYSQLConnectionStr(connParams.Host, connParams.Port, connParams.User, connParams.Pwd, connParams.Db) case SourceProfileConnectionTypePostgreSQL: connParams := sourceProfile.Conn.Pg return getPGSQLConnectionStr(connParams.Host, connParams.Port, connParams.User, connParams.Pwd, connParams.Db) case SourceProfileConnectionTypeDynamoDB: // For DynamoDB, client provided by aws-sdk reads connection credentials from env variables only. // Thus, there is no need to create sqlConnectionStr for the same. We instead set the env variables // programmatically if not set. return "" case SourceProfileConnectionTypeSqlServer: connParams := sourceProfile.Conn.SqlServer return getSQLSERVERConnectionStr(connParams.Host, connParams.Port, connParams.User, connParams.Pwd, connParams.Db) case SourceProfileConnectionTypeOracle: connParams := sourceProfile.Conn.Oracle return getORACLEConnectionStr(connParams.Host, connParams.Port, connParams.User, connParams.Pwd, connParams.Db) } } return sqlConnectionStr } func GeneratePGSQLConnectionStr() (string, error) { server := os.Getenv("PGHOST") port := os.Getenv("PGPORT") user := os.Getenv("PGUSER") dbName := os.Getenv("PGDATABASE") if server == "" || port == "" || user == "" || dbName == "" { fmt.Printf("Please specify host, port, user and database using PGHOST, PGPORT, PGUSER and PGDATABASE environment variables\n") return "", fmt.Errorf("could not connect to source database") } password := os.Getenv("PGPASSWORD") if password == "" { getInfo := utils.GetUtilInfoImpl{} password = getInfo.GetPassword() } return getPGSQLConnectionStr(server, port, user, password, dbName), nil } func getPGSQLConnectionStr(server, port, user, password, dbName string) string { return fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", server, port, user, password, dbName) } func GenerateMYSQLConnectionStr() (string, error) { server := os.Getenv("MYSQLHOST") port := os.Getenv("MYSQLPORT") user := os.Getenv("MYSQLUSER") dbName := os.Getenv("MYSQLDATABASE") if server == "" || port == "" || user == "" || dbName == "" { fmt.Printf("Please specify host, port, user and database using MYSQLHOST, MYSQLPORT, MYSQLUSER and MYSQLDATABASE environment variables\n") return "", fmt.Errorf("could not connect to source database") } password := os.Getenv("MYSQLPWD") if password == "" { getInfo := utils.GetUtilInfoImpl{} password = getInfo.GetPassword() } return getMYSQLConnectionStr(server, port, user, password, dbName), nil } func getMYSQLConnectionStr(server, port, user, password, dbName string) string { return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", user, password, server, port, dbName) } func getSQLSERVERConnectionStr(server, port, user, password, dbName string) string { return fmt.Sprintf(`sqlserver://%s:%s@%s:%s?database=%s`, user, password, server, port, dbName) } func GetSchemaSampleSize(sourceProfile SourceProfile) int64 { schemaSampleSize := int64(100000) if sourceProfile.Ty == SourceProfileTypeConnection { if sourceProfile.Conn.Ty == SourceProfileConnectionTypeDynamoDB { if sourceProfile.Conn.Dydb.SchemaSampleSize != 0 { schemaSampleSize = sourceProfile.Conn.Dydb.SchemaSampleSize } } } return schemaSampleSize } func getORACLEConnectionStr(server, port, user, password, dbName string) string { portNumber, _ := strconv.Atoi(port) return go_ora.BuildUrl(server, portNumber, dbName, user, password, nil) }