// getFile builds the filepath from the TBuf information, and retrieves the // file from files. If the built filename is not already defined, then it calls // the os.OpenFile with the correct parameters depending on the state of args. func getFile(args *internal.ArgType, t *internal.TBuf) (*os.File, error) { var f *os.File var err error // determine filename var filename = strings.ToLower(t.Name) + args.Suffix if args.SingleFile { filename = args.Filename } filename = path.Join(args.Path, filename) // lookup file f, ok := files[filename] if ok { return f, nil } // default open mode mode := os.O_RDWR | os.O_CREATE | os.O_TRUNC // stat file to determine if file already exists fi, err := os.Stat(filename) if err == nil && fi.IsDir() { return nil, errors.New("filename cannot be directory") } else if _, ok = err.(*os.PathError); !ok && args.Append && t.TemplateType != internal.XOTemplate { // file exists so append if append is set and not XO type mode = os.O_APPEND | os.O_WRONLY } // skip if t.TemplateType == internal.XOTemplate && fi != nil { return nil, nil } // open file f, err = os.OpenFile(filename, mode, 0666) if err != nil { return nil, err } // file didn't originally exist, so add package header if fi == nil || !args.Append { err = args.TemplateSet().Execute(f, "xo_package.go.tpl", args) if err != nil { return nil, err } } // store file files[filename] = f return f, nil }
// openDB attempts to open a database connection. func openDB(args *internal.ArgType) error { var err error // parse dsn u, err := dburl.Parse(args.DSN) if err != nil { return err } // save driver type args.LoaderType = u.Driver // grab loader var ok bool args.Loader, ok = internal.SchemaLoaders[u.Driver] if !ok { return errors.New("unsupported database type") } // open database connection args.DB, err = sql.Open(u.Driver, u.DSN) if err != nil { return err } return nil }
// PgParseType parse a postgres type into a Go type based on the column // definition. func PgParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilVal := "nil" asSlice := false // handle SETOF if strings.HasPrefix(dt, "SETOF ") { _, _, t := PgParseType(args, dt[len("SETOF "):], false) return 0, "nil", "[]" + t } // determine if it's a slice if strings.HasSuffix(dt, "[]") { dt = dt[:len(dt)-2] asSlice = true } // extract precision dt, precision, _ = args.ParsePrecision(dt) var typ string switch dt { case "boolean": nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } case "character", "character varying", "text", "money": nilVal = `""` typ = "string" if nullable { nilVal = "sql.NullString{}" typ = "sql.NullString" } case "smallint": nilVal = "0" typ = "int16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "integer": nilVal = "0" typ = args.Int32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigint": nilVal = "0" typ = "int64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "smallserial": nilVal = "0" typ = "uint16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "serial": nilVal = "0" typ = args.Uint32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigserial": nilVal = "0" typ = "uint64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "real": nilVal = "0.0" typ = "float32" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "numeric", "double precision": nilVal = "0.0" typ = "float64" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "bytea": asSlice = true typ = "byte" case "date", "timestamp with time zone", "time with time zone", "time without time zone", "timestamp without time zone": typ = "*time.Time" if nullable { nilVal = "pq.NullTime{}" typ = "pq.NullTime" } case "interval": typ = "*time.Duration" case `"char"`, "bit": // FIXME: this needs to actually be tested ... // i think this should be 'rune' but I don't think database/sql // supports 'rune' as a type? // // this is mainly here because postgres's pg_catalog.* meta tables have // this as a type. //typ = "rune" nilVal = `uint8(0)` typ = "uint8" case `"any"`, "bit varying": asSlice = true typ = "byte" default: if strings.HasPrefix(dt, args.Schema+".") { // in the same schema, so chop off typ = internal.SnakeToIdentifier(dt[len(args.Schema)+1:]) nilVal = typ + "(0)" } else { typ = internal.SnakeToIdentifier(dt) nilVal = typ + "{}" } } // special case for []slice if typ == "string" && asSlice { return precision, "StringSlice{}", "StringSlice" } // correct type if slice if asSlice { typ = "[]" + typ nilVal = "nil" } return precision, nilVal, typ }
// MyParseType parse a mysql type into a Go type based on the column // definition. func MyParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilVal := "nil" unsigned := false // extract unsigned if strings.HasSuffix(dt, " unsigned") { unsigned = true dt = dt[:len(dt)-len(" unsigned")] } // extract precision dt, precision, _ = args.ParsePrecision(dt) var typ string switchDT: switch dt { case "bit": nilVal = "0" if precision == 1 { nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } break switchDT } else if precision <= 8 { typ = "uint8" } else if precision <= 16 { typ = "uint16" } else if precision <= 32 { typ = "uint32" } else { typ = "uint64" } if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bool", "boolean": nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } case "char", "varchar", "tinytext", "text", "mediumtext", "longtext": nilVal = `""` typ = "string" if nullable { nilVal = "sql.NullString{}" typ = "sql.NullString" } case "tinyint", "smallint": nilVal = "0" typ = "int16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "mediumint", "int", "integer": nilVal = "0" typ = args.Int32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigint": nilVal = "0" typ = "int64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "float": nilVal = "0.0" typ = "float32" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "decimal", "double": nilVal = "0.0" typ = "float64" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": typ = "[]byte" case "timestamp", "datetime", "date": typ = "*time.Time" if nullable { nilVal = "mysql.NullTime{}" typ = "mysql.NullTime" } default: if strings.HasPrefix(dt, args.Schema+".") { // in the same schema, so chop off typ = internal.SnakeToIdentifier(dt[len(args.Schema)+1:]) nilVal = typ + "(0)" } else { typ = internal.SnakeToIdentifier(dt) nilVal = typ + "{}" } } // add 'u' as prefix to type if its unsigned // FIXME: this needs to be tested properly... if unsigned && internal.IntRE.MatchString(typ) { typ = "u" + typ } return precision, nilVal, typ }
// SqParseType parse a postgres type into a Go type based on the column // definition. func SqParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilVal := "nil" unsigned := false dt = strings.ToLower(dt) // extract precision dt, precision, _ = args.ParsePrecision(dt) if uRE.MatchString(dt) { unsigned = true uRE.ReplaceAllString(dt, "") } var typ string switch dt { case "bool", "boolean": nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } case "int", "integer", "tinyint", "smallint", "mediumint", "bigint": nilVal = "0" typ = args.Int32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "numeric", "real", "double", "float", "decimal": nilVal = "0.0" typ = "float64" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "blob": typ = "[]byte" default: // case "varchar", "character", "varying character", "nchar", "native character", "nvarchar", "text", "clob", "datetime", "date", "time": nilVal = `""` typ = "string" if nullable { nilVal = "sql.NullString{}" typ = "sql.NullString" } } // if unsigned ... if internal.IntRE.MatchString(typ) && unsigned { typ = "u" + typ } return precision, nilVal, typ }
// processArgs processs cli args. func processArgs(args *internal.ArgType) error { var err error // get working directory cwd, err := os.Getwd() if err != nil { return err } // determine out path if args.Out == "" { args.Path = cwd } else { // determine what to do with Out fi, err := os.Stat(args.Out) if err == nil && fi.IsDir() { // out is directory args.Path = args.Out } else if err == nil && !fi.IsDir() { // file exists (will truncate later) args.Path = path.Dir(args.Out) args.Filename = path.Base(args.Out) // error if not split was set, but destination is not a directory if !args.SingleFile { return errors.New("output path is not directory") } } else if _, ok := err.(*os.PathError); ok { // path error (ie, file doesn't exist yet) args.Path = path.Dir(args.Out) args.Filename = path.Base(args.Out) // error if split was set, but dest doesn't exist if !args.SingleFile { return errors.New("output path must be a directory and already exist when not writing to a single file") } } else { return err } } // check user template path if args.TemplatePath != "" { fi, err := os.Stat(args.TemplatePath) if err == nil && !fi.IsDir() { return errors.New("template path is not directory") } else if err != nil { return errors.New("template path must exist") } } // fix path if args.Path == "." { args.Path = cwd } // determine package name if args.Package == "" { args.Package = path.Base(args.Path) } // determine filename if not previously set if args.Filename == "" { args.Filename = args.Package + args.Suffix } // if query mode toggled, but no query, read Stdin. if args.QueryMode && args.Query == "" { buf, err := ioutil.ReadAll(os.Stdin) if err != nil { return err } args.Query = string(buf) } // query mode parsing if args.Query != "" { args.QueryMode = true } // check that query type was specified if args.QueryMode && args.QueryType == "" { return errors.New("query type must be supplied for query parsing mode") } // query trim if args.QueryMode && args.QueryTrim { args.Query = strings.TrimSpace(args.Query) } // escape all if args.EscapeAll { args.EscapeSchemaName = true args.EscapeTableNames = true args.EscapeColumnNames = true } // if verbose if args.Verbose { models.XOLog = func(s string, p ...interface{}) { fmt.Printf("SQL:\n%s\nPARAMS:\n%v\n\n", s, p) } } return nil }
// OrParseType parse a oracle type into a Go type based on the column // definition. func OrParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilVal := "nil" dt = strings.ToLower(dt) // special boolean case if dt == "char(1)" { return 0, "false", "bool" } // extract precision dt, precision, _ = args.ParsePrecision(dt) // strip remaining length (on things like timestamp) dt = OrLenRE.ReplaceAllString(dt, "") var typ string switch dt { case "char", "nchar", "varchar", "varchar2", "nvarchar2", "long", "clob", "nclob": nilVal = `""` typ = "string" if nullable { nilVal = "sql.NullString{}" typ = "sql.NullString" } case "shortint": nilVal = "0" typ = "int16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "integer": nilVal = "0" typ = args.Int32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "longinteger", "rowid": nilVal = "0" typ = "int64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "float", "shortdecimal": nilVal = "0.0" typ = "float32" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "number", "decimal": nilVal = "0.0" typ = "float64" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "blob", "long raw", "raw": typ = "[]byte" case "date", "timestamp", "timestamp with time zone": typ = "time.Time" nilVal = "time.Time{}" default: // bail fmt.Fprintf(os.Stderr, "error: unknown type %s\n", dt) os.Exit(1) } // special case for bool if typ == "int" && precision == 1 { nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } } return precision, nilVal, typ }
// MsParseType parse a postgres type into a Go type based on the column // definition. func MsParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilVal := "nil" // extract precision dt, precision, _ = args.ParsePrecision(dt) var typ string switch dt { case "tinyint", "bit": nilVal = "false" typ = "bool" if nullable { nilVal = "sql.NullBool{}" typ = "sql.NullBool" } case "char", "varchar", "text", "nchar", "nvarchar", "ntext", "smallmoney", "money": nilVal = `""` typ = "string" if nullable { nilVal = "sql.NullString{}" typ = "sql.NullString" } case "smallint": nilVal = "0" typ = "int16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "int": nilVal = "0" typ = args.Int32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigint": nilVal = "0" typ = "int64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "smallserial": nilVal = "0" typ = "uint16" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "serial": nilVal = "0" typ = args.Uint32Type if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigserial": nilVal = "0" typ = "uint64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "real": nilVal = "0.0" typ = "float32" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "numeric", "decimal": nilVal = "0.0" typ = "float64" if nullable { nilVal = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "binary", "varbinary": typ = "[]byte" case "datetime", "datetime2", "timestamp": nilVal = "time.Time{}" typ = "time.Time" case "time with time zone", "time without time zone", "timestamp without time zone": nilVal = "0" typ = "int64" if nullable { nilVal = "sql.NullInt64{}" typ = "sql.NullInt64" } case "interval": typ = "*time.Duration" default: if strings.HasPrefix(dt, args.Schema+".") { // in the same schema, so chop off typ = internal.SnakeToIdentifier(dt[len(args.Schema)+1:]) nilVal = typ + "(0)" } else { typ = internal.SnakeToIdentifier(dt) nilVal = typ + "{}" } } return precision, nilVal, typ }