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