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  }