github.com/wangyougui/gf/v2@v2.6.5/database/gdb/gdb.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/wangyougui/gf.
     6  
     7  // Package gdb provides ORM features for popular relationship databases.
     8  //
     9  // TODO use context.Context as required parameter for all DB operations.
    10  package gdb
    11  
    12  import (
    13  	"context"
    14  	"database/sql"
    15  	"fmt"
    16  	"time"
    17  
    18  	"github.com/wangyougui/gf/v2/container/garray"
    19  	"github.com/wangyougui/gf/v2/container/gmap"
    20  	"github.com/wangyougui/gf/v2/container/gtype"
    21  	"github.com/wangyougui/gf/v2/container/gvar"
    22  	"github.com/wangyougui/gf/v2/errors/gcode"
    23  	"github.com/wangyougui/gf/v2/errors/gerror"
    24  	"github.com/wangyougui/gf/v2/os/gcache"
    25  	"github.com/wangyougui/gf/v2/os/gcmd"
    26  	"github.com/wangyougui/gf/v2/os/gctx"
    27  	"github.com/wangyougui/gf/v2/os/glog"
    28  	"github.com/wangyougui/gf/v2/util/grand"
    29  	"github.com/wangyougui/gf/v2/util/gutil"
    30  )
    31  
    32  // DB defines the interfaces for ORM operations.
    33  type DB interface {
    34  	// ===========================================================================
    35  	// Model creation.
    36  	// ===========================================================================
    37  
    38  	// Model creates and returns a new ORM model from given schema.
    39  	// The parameter `table` can be more than one table names, and also alias name, like:
    40  	// 1. Model names:
    41  	//    Model("user")
    42  	//    Model("user u")
    43  	//    Model("user, user_detail")
    44  	//    Model("user u, user_detail ud")
    45  	// 2. Model name with alias: Model("user", "u")
    46  	// Also see Core.Model.
    47  	Model(tableNameOrStruct ...interface{}) *Model
    48  
    49  	// Raw creates and returns a model based on a raw sql not a table.
    50  	Raw(rawSql string, args ...interface{}) *Model
    51  
    52  	// Schema creates and returns a schema.
    53  	// Also see Core.Schema.
    54  	Schema(schema string) *Schema
    55  
    56  	// With creates and returns an ORM model based on metadata of given object.
    57  	// Also see Core.With.
    58  	With(objects ...interface{}) *Model
    59  
    60  	// Open creates a raw connection object for database with given node configuration.
    61  	// Note that it is not recommended using the function manually.
    62  	// Also see DriverMysql.Open.
    63  	Open(config *ConfigNode) (*sql.DB, error)
    64  
    65  	// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
    66  	// of current DB object and with given context in it.
    67  	// Also see Core.Ctx.
    68  	Ctx(ctx context.Context) DB
    69  
    70  	// Close closes the database and prevents new queries from starting.
    71  	// Close then waits for all queries that have started processing on the server
    72  	// to finish.
    73  	//
    74  	// It is rare to Close a DB, as the DB handle is meant to be
    75  	// long-lived and shared between many goroutines.
    76  	Close(ctx context.Context) error
    77  
    78  	// ===========================================================================
    79  	// Query APIs.
    80  	// ===========================================================================
    81  
    82  	Query(ctx context.Context, sql string, args ...interface{}) (Result, error)    // See Core.Query.
    83  	Exec(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) // See Core.Exec.
    84  	Prepare(ctx context.Context, sql string, execOnMaster ...bool) (*Stmt, error)  // See Core.Prepare.
    85  
    86  	// ===========================================================================
    87  	// Common APIs for CURD.
    88  	// ===========================================================================
    89  
    90  	Insert(ctx context.Context, table string, data interface{}, batch ...int) (sql.Result, error)                               // See Core.Insert.
    91  	InsertIgnore(ctx context.Context, table string, data interface{}, batch ...int) (sql.Result, error)                         // See Core.InsertIgnore.
    92  	InsertAndGetId(ctx context.Context, table string, data interface{}, batch ...int) (int64, error)                            // See Core.InsertAndGetId.
    93  	Replace(ctx context.Context, table string, data interface{}, batch ...int) (sql.Result, error)                              // See Core.Replace.
    94  	Save(ctx context.Context, table string, data interface{}, batch ...int) (sql.Result, error)                                 // See Core.Save.
    95  	Update(ctx context.Context, table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update.
    96  	Delete(ctx context.Context, table string, condition interface{}, args ...interface{}) (sql.Result, error)                   // See Core.Delete.
    97  
    98  	// ===========================================================================
    99  	// Internal APIs for CURD, which can be overwritten by custom CURD implements.
   100  	// ===========================================================================
   101  
   102  	DoSelect(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error)                                           // See Core.DoSelect.
   103  	DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error)                        // See Core.DoInsert.
   104  	DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
   105  	DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error)                   // See Core.DoDelete.
   106  
   107  	DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error)    // See Core.DoQuery.
   108  	DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
   109  
   110  	DoFilter(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoFilter.
   111  	DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutput, err error)                                            // See Core.DoCommit.
   112  
   113  	DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
   114  
   115  	// ===========================================================================
   116  	// Query APIs for convenience purpose.
   117  	// ===========================================================================
   118  
   119  	GetAll(ctx context.Context, sql string, args ...interface{}) (Result, error)                // See Core.GetAll.
   120  	GetOne(ctx context.Context, sql string, args ...interface{}) (Record, error)                // See Core.GetOne.
   121  	GetValue(ctx context.Context, sql string, args ...interface{}) (Value, error)               // See Core.GetValue.
   122  	GetArray(ctx context.Context, sql string, args ...interface{}) ([]Value, error)             // See Core.GetArray.
   123  	GetCount(ctx context.Context, sql string, args ...interface{}) (int, error)                 // See Core.GetCount.
   124  	GetScan(ctx context.Context, objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
   125  	Union(unions ...*Model) *Model                                                              // See Core.Union.
   126  	UnionAll(unions ...*Model) *Model                                                           // See Core.UnionAll.
   127  
   128  	// ===========================================================================
   129  	// Master/Slave specification support.
   130  	// ===========================================================================
   131  
   132  	Master(schema ...string) (*sql.DB, error) // See Core.Master.
   133  	Slave(schema ...string) (*sql.DB, error)  // See Core.Slave.
   134  
   135  	// ===========================================================================
   136  	// Ping-Pong.
   137  	// ===========================================================================
   138  
   139  	PingMaster() error // See Core.PingMaster.
   140  	PingSlave() error  // See Core.PingSlave.
   141  
   142  	// ===========================================================================
   143  	// Transaction.
   144  	// ===========================================================================
   145  
   146  	Begin(ctx context.Context) (TX, error)                                           // See Core.Begin.
   147  	Transaction(ctx context.Context, f func(ctx context.Context, tx TX) error) error // See Core.Transaction.
   148  
   149  	// ===========================================================================
   150  	// Configuration methods.
   151  	// ===========================================================================
   152  
   153  	GetCache() *gcache.Cache            // See Core.GetCache.
   154  	SetDebug(debug bool)                // See Core.SetDebug.
   155  	GetDebug() bool                     // See Core.GetDebug.
   156  	GetSchema() string                  // See Core.GetSchema.
   157  	GetPrefix() string                  // See Core.GetPrefix.
   158  	GetGroup() string                   // See Core.GetGroup.
   159  	SetDryRun(enabled bool)             // See Core.SetDryRun.
   160  	GetDryRun() bool                    // See Core.GetDryRun.
   161  	SetLogger(logger glog.ILogger)      // See Core.SetLogger.
   162  	GetLogger() glog.ILogger            // See Core.GetLogger.
   163  	GetConfig() *ConfigNode             // See Core.GetConfig.
   164  	SetMaxIdleConnCount(n int)          // See Core.SetMaxIdleConnCount.
   165  	SetMaxOpenConnCount(n int)          // See Core.SetMaxOpenConnCount.
   166  	SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime.
   167  
   168  	// ===========================================================================
   169  	// Utility methods.
   170  	// ===========================================================================
   171  
   172  	GetCtx() context.Context                                                                                 // See Core.GetCtx.
   173  	GetCore() *Core                                                                                          // See Core.GetCore
   174  	GetChars() (charLeft string, charRight string)                                                           // See Core.GetChars.
   175  	Tables(ctx context.Context, schema ...string) (tables []string, err error)                               // See Core.Tables. The driver must implement this function.
   176  	TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error)         // See Core.TableFields. The driver must implement this function.
   177  	ConvertValueForField(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) // See Core.ConvertValueForField
   178  	ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) // See Core.ConvertValueForLocal
   179  	CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (LocalType, error) // See Core.CheckLocalTypeForField
   180  	FormatUpsert(columns []string, list List, option DoInsertOption) (string, error)                         // See Core.DoFormatUpsert
   181  }
   182  
   183  // TX defines the interfaces for ORM transaction operations.
   184  type TX interface {
   185  	Link
   186  
   187  	Ctx(ctx context.Context) TX
   188  	Raw(rawSql string, args ...interface{}) *Model
   189  	Model(tableNameQueryOrStruct ...interface{}) *Model
   190  	With(object interface{}) *Model
   191  
   192  	// ===========================================================================
   193  	// Nested transaction if necessary.
   194  	// ===========================================================================
   195  
   196  	Begin() error
   197  	Commit() error
   198  	Rollback() error
   199  	Transaction(ctx context.Context, f func(ctx context.Context, tx TX) error) (err error)
   200  
   201  	// ===========================================================================
   202  	// Core method.
   203  	// ===========================================================================
   204  
   205  	Query(sql string, args ...interface{}) (result Result, err error)
   206  	Exec(sql string, args ...interface{}) (sql.Result, error)
   207  	Prepare(sql string) (*Stmt, error)
   208  
   209  	// ===========================================================================
   210  	// Query.
   211  	// ===========================================================================
   212  
   213  	GetAll(sql string, args ...interface{}) (Result, error)
   214  	GetOne(sql string, args ...interface{}) (Record, error)
   215  	GetStruct(obj interface{}, sql string, args ...interface{}) error
   216  	GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error
   217  	GetScan(pointer interface{}, sql string, args ...interface{}) error
   218  	GetValue(sql string, args ...interface{}) (Value, error)
   219  	GetCount(sql string, args ...interface{}) (int64, error)
   220  
   221  	// ===========================================================================
   222  	// CURD.
   223  	// ===========================================================================
   224  
   225  	Insert(table string, data interface{}, batch ...int) (sql.Result, error)
   226  	InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error)
   227  	InsertAndGetId(table string, data interface{}, batch ...int) (int64, error)
   228  	Replace(table string, data interface{}, batch ...int) (sql.Result, error)
   229  	Save(table string, data interface{}, batch ...int) (sql.Result, error)
   230  	Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
   231  	Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
   232  
   233  	// ===========================================================================
   234  	// Utility methods.
   235  	// ===========================================================================
   236  
   237  	GetCtx() context.Context
   238  	GetDB() DB
   239  	GetSqlTX() *sql.Tx
   240  	IsClosed() bool
   241  
   242  	// ===========================================================================
   243  	// Save point feature.
   244  	// ===========================================================================
   245  
   246  	SavePoint(point string) error
   247  	RollbackTo(point string) error
   248  }
   249  
   250  // Core is the base struct for database management.
   251  type Core struct {
   252  	db            DB              // DB interface object.
   253  	ctx           context.Context // Context for chaining operation only. Do not set a default value in Core initialization.
   254  	group         string          // Configuration group name.
   255  	schema        string          // Custom schema for this object.
   256  	debug         *gtype.Bool     // Enable debug mode for the database, which can be changed in runtime.
   257  	cache         *gcache.Cache   // Cache manager, SQL result cache only.
   258  	links         *gmap.StrAnyMap // links caches all created links by node.
   259  	logger        glog.ILogger    // Logger for logging functionality.
   260  	config        *ConfigNode     // Current config node.
   261  	dynamicConfig dynamicConfig   // Dynamic configurations, which can be changed in runtime.
   262  }
   263  
   264  type dynamicConfig struct {
   265  	MaxIdleConnCount int
   266  	MaxOpenConnCount int
   267  	MaxConnLifeTime  time.Duration
   268  }
   269  
   270  // DoCommitInput is the input parameters for function DoCommit.
   271  type DoCommitInput struct {
   272  	Db            *sql.DB
   273  	Tx            *sql.Tx
   274  	Stmt          *sql.Stmt
   275  	Link          Link
   276  	Sql           string
   277  	Args          []interface{}
   278  	Type          string
   279  	IsTransaction bool
   280  }
   281  
   282  // DoCommitOutput is the output parameters for function DoCommit.
   283  type DoCommitOutput struct {
   284  	Result    sql.Result  // Result is the result of exec statement.
   285  	Records   []Record    // Records is the result of query statement.
   286  	Stmt      *Stmt       // Stmt is the Statement object result for Prepare.
   287  	Tx        TX          // Tx is the transaction object result for Begin.
   288  	RawResult interface{} // RawResult is the underlying result, which might be sql.Result/*sql.Rows/*sql.Row.
   289  }
   290  
   291  // Driver is the interface for integrating sql drivers into package gdb.
   292  type Driver interface {
   293  	// New creates and returns a database object for specified database server.
   294  	New(core *Core, node *ConfigNode) (DB, error)
   295  }
   296  
   297  // Link is a common database function wrapper interface.
   298  // Note that, any operation using `Link` will have no SQL logging.
   299  type Link interface {
   300  	QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error)
   301  	ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error)
   302  	PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error)
   303  	IsOnMaster() bool
   304  	IsTransaction() bool
   305  }
   306  
   307  // Sql is the sql recording struct.
   308  type Sql struct {
   309  	Sql           string        // SQL string(may contain reserved char '?').
   310  	Type          string        // SQL operation type.
   311  	Args          []interface{} // Arguments for this sql.
   312  	Format        string        // Formatted sql which contains arguments in the sql.
   313  	Error         error         // Execution result.
   314  	Start         int64         // Start execution timestamp in milliseconds.
   315  	End           int64         // End execution timestamp in milliseconds.
   316  	Group         string        // Group is the group name of the configuration that the sql is executed from.
   317  	Schema        string        // Schema is the schema name of the configuration that the sql is executed from.
   318  	IsTransaction bool          // IsTransaction marks whether this sql is executed in transaction.
   319  	RowsAffected  int64         // RowsAffected marks retrieved or affected number with current sql statement.
   320  }
   321  
   322  // DoInsertOption is the input struct for function DoInsert.
   323  type DoInsertOption struct {
   324  	OnDuplicateStr string                 // Custom string for `on duplicated` statement.
   325  	OnDuplicateMap map[string]interface{} // Custom key-value map from `OnDuplicateEx` function for `on duplicated` statement.
   326  	OnConflict     []string               // Custom conflict key of upsert clause, if the database needs it.
   327  	InsertOption   InsertOption           // Insert operation in constant value.
   328  	BatchCount     int                    // Batch count for batch inserting.
   329  }
   330  
   331  // TableField is the struct for table field.
   332  type TableField struct {
   333  	Index   int         // For ordering purpose as map is unordered.
   334  	Name    string      // Field name.
   335  	Type    string      // Field type. Eg: 'int(10) unsigned', 'varchar(64)'.
   336  	Null    bool        // Field can be null or not.
   337  	Key     string      // The index information(empty if it's not an index). Eg: PRI, MUL.
   338  	Default interface{} // Default value for the field.
   339  	Extra   string      // Extra information. Eg: auto_increment.
   340  	Comment string      // Field comment.
   341  }
   342  
   343  // Counter  is the type for update count.
   344  type Counter struct {
   345  	Field string
   346  	Value float64
   347  }
   348  
   349  type (
   350  	Raw    string                   // Raw is a raw sql that will not be treated as argument but as a direct sql part.
   351  	Value  = *gvar.Var              // Value is the field value type.
   352  	Record map[string]Value         // Record is the row record of the table.
   353  	Result []Record                 // Result is the row record array.
   354  	Map    = map[string]interface{} // Map is alias of map[string]interface{}, which is the most common usage map type.
   355  	List   = []Map                  // List is type of map array.
   356  )
   357  
   358  type CatchSQLManager struct {
   359  	SQLArray *garray.StrArray
   360  	DoCommit bool // DoCommit marks it will be committed to underlying driver or not.
   361  }
   362  
   363  const (
   364  	defaultModelSafe                      = false
   365  	defaultCharset                        = `utf8`
   366  	defaultProtocol                       = `tcp`
   367  	unionTypeNormal                       = 0
   368  	unionTypeAll                          = 1
   369  	defaultMaxIdleConnCount               = 10               // Max idle connection count in pool.
   370  	defaultMaxOpenConnCount               = 0                // Max open connection count in pool. Default is no limit.
   371  	defaultMaxConnLifeTime                = 30 * time.Second // Max lifetime for per connection in pool in seconds.
   372  	ctxTimeoutTypeExec                    = 0
   373  	ctxTimeoutTypeQuery                   = 1
   374  	ctxTimeoutTypePrepare                 = 2
   375  	cachePrefixTableFields                = `TableFields:`
   376  	cachePrefixSelectCache                = `SelectCache:`
   377  	commandEnvKeyForDryRun                = "gf.gdb.dryrun"
   378  	modelForDaoSuffix                     = `ForDao`
   379  	dbRoleSlave                           = `slave`
   380  	ctxKeyForDB               gctx.StrKey = `CtxKeyForDB`
   381  	ctxKeyCatchSQL            gctx.StrKey = `CtxKeyCatchSQL`
   382  	ctxKeyInternalProducedSQL gctx.StrKey = `CtxKeyInternalProducedSQL`
   383  
   384  	// type:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]
   385  	linkPattern = `(\w+):([\w\-\$]*):(.*?)@(\w+?)\((.+?)\)/{0,1}([^\?]*)\?{0,1}(.*)`
   386  )
   387  
   388  type queryType int
   389  
   390  const (
   391  	queryTypeNormal queryType = iota
   392  	queryTypeCount
   393  	queryTypeValue
   394  )
   395  
   396  type joinOperator string
   397  
   398  const (
   399  	joinOperatorLeft  joinOperator = "LEFT"
   400  	joinOperatorRight joinOperator = "RIGHT"
   401  	joinOperatorInner joinOperator = "INNER"
   402  )
   403  
   404  type InsertOption int
   405  
   406  const (
   407  	InsertOptionDefault InsertOption = iota
   408  	InsertOptionReplace
   409  	InsertOptionSave
   410  	InsertOptionIgnore
   411  )
   412  
   413  const (
   414  	InsertOperationInsert      = "INSERT"
   415  	InsertOperationReplace     = "REPLACE"
   416  	InsertOperationIgnore      = "INSERT IGNORE"
   417  	InsertOnDuplicateKeyUpdate = "ON DUPLICATE KEY UPDATE"
   418  )
   419  
   420  const (
   421  	SqlTypeBegin               = "DB.Begin"
   422  	SqlTypeTXCommit            = "TX.Commit"
   423  	SqlTypeTXRollback          = "TX.Rollback"
   424  	SqlTypeExecContext         = "DB.ExecContext"
   425  	SqlTypeQueryContext        = "DB.QueryContext"
   426  	SqlTypePrepareContext      = "DB.PrepareContext"
   427  	SqlTypeStmtExecContext     = "DB.Statement.ExecContext"
   428  	SqlTypeStmtQueryContext    = "DB.Statement.QueryContext"
   429  	SqlTypeStmtQueryRowContext = "DB.Statement.QueryRowContext"
   430  )
   431  
   432  type LocalType string
   433  
   434  const (
   435  	LocalTypeUndefined   LocalType = ""
   436  	LocalTypeString      LocalType = "string"
   437  	LocalTypeDate        LocalType = "date"
   438  	LocalTypeDatetime    LocalType = "datetime"
   439  	LocalTypeInt         LocalType = "int"
   440  	LocalTypeUint        LocalType = "uint"
   441  	LocalTypeInt64       LocalType = "int64"
   442  	LocalTypeUint64      LocalType = "uint64"
   443  	LocalTypeIntSlice    LocalType = "[]int"
   444  	LocalTypeInt64Slice  LocalType = "[]int64"
   445  	LocalTypeUint64Slice LocalType = "[]uint64"
   446  	LocalTypeInt64Bytes  LocalType = "int64-bytes"
   447  	LocalTypeUint64Bytes LocalType = "uint64-bytes"
   448  	LocalTypeFloat32     LocalType = "float32"
   449  	LocalTypeFloat64     LocalType = "float64"
   450  	LocalTypeBytes       LocalType = "[]byte"
   451  	LocalTypeBool        LocalType = "bool"
   452  	LocalTypeJson        LocalType = "json"
   453  	LocalTypeJsonb       LocalType = "jsonb"
   454  )
   455  
   456  const (
   457  	fieldTypeBinary     = "binary"
   458  	fieldTypeVarbinary  = "varbinary"
   459  	fieldTypeBlob       = "blob"
   460  	fieldTypeTinyblob   = "tinyblob"
   461  	fieldTypeMediumblob = "mediumblob"
   462  	fieldTypeLongblob   = "longblob"
   463  	fieldTypeInt        = "int"
   464  	fieldTypeTinyint    = "tinyint"
   465  	fieldTypeSmallInt   = "small_int"
   466  	fieldTypeSmallint   = "smallint"
   467  	fieldTypeMediumInt  = "medium_int"
   468  	fieldTypeMediumint  = "mediumint"
   469  	fieldTypeSerial     = "serial"
   470  	fieldTypeBigInt     = "big_int"
   471  	fieldTypeBigint     = "bigint"
   472  	fieldTypeBigserial  = "bigserial"
   473  	fieldTypeReal       = "real"
   474  	fieldTypeFloat      = "float"
   475  	fieldTypeDouble     = "double"
   476  	fieldTypeDecimal    = "decimal"
   477  	fieldTypeMoney      = "money"
   478  	fieldTypeNumeric    = "numeric"
   479  	fieldTypeSmallmoney = "smallmoney"
   480  	fieldTypeBool       = "bool"
   481  	fieldTypeBit        = "bit"
   482  	fieldTypeDate       = "date"
   483  	fieldTypeDatetime   = "datetime"
   484  	fieldTypeTimestamp  = "timestamp"
   485  	fieldTypeTimestampz = "timestamptz"
   486  	fieldTypeJson       = "json"
   487  	fieldTypeJsonb      = "jsonb"
   488  )
   489  
   490  var (
   491  	// instances is the management map for instances.
   492  	instances = gmap.NewStrAnyMap(true)
   493  
   494  	// driverMap manages all custom registered driver.
   495  	driverMap = map[string]Driver{}
   496  
   497  	// lastOperatorRegPattern is the regular expression pattern for a string
   498  	// which has operator at its tail.
   499  	lastOperatorRegPattern = `[<>=]+\s*$`
   500  
   501  	// regularFieldNameRegPattern is the regular expression pattern for a string
   502  	// which is a regular field name of table.
   503  	regularFieldNameRegPattern = `^[\w\.\-]+$`
   504  
   505  	// regularFieldNameWithCommaRegPattern is the regular expression pattern for one or more strings
   506  	// which are regular field names of table, multiple field names joined with char ','.
   507  	regularFieldNameWithCommaRegPattern = `^[\w\.\-,\s]+$`
   508  
   509  	// regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'.
   510  	// Note that, although some databases allow char '.' in the field name, but it here does not allow '.'
   511  	// in the field name as it conflicts with "db.table.field" pattern in SOME situations.
   512  	regularFieldNameWithoutDotRegPattern = `^[\w\-]+$`
   513  
   514  	// allDryRun sets dry-run feature for all database connections.
   515  	// It is commonly used for command options for convenience.
   516  	allDryRun = false
   517  
   518  	// tableFieldsMap caches the table information retrieved from database.
   519  	tableFieldsMap = gmap.NewStrAnyMap(true)
   520  )
   521  
   522  func init() {
   523  	// allDryRun is initialized from environment or command options.
   524  	allDryRun = gcmd.GetOptWithEnv(commandEnvKeyForDryRun, false).Bool()
   525  }
   526  
   527  // Register registers custom database driver to gdb.
   528  func Register(name string, driver Driver) error {
   529  	driverMap[name] = newDriverWrapper(driver)
   530  	return nil
   531  }
   532  
   533  // New creates and returns an ORM object with given configuration node.
   534  func New(node ConfigNode) (db DB, err error) {
   535  	return newDBByConfigNode(&node, "")
   536  }
   537  
   538  // NewByGroup creates and returns an ORM object with global configurations.
   539  // The parameter `name` specifies the configuration group name,
   540  // which is DefaultGroupName in default.
   541  func NewByGroup(group ...string) (db DB, err error) {
   542  	groupName := configs.group
   543  	if len(group) > 0 && group[0] != "" {
   544  		groupName = group[0]
   545  	}
   546  	configs.RLock()
   547  	defer configs.RUnlock()
   548  
   549  	if len(configs.config) < 1 {
   550  		return nil, gerror.NewCode(
   551  			gcode.CodeInvalidConfiguration,
   552  			"database configuration is empty, please set the database configuration before using",
   553  		)
   554  	}
   555  	if _, ok := configs.config[groupName]; ok {
   556  		var node *ConfigNode
   557  		if node, err = getConfigNodeByGroup(groupName, true); err == nil {
   558  			return newDBByConfigNode(node, groupName)
   559  		}
   560  		return nil, err
   561  	}
   562  	return nil, gerror.NewCodef(
   563  		gcode.CodeInvalidConfiguration,
   564  		`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
   565  		groupName, groupName,
   566  	)
   567  }
   568  
   569  // newDBByConfigNode creates and returns an ORM object with given configuration node and group name.
   570  //
   571  // Very Note:
   572  // The parameter `node` is used for DB creation, not for underlying connection creation.
   573  // So all db type configurations in the same group should be the same.
   574  func newDBByConfigNode(node *ConfigNode, group string) (db DB, err error) {
   575  	if node.Link != "" {
   576  		node = parseConfigNodeLink(node)
   577  	}
   578  	c := &Core{
   579  		group:  group,
   580  		debug:  gtype.NewBool(),
   581  		cache:  gcache.New(),
   582  		links:  gmap.NewStrAnyMap(true),
   583  		logger: glog.New(),
   584  		config: node,
   585  		dynamicConfig: dynamicConfig{
   586  			MaxIdleConnCount: node.MaxIdleConnCount,
   587  			MaxOpenConnCount: node.MaxOpenConnCount,
   588  			MaxConnLifeTime:  node.MaxConnLifeTime,
   589  		},
   590  	}
   591  	if v, ok := driverMap[node.Type]; ok {
   592  		if c.db, err = v.New(c, node); err != nil {
   593  			return nil, err
   594  		}
   595  		return c.db, nil
   596  	}
   597  	errorMsg := `cannot find database driver for specified database type "%s"`
   598  	errorMsg += `, did you misspell type name "%s" or forget importing the database driver? `
   599  	errorMsg += `possible reference: https://github.com/wangyougui/gf/tree/master/contrib/drivers`
   600  	return nil, gerror.NewCodef(gcode.CodeInvalidConfiguration, errorMsg, node.Type, node.Type)
   601  }
   602  
   603  // Instance returns an instance for DB operations.
   604  // The parameter `name` specifies the configuration group name,
   605  // which is DefaultGroupName in default.
   606  func Instance(name ...string) (db DB, err error) {
   607  	group := configs.group
   608  	if len(name) > 0 && name[0] != "" {
   609  		group = name[0]
   610  	}
   611  	v := instances.GetOrSetFuncLock(group, func() interface{} {
   612  		db, err = NewByGroup(group)
   613  		return db
   614  	})
   615  	if v != nil {
   616  		return v.(DB), nil
   617  	}
   618  	return
   619  }
   620  
   621  // getConfigNodeByGroup calculates and returns a configuration node of given group. It
   622  // calculates the value internally using weight algorithm for load balance.
   623  //
   624  // The parameter `master` specifies whether retrieving a master node, or else a slave node
   625  // if master-slave configured.
   626  func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
   627  	if list, ok := configs.config[group]; ok {
   628  		// Separates master and slave configuration nodes array.
   629  		var (
   630  			masterList = make(ConfigGroup, 0)
   631  			slaveList  = make(ConfigGroup, 0)
   632  		)
   633  		for i := 0; i < len(list); i++ {
   634  			if list[i].Role == dbRoleSlave {
   635  				slaveList = append(slaveList, list[i])
   636  			} else {
   637  				masterList = append(masterList, list[i])
   638  			}
   639  		}
   640  		if len(masterList) < 1 {
   641  			return nil, gerror.NewCode(
   642  				gcode.CodeInvalidConfiguration,
   643  				"at least one master node configuration's need to make sense",
   644  			)
   645  		}
   646  		if len(slaveList) < 1 {
   647  			slaveList = masterList
   648  		}
   649  		if master {
   650  			return getConfigNodeByWeight(masterList), nil
   651  		} else {
   652  			return getConfigNodeByWeight(slaveList), nil
   653  		}
   654  	}
   655  	return nil, gerror.NewCodef(
   656  		gcode.CodeInvalidConfiguration,
   657  		"empty database configuration for item name '%s'",
   658  		group,
   659  	)
   660  }
   661  
   662  // getConfigNodeByWeight calculates the configuration weights and randomly returns a node.
   663  //
   664  // Calculation algorithm brief:
   665  // 1. If we have 2 nodes, and their weights are both 1, then the weight range is [0, 199];
   666  // 2. Node1 weight range is [0, 99], and node2 weight range is [100, 199], ratio is 1:1;
   667  // 3. If the random number is 99, it then chooses and returns node1;.
   668  func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
   669  	if len(cg) < 2 {
   670  		return &cg[0]
   671  	}
   672  	var total int
   673  	for i := 0; i < len(cg); i++ {
   674  		total += cg[i].Weight * 100
   675  	}
   676  	// If total is 0 means all the nodes have no weight attribute configured.
   677  	// It then defaults each node's weight attribute to 1.
   678  	if total == 0 {
   679  		for i := 0; i < len(cg); i++ {
   680  			cg[i].Weight = 1
   681  			total += cg[i].Weight * 100
   682  		}
   683  	}
   684  	// Exclude the right border value.
   685  	var (
   686  		min    = 0
   687  		max    = 0
   688  		random = grand.N(0, total-1)
   689  	)
   690  	for i := 0; i < len(cg); i++ {
   691  		max = min + cg[i].Weight*100
   692  		if random >= min && random < max {
   693  			// ====================================================
   694  			// Return a COPY of the ConfigNode.
   695  			// ====================================================
   696  			node := ConfigNode{}
   697  			node = cg[i]
   698  			return &node
   699  		}
   700  		min = max
   701  	}
   702  	return nil
   703  }
   704  
   705  // getSqlDb retrieves and returns an underlying database connection object.
   706  // The parameter `master` specifies whether retrieves master node connection if
   707  // master-slave nodes are configured.
   708  func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
   709  	var (
   710  		node *ConfigNode
   711  		ctx  = c.db.GetCtx()
   712  	)
   713  	if c.group != "" {
   714  		// Load balance.
   715  		configs.RLock()
   716  		defer configs.RUnlock()
   717  		// Value COPY for node.
   718  		node, err = getConfigNodeByGroup(c.group, master)
   719  		if err != nil {
   720  			return nil, err
   721  		}
   722  	} else {
   723  		// Value COPY for node.
   724  		n := *c.db.GetConfig()
   725  		node = &n
   726  	}
   727  	if node.Charset == "" {
   728  		node.Charset = defaultCharset
   729  	}
   730  	// Changes the schema.
   731  	nodeSchema := gutil.GetOrDefaultStr(c.schema, schema...)
   732  	if nodeSchema != "" {
   733  		node.Name = nodeSchema
   734  	}
   735  	// Update the configuration object in internal data.
   736  	internalData := c.GetInternalCtxDataFromCtx(ctx)
   737  	if internalData != nil {
   738  		internalData.ConfigNode = node
   739  	}
   740  	// Cache the underlying connection pool object by node.
   741  	instanceNameByNode := fmt.Sprintf(`%+v`, node)
   742  	instanceValue := c.links.GetOrSetFuncLock(instanceNameByNode, func() interface{} {
   743  		if sqlDb, err = c.db.Open(node); err != nil {
   744  			return nil
   745  		}
   746  		if sqlDb == nil {
   747  			return nil
   748  		}
   749  		if c.dynamicConfig.MaxIdleConnCount > 0 {
   750  			sqlDb.SetMaxIdleConns(c.dynamicConfig.MaxIdleConnCount)
   751  		} else {
   752  			sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount)
   753  		}
   754  		if c.dynamicConfig.MaxOpenConnCount > 0 {
   755  			sqlDb.SetMaxOpenConns(c.dynamicConfig.MaxOpenConnCount)
   756  		} else {
   757  			sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount)
   758  		}
   759  		if c.dynamicConfig.MaxConnLifeTime > 0 {
   760  			sqlDb.SetConnMaxLifetime(c.dynamicConfig.MaxConnLifeTime)
   761  		} else {
   762  			sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
   763  		}
   764  		return sqlDb
   765  	})
   766  	if instanceValue != nil && sqlDb == nil {
   767  		// It reads from instance map.
   768  		sqlDb = instanceValue.(*sql.DB)
   769  	}
   770  	if node.Debug {
   771  		c.db.SetDebug(node.Debug)
   772  	}
   773  	if node.DryRun {
   774  		c.db.SetDryRun(node.DryRun)
   775  	}
   776  	return
   777  }