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&...¶mN=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 }