github.com/gogf/gf@v1.16.9/database/gdb/gdb_driver_mysql.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gdb 8 9 import ( 10 "context" 11 "database/sql" 12 "fmt" 13 "github.com/gogf/gf/errors/gcode" 14 "github.com/gogf/gf/errors/gerror" 15 "github.com/gogf/gf/internal/intlog" 16 "github.com/gogf/gf/text/gregex" 17 "github.com/gogf/gf/text/gstr" 18 "net/url" 19 20 _ "github.com/go-sql-driver/mysql" 21 ) 22 23 // DriverMysql is the driver for mysql database. 24 type DriverMysql struct { 25 *Core 26 } 27 28 // New creates and returns a database object for mysql. 29 // It implements the interface of gdb.Driver for extra database driver installation. 30 func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) { 31 return &DriverMysql{ 32 Core: core, 33 }, nil 34 } 35 36 // Open creates and returns an underlying sql.DB object for mysql. 37 // Note that it converts time.Time argument to local timezone in default. 38 func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { 39 var source string 40 if config.Link != "" { 41 source = config.Link 42 // Custom changing the schema in runtime. 43 if config.Name != "" { 44 source, _ = gregex.ReplaceString(`/([\w\.\-]+)+`, "/"+config.Name, source) 45 } 46 } else { 47 source = fmt.Sprintf( 48 "%s:%s@tcp(%s:%s)/%s?charset=%s", 49 config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, 50 ) 51 if config.Timezone != "" { 52 source = fmt.Sprintf("%s&loc=%s", source, url.QueryEscape(config.Timezone)) 53 } 54 } 55 intlog.Printf(d.GetCtx(), "Open: %s", source) 56 if db, err := sql.Open("mysql", source); err == nil { 57 return db, nil 58 } else { 59 return nil, err 60 } 61 } 62 63 // FilteredLink retrieves and returns filtered `linkInfo` that can be using for 64 // logging or tracing purpose. 65 func (d *DriverMysql) FilteredLink() string { 66 linkInfo := d.GetConfig().Link 67 if linkInfo == "" { 68 return "" 69 } 70 s, _ := gregex.ReplaceString( 71 `(.+?):(.+)@tcp(.+)`, 72 `$1:xxx@tcp$3`, 73 linkInfo, 74 ) 75 return s 76 } 77 78 // GetChars returns the security char for this type of database. 79 func (d *DriverMysql) GetChars() (charLeft string, charRight string) { 80 return "`", "`" 81 } 82 83 // DoCommit handles the sql before posts it to database. 84 func (d *DriverMysql) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) { 85 return d.Core.DoCommit(ctx, link, sql, args) 86 } 87 88 // Tables retrieves and returns the tables of current schema. 89 // It's mainly used in cli tool chain for automatically generating the models. 90 func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []string, err error) { 91 var result Result 92 link, err := d.SlaveLink(schema...) 93 if err != nil { 94 return nil, err 95 } 96 result, err = d.DoGetAll(ctx, link, `SHOW TABLES`) 97 if err != nil { 98 return 99 } 100 for _, m := range result { 101 for _, v := range m { 102 tables = append(tables, v.String()) 103 } 104 } 105 return 106 } 107 108 // TableFields retrieves and returns the fields information of specified table of current 109 // schema. 110 // 111 // The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection 112 // as its link to proceed necessary sql query. 113 // 114 // Note that it returns a map containing the field name and its corresponding fields. 115 // As a map is unsorted, the TableField struct has a "Index" field marks its sequence in 116 // the fields. 117 // 118 // It's using cache feature to enhance the performance, which is never expired util the 119 // process restarts. 120 func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) { 121 charL, charR := d.GetChars() 122 table = gstr.Trim(table, charL+charR) 123 if gstr.Contains(table, " ") { 124 return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations") 125 } 126 useSchema := d.schema.Val() 127 if len(schema) > 0 && schema[0] != "" { 128 useSchema = schema[0] 129 } 130 tableFieldsCacheKey := fmt.Sprintf( 131 `mysql_table_fields_%s_%s@group:%s`, 132 table, useSchema, d.GetGroup(), 133 ) 134 v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} { 135 var ( 136 result Result 137 link, err = d.SlaveLink(useSchema) 138 ) 139 if err != nil { 140 return nil 141 } 142 result, err = d.DoGetAll(ctx, link, fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table))) 143 if err != nil { 144 return nil 145 } 146 fields = make(map[string]*TableField) 147 for i, m := range result { 148 fields[m["Field"].String()] = &TableField{ 149 Index: i, 150 Name: m["Field"].String(), 151 Type: m["Type"].String(), 152 Null: m["Null"].Bool(), 153 Key: m["Key"].String(), 154 Default: m["Default"].Val(), 155 Extra: m["Extra"].String(), 156 Comment: m["Comment"].String(), 157 } 158 } 159 return fields 160 }) 161 if v != nil { 162 fields = v.(map[string]*TableField) 163 } 164 return 165 }