github.com/gogf/gf@v1.16.9/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 package gdb 9 10 import ( 11 "context" 12 "database/sql" 13 "github.com/gogf/gf/errors/gcode" 14 "time" 15 16 "github.com/gogf/gf/errors/gerror" 17 "github.com/gogf/gf/os/gcmd" 18 19 "github.com/gogf/gf/container/gvar" 20 "github.com/gogf/gf/internal/intlog" 21 22 "github.com/gogf/gf/os/glog" 23 24 "github.com/gogf/gf/container/gmap" 25 "github.com/gogf/gf/container/gtype" 26 "github.com/gogf/gf/os/gcache" 27 "github.com/gogf/gf/util/grand" 28 ) 29 30 // DB defines the interfaces for ORM operations. 31 type DB interface { 32 // =========================================================================== 33 // Model creation. 34 // =========================================================================== 35 36 // Table function is deprecated, use Model instead. 37 // The DB interface is designed not only for 38 // relational databases but also for NoSQL databases in the future. The name 39 // "Table" is not proper for that purpose any more. 40 // Also see Core.Table. 41 // Deprecated. 42 Table(tableNameOrStruct ...interface{}) *Model 43 44 // Model creates and returns a new ORM model from given schema. 45 // The parameter `table` can be more than one table names, and also alias name, like: 46 // 1. Model names: 47 // Model("user") 48 // Model("user u") 49 // Model("user, user_detail") 50 // Model("user u, user_detail ud") 51 // 2. Model name with alias: Model("user", "u") 52 // Also see Core.Model. 53 Model(tableNameOrStruct ...interface{}) *Model 54 55 // Raw creates and returns a model based on a raw sql not a table. 56 Raw(rawSql string, args ...interface{}) *Model 57 58 // Schema creates and returns a schema. 59 // Also see Core.Schema. 60 Schema(schema string) *Schema 61 62 // With creates and returns an ORM model based on meta data of given object. 63 // Also see Core.With. 64 With(objects ...interface{}) *Model 65 66 // Open creates a raw connection object for database with given node configuration. 67 // Note that it is not recommended using the this function manually. 68 // Also see DriverMysql.Open. 69 Open(config *ConfigNode) (*sql.DB, error) 70 71 // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy 72 // of current DB object and with given context in it. 73 // Also see Core.Ctx. 74 Ctx(ctx context.Context) DB 75 76 // Close closes the database and prevents new queries from starting. 77 // Close then waits for all queries that have started processing on the server 78 // to finish. 79 // 80 // It is rare to Close a DB, as the DB handle is meant to be 81 // long-lived and shared between many goroutines. 82 Close(ctx context.Context) error 83 84 // =========================================================================== 85 // Query APIs. 86 // =========================================================================== 87 88 Query(sql string, args ...interface{}) (*sql.Rows, error) // See Core.Query. 89 Exec(sql string, args ...interface{}) (sql.Result, error) // See Core.Exec. 90 Prepare(sql string, execOnMaster ...bool) (*Stmt, error) // See Core.Prepare. 91 92 // =========================================================================== 93 // Common APIs for CURD. 94 // =========================================================================== 95 96 Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert. 97 InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore. 98 InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId. 99 Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace. 100 Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save. 101 Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update. 102 Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete. 103 104 // =========================================================================== 105 // Internal APIs for CURD, which can be overwritten by custom CURD implements. 106 // =========================================================================== 107 108 DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll. 109 DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert. 110 DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate. 111 DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete. 112 DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery. 113 DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec. 114 DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoCommit. 115 DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare. 116 117 // =========================================================================== 118 // Query APIs for convenience purpose. 119 // =========================================================================== 120 121 GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll. 122 GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne. 123 GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue. 124 GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray. 125 GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount. 126 GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan. 127 Union(unions ...*Model) *Model // See Core.Union. 128 UnionAll(unions ...*Model) *Model // See Core.UnionAll. 129 130 // =========================================================================== 131 // Master/Slave specification support. 132 // =========================================================================== 133 134 Master(schema ...string) (*sql.DB, error) // See Core.Master. 135 Slave(schema ...string) (*sql.DB, error) // See Core.Slave. 136 137 // =========================================================================== 138 // Ping-Pong. 139 // =========================================================================== 140 141 PingMaster() error // See Core.PingMaster. 142 PingSlave() error // See Core.PingSlave. 143 144 // =========================================================================== 145 // Transaction. 146 // =========================================================================== 147 148 Begin() (*TX, error) // See Core.Begin. 149 Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) error // See Core.Transaction. 150 151 // =========================================================================== 152 // Configuration methods. 153 // =========================================================================== 154 155 GetCache() *gcache.Cache // See Core.GetCache. 156 SetDebug(debug bool) // See Core.SetDebug. 157 GetDebug() bool // See Core.GetDebug. 158 SetSchema(schema string) // See Core.SetSchema. 159 GetSchema() string // See Core.GetSchema. 160 GetPrefix() string // See Core.GetPrefix. 161 GetGroup() string // See Core.GetGroup. 162 SetDryRun(enabled bool) // See Core.SetDryRun. 163 GetDryRun() bool // See Core.GetDryRun. 164 SetLogger(logger *glog.Logger) // See Core.SetLogger. 165 GetLogger() *glog.Logger // See Core.GetLogger. 166 GetConfig() *ConfigNode // See Core.GetConfig. 167 SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount. 168 SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount. 169 SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime. 170 171 // =========================================================================== 172 // Utility methods. 173 // =========================================================================== 174 175 GetCtx() context.Context // See Core.GetCtx. 176 GetCore() *Core // See Core.GetCore 177 GetChars() (charLeft string, charRight string) // See Core.GetChars. 178 Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables. 179 TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields. 180 FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server. 181 } 182 183 // Core is the base struct for database management. 184 type Core struct { 185 db DB // DB interface object. 186 ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization. 187 group string // Configuration group name. 188 debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime. 189 cache *gcache.Cache // Cache manager, SQL result cache only. 190 links *gmap.StrAnyMap // links caches all created links by node. 191 schema *gtype.String // Custom schema for this object. 192 logger *glog.Logger // Logger for logging functionality. 193 config *ConfigNode // Current config node. 194 } 195 196 // Driver is the interface for integrating sql drivers into package gdb. 197 type Driver interface { 198 // New creates and returns a database object for specified database server. 199 New(core *Core, node *ConfigNode) (DB, error) 200 } 201 202 // Link is a common database function wrapper interface. 203 type Link interface { 204 Query(sql string, args ...interface{}) (*sql.Rows, error) 205 Exec(sql string, args ...interface{}) (sql.Result, error) 206 Prepare(sql string) (*sql.Stmt, error) 207 QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error) 208 ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error) 209 PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error) 210 IsTransaction() bool 211 } 212 213 // Logger is the logging interface for DB. 214 type Logger interface { 215 Error(ctx context.Context, s string) 216 Debug(ctx context.Context, s string) 217 } 218 219 // Sql is the sql recording struct. 220 type Sql struct { 221 Sql string // SQL string(may contain reserved char '?'). 222 Type string // SQL operation type. 223 Args []interface{} // Arguments for this sql. 224 Format string // Formatted sql which contains arguments in the sql. 225 Error error // Execution result. 226 Start int64 // Start execution timestamp in milliseconds. 227 End int64 // End execution timestamp in milliseconds. 228 Group string // Group is the group name of the configuration that the sql is executed from. 229 IsTransaction bool // IsTransaction marks whether this sql is executed in transaction. 230 } 231 232 // DoInsertOption is the input struct for function DoInsert. 233 type DoInsertOption struct { 234 OnDuplicateStr string 235 OnDuplicateMap map[string]interface{} 236 InsertOption int // Insert operation. 237 BatchCount int // Batch count for batch inserting. 238 } 239 240 // TableField is the struct for table field. 241 type TableField struct { 242 Index int // For ordering purpose as map is unordered. 243 Name string // Field name. 244 Type string // Field type. 245 Null bool // Field can be null or not. 246 Key string // The index information(empty if it's not a index). 247 Default interface{} // Default value for the field. 248 Extra string // Extra information. 249 Comment string // Comment. 250 } 251 252 // Counter is the type for update count. 253 type Counter struct { 254 Field string 255 Value float64 256 } 257 258 type ( 259 Raw string // Raw is a raw sql that will not be treated as argument but as a direct sql part. 260 Value = *gvar.Var // Value is the field value type. 261 Record map[string]Value // Record is the row record of the table. 262 Result []Record // Result is the row record array. 263 Map = map[string]interface{} // Map is alias of map[string]interface{}, which is the most common usage map type. 264 List = []Map // List is type of map array. 265 ) 266 267 const ( 268 queryTypeNormal = 0 269 queryTypeCount = 1 270 unionTypeNormal = 0 271 unionTypeAll = 1 272 insertOptionDefault = 0 273 insertOptionReplace = 1 274 insertOptionSave = 2 275 insertOptionIgnore = 3 276 defaultBatchNumber = 10 // Per count for batch insert/replace/save. 277 defaultMaxIdleConnCount = 10 // Max idle connection count in pool. 278 defaultMaxOpenConnCount = 100 // Max open connection count in pool. 279 defaultMaxConnLifeTime = 30 * time.Second // Max life time for per connection in pool in seconds. 280 ctxTimeoutTypeExec = iota 281 ctxTimeoutTypeQuery 282 ctxTimeoutTypePrepare 283 commandEnvKeyForDryRun = "gf.gdb.dryrun" 284 ctxStrictKeyName = "gf.gdb.CtxStrictEnabled" 285 ctxStrictErrorStr = "context is required for database operation, did you missing call function Ctx" 286 ) 287 288 var ( 289 // instances is the management map for instances. 290 instances = gmap.NewStrAnyMap(true) 291 292 // driverMap manages all custom registered driver. 293 driverMap = map[string]Driver{ 294 "mysql": &DriverMysql{}, 295 "mssql": &DriverMssql{}, 296 "pgsql": &DriverPgsql{}, 297 "oracle": &DriverOracle{}, 298 "sqlite": &DriverSqlite{}, 299 } 300 301 // lastOperatorRegPattern is the regular expression pattern for a string 302 // which has operator at its tail. 303 lastOperatorRegPattern = `[<>=]+\s*$` 304 305 // regularFieldNameRegPattern is the regular expression pattern for a string 306 // which is a regular field name of table. 307 regularFieldNameRegPattern = `^[\w\.\-]+$` 308 309 // regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'. 310 // Note that, although some databases allow char '.' in the field name, but it here does not allow '.' 311 // in the field name as it conflicts with "db.table.field" pattern in SOME situations. 312 regularFieldNameWithoutDotRegPattern = `^[\w\-]+$` 313 314 // tableFieldsMap caches the table information retrived from database. 315 tableFieldsMap = gmap.New(true) 316 317 // allDryRun sets dry-run feature for all database connections. 318 // It is commonly used for command options for convenience. 319 allDryRun = false 320 ) 321 322 func init() { 323 // allDryRun is initialized from environment or command options. 324 allDryRun = gcmd.GetOptWithEnv(commandEnvKeyForDryRun, false).Bool() 325 } 326 327 // Register registers custom database driver to gdb. 328 func Register(name string, driver Driver) error { 329 driverMap[name] = driver 330 return nil 331 } 332 333 // New creates and returns an ORM object with global configurations. 334 // The parameter `name` specifies the configuration group name, 335 // which is DefaultGroupName in default. 336 func New(group ...string) (db DB, err error) { 337 groupName := configs.group 338 if len(group) > 0 && group[0] != "" { 339 groupName = group[0] 340 } 341 configs.RLock() 342 defer configs.RUnlock() 343 344 if len(configs.config) < 1 { 345 return nil, gerror.NewCode( 346 gcode.CodeInvalidConfiguration, 347 "database configuration is empty, please set the database configuration before using", 348 ) 349 } 350 if _, ok := configs.config[groupName]; ok { 351 if node, err := getConfigNodeByGroup(groupName, true); err == nil { 352 c := &Core{ 353 group: groupName, 354 debug: gtype.NewBool(), 355 cache: gcache.New(), 356 links: gmap.NewStrAnyMap(true), 357 schema: gtype.NewString(), 358 logger: glog.New(), 359 config: node, 360 } 361 if v, ok := driverMap[node.Type]; ok { 362 c.db, err = v.New(c, node) 363 if err != nil { 364 return nil, err 365 } 366 return c.db, nil 367 } else { 368 return nil, gerror.NewCodef( 369 gcode.CodeInvalidConfiguration, 370 `cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`, 371 node.Type, node.Type, 372 ) 373 } 374 } else { 375 return nil, err 376 } 377 } else { 378 return nil, gerror.NewCodef( 379 gcode.CodeInvalidConfiguration, 380 `database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`, 381 groupName, groupName, 382 ) 383 } 384 } 385 386 // Instance returns an instance for DB operations. 387 // The parameter `name` specifies the configuration group name, 388 // which is DefaultGroupName in default. 389 func Instance(name ...string) (db DB, err error) { 390 group := configs.group 391 if len(name) > 0 && name[0] != "" { 392 group = name[0] 393 } 394 v := instances.GetOrSetFuncLock(group, func() interface{} { 395 db, err = New(group) 396 return db 397 }) 398 if v != nil { 399 return v.(DB), nil 400 } 401 return 402 } 403 404 // getConfigNodeByGroup calculates and returns a configuration node of given group. It 405 // calculates the value internally using weight algorithm for load balance. 406 // 407 // The parameter `master` specifies whether retrieving a master node, or else a slave node 408 // if master-slave configured. 409 func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) { 410 if list, ok := configs.config[group]; ok { 411 // Separates master and slave configuration nodes array. 412 masterList := make(ConfigGroup, 0) 413 slaveList := make(ConfigGroup, 0) 414 for i := 0; i < len(list); i++ { 415 if list[i].Role == "slave" { 416 slaveList = append(slaveList, list[i]) 417 } else { 418 masterList = append(masterList, list[i]) 419 } 420 } 421 if len(masterList) < 1 { 422 return nil, gerror.NewCode(gcode.CodeInvalidConfiguration, "at least one master node configuration's need to make sense") 423 } 424 if len(slaveList) < 1 { 425 slaveList = masterList 426 } 427 if master { 428 return getConfigNodeByWeight(masterList), nil 429 } else { 430 return getConfigNodeByWeight(slaveList), nil 431 } 432 } else { 433 return nil, gerror.NewCodef(gcode.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group) 434 } 435 } 436 437 // getConfigNodeByWeight calculates the configuration weights and randomly returns a node. 438 // 439 // Calculation algorithm brief: 440 // 1. If we have 2 nodes, and their weights are both 1, then the weight range is [0, 199]; 441 // 2. Node1 weight range is [0, 99], and node2 weight range is [100, 199], ratio is 1:1; 442 // 3. If the random number is 99, it then chooses and returns node1; 443 func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode { 444 if len(cg) < 2 { 445 return &cg[0] 446 } 447 var total int 448 for i := 0; i < len(cg); i++ { 449 total += cg[i].Weight * 100 450 } 451 // If total is 0 means all the nodes have no weight attribute configured. 452 // It then defaults each node's weight attribute to 1. 453 if total == 0 { 454 for i := 0; i < len(cg); i++ { 455 cg[i].Weight = 1 456 total += cg[i].Weight * 100 457 } 458 } 459 // Exclude the right border value. 460 r := grand.N(0, total-1) 461 min := 0 462 max := 0 463 for i := 0; i < len(cg); i++ { 464 max = min + cg[i].Weight*100 465 //fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max) 466 if r >= min && r < max { 467 return &cg[i] 468 } else { 469 min = max 470 } 471 } 472 return nil 473 } 474 475 // getSqlDb retrieves and returns a underlying database connection object. 476 // The parameter `master` specifies whether retrieves master node connection if 477 // master-slave nodes are configured. 478 func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) { 479 // Load balance. 480 node, err := getConfigNodeByGroup(c.group, master) 481 if err != nil { 482 return nil, err 483 } 484 // Default value checks. 485 if node.Charset == "" { 486 node.Charset = "utf8" 487 } 488 // Changes the schema. 489 nodeSchema := c.schema.Val() 490 if len(schema) > 0 && schema[0] != "" { 491 nodeSchema = schema[0] 492 } 493 if nodeSchema != "" { 494 // Value copy. 495 n := *node 496 n.Name = nodeSchema 497 node = &n 498 } 499 // Cache the underlying connection pool object by node. 500 v := c.links.GetOrSetFuncLock(node.String(), func() interface{} { 501 intlog.Printf( 502 c.db.GetCtx(), 503 `open new connection, master:%#v, config:%#v, node:%#v`, 504 master, c.config, node, 505 ) 506 defer func() { 507 if err != nil { 508 intlog.Printf(c.db.GetCtx(), `open new connection failed: %v, %#v`, err, node) 509 } else { 510 intlog.Printf( 511 c.db.GetCtx(), 512 `open new connection success, master:%#v, config:%#v, node:%#v`, 513 master, c.config, node, 514 ) 515 } 516 }() 517 518 sqlDb, err = c.db.Open(node) 519 if err != nil { 520 return nil 521 } 522 523 if c.config.MaxIdleConnCount > 0 { 524 sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount) 525 } else { 526 sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount) 527 } 528 if c.config.MaxOpenConnCount > 0 { 529 sqlDb.SetMaxOpenConns(c.config.MaxOpenConnCount) 530 } else { 531 sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount) 532 } 533 if c.config.MaxConnLifeTime > 0 { 534 // Automatically checks whether MaxConnLifetime is configured using string like: "30s", "60s", etc. 535 // Or else it is configured just using number, which means value in seconds. 536 if c.config.MaxConnLifeTime > time.Second { 537 sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime) 538 } else { 539 sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime * time.Second) 540 } 541 } else { 542 sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime) 543 } 544 return sqlDb 545 }) 546 if v != nil && sqlDb == nil { 547 sqlDb = v.(*sql.DB) 548 } 549 if node.Debug { 550 c.db.SetDebug(node.Debug) 551 } 552 if node.DryRun { 553 c.db.SetDryRun(node.DryRun) 554 } 555 return 556 }