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