2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2017-12-31 18:19:58 +08:00
|
|
|
|
|
2019-01-15 23:27:47 +08:00
|
|
|
|
// Package gdb provides ORM features for popular relationship databases.
|
2017-11-23 10:21:28 +08:00
|
|
|
|
package gdb
|
|
|
|
|
|
|
|
|
|
import (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
"database/sql"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2019-06-29 18:17:33 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
2019-08-26 19:39:04 +08:00
|
|
|
|
"github.com/gogf/gf/os/glog"
|
|
|
|
|
|
2019-07-29 21:01:19 +08:00
|
|
|
|
"github.com/gogf/gf/container/gmap"
|
|
|
|
|
"github.com/gogf/gf/container/gtype"
|
|
|
|
|
"github.com/gogf/gf/container/gvar"
|
|
|
|
|
"github.com/gogf/gf/os/gcache"
|
|
|
|
|
"github.com/gogf/gf/util/grand"
|
2017-11-23 10:21:28 +08:00
|
|
|
|
)
|
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// DB is the interface for ORM operations.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
type DB interface {
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Open creates a raw connection object for database with given node configuration.
|
|
|
|
|
// Note that it is not recommended using the this function manually.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
Open(config *ConfigNode) (*sql.DB, error)
|
2018-12-15 15:50:39 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Query APIs.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
Query(query string, args ...interface{}) (*sql.Rows, error)
|
|
|
|
|
Exec(sql string, args ...interface{}) (sql.Result, error)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
|
2019-08-08 17:43:21 +08:00
|
|
|
|
doGetAll(link dbLink, query string, args ...interface{}) (result Result, err error)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
|
|
|
|
|
doPrepare(link dbLink, query string) (*sql.Stmt, error)
|
|
|
|
|
doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
|
|
|
|
|
doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
|
|
|
|
|
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
|
|
|
|
|
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
|
2018-12-15 15:50:39 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Query APIs for convenience purpose.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
GetAll(query string, args ...interface{}) (Result, error)
|
|
|
|
|
GetOne(query string, args ...interface{}) (Record, error)
|
|
|
|
|
GetValue(query string, args ...interface{}) (Value, error)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
GetCount(query string, args ...interface{}) (int, error)
|
|
|
|
|
GetStruct(objPointer interface{}, query string, args ...interface{}) error
|
|
|
|
|
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
|
|
|
|
|
GetScan(objPointer interface{}, query string, args ...interface{}) error
|
2018-12-15 15:50:39 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Master/Slave support.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
Master() (*sql.DB, error)
|
|
|
|
|
Slave() (*sql.DB, error)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Ping.
|
2018-08-08 09:09:28 +08:00
|
|
|
|
PingMaster() error
|
|
|
|
|
PingSlave() error
|
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Transaction.
|
2018-12-15 15:50:39 +08:00
|
|
|
|
Begin() (*TX, error)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
|
|
|
|
|
Replace(table string, data interface{}, batch ...int) (sql.Result, error)
|
|
|
|
|
Save(table string, data interface{}, batch ...int) (sql.Result, error)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error)
|
|
|
|
|
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error)
|
|
|
|
|
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
|
|
|
|
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
|
|
|
|
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
|
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Create model.
|
2018-08-08 09:09:28 +08:00
|
|
|
|
From(tables string) *Model
|
2019-07-04 19:22:11 +08:00
|
|
|
|
Table(tables string) *Model
|
2020-01-10 23:48:19 +08:00
|
|
|
|
Schema(schema string) *Schema
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Configuration methods.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
SetDebug(debug bool)
|
|
|
|
|
SetSchema(schema string)
|
2019-08-26 19:39:04 +08:00
|
|
|
|
SetLogger(logger *glog.Logger)
|
2019-12-04 16:04:52 +08:00
|
|
|
|
GetLogger() *glog.Logger
|
2019-06-29 18:17:33 +08:00
|
|
|
|
SetMaxIdleConnCount(n int)
|
|
|
|
|
SetMaxOpenConnCount(n int)
|
2019-09-24 23:41:18 +08:00
|
|
|
|
SetMaxConnLifetime(d time.Duration)
|
2020-01-07 22:14:32 +08:00
|
|
|
|
Tables(schema ...string) (tables []string, err error)
|
|
|
|
|
TableFields(table string, schema ...string) (map[string]*TableField, error)
|
2018-12-14 18:35:51 +08:00
|
|
|
|
|
2019-12-23 23:14:54 +08:00
|
|
|
|
// Internal methods.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
getCache() *gcache.Cache
|
2018-12-14 18:35:51 +08:00
|
|
|
|
getChars() (charLeft string, charRight string)
|
|
|
|
|
getDebug() bool
|
2019-12-10 21:14:15 +08:00
|
|
|
|
getPrefix() string
|
2020-01-07 22:14:32 +08:00
|
|
|
|
getMaster(schema ...string) (*sql.DB, error)
|
|
|
|
|
getSlave(schema ...string) (*sql.DB, error)
|
2019-07-22 23:48:39 +08:00
|
|
|
|
quoteWord(s string) string
|
2019-12-13 15:25:49 +08:00
|
|
|
|
quoteString(s string) string
|
2019-12-23 23:14:54 +08:00
|
|
|
|
handleTableName(table string) string
|
2020-01-07 22:14:32 +08:00
|
|
|
|
filterFields(schema, table string, data map[string]interface{}) map[string]interface{}
|
2019-07-22 15:10:40 +08:00
|
|
|
|
convertValue(fieldValue []byte, fieldType string) interface{}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
rowsToResult(rows *sql.Rows) (Result, error)
|
|
|
|
|
handleSqlBeforeExec(sql string) string
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// dbLink is a common database function wrapper interface for internal usage.
|
2018-12-15 15:50:39 +08:00
|
|
|
|
type dbLink interface {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
Query(query string, args ...interface{}) (*sql.Rows, error)
|
|
|
|
|
Exec(sql string, args ...interface{}) (sql.Result, error)
|
|
|
|
|
Prepare(sql string) (*sql.Stmt, error)
|
2018-12-15 15:50:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// dbBase is the base struct for database management.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
type dbBase struct {
|
2020-01-07 22:14:32 +08:00
|
|
|
|
db DB // DB interface object.
|
|
|
|
|
group string // Configuration group name.
|
|
|
|
|
debug *gtype.Bool // Enable debug mode for the database.
|
|
|
|
|
cache *gcache.Cache // Cache manager.
|
|
|
|
|
schema *gtype.String // Custom schema for this object.
|
|
|
|
|
prefix string // Table prefix.
|
|
|
|
|
logger *glog.Logger // Logger.
|
|
|
|
|
maxIdleConnCount int // Max idle connection count.
|
|
|
|
|
maxOpenConnCount int // Max open connection count.
|
|
|
|
|
maxConnLifetime time.Duration // Max TTL for a connection.
|
2018-07-05 19:36:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// Sql is the sql recording struct.
|
2018-07-05 19:36:35 +08:00
|
|
|
|
type Sql struct {
|
2020-01-07 22:14:32 +08:00
|
|
|
|
Sql string // SQL string(may contain reserved char '?').
|
|
|
|
|
Args []interface{} // Arguments for this sql.
|
|
|
|
|
Format string // Formatted sql which contains arguments in the sql.
|
|
|
|
|
Error error // Execution result.
|
|
|
|
|
Start int64 // Start execution timestamp in milliseconds.
|
|
|
|
|
End int64 // End execution timestamp in milliseconds.
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-02 15:48:25 +08:00
|
|
|
|
// 表字段结构信息
|
|
|
|
|
type TableField struct {
|
2019-12-12 23:38:46 +08:00
|
|
|
|
Index int // 用于字段排序(因为map类型是无序的)
|
2019-09-02 15:48:25 +08:00
|
|
|
|
Name string // 字段名称
|
|
|
|
|
Type string // 字段类型
|
|
|
|
|
Null bool // 是否可为null
|
|
|
|
|
Key string // 索引信息
|
|
|
|
|
Default interface{} // 默认值
|
|
|
|
|
Extra string // 其他信息
|
2019-12-12 23:38:46 +08:00
|
|
|
|
Comment string // 字段描述
|
2019-09-02 15:48:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-01 10:09:57 +08:00
|
|
|
|
// 返回数据表记录值
|
2019-01-14 22:55:43 +08:00
|
|
|
|
type Value = *gvar.Var
|
2018-05-01 10:09:57 +08:00
|
|
|
|
|
|
|
|
|
// 返回数据表记录Map
|
2018-08-08 09:09:28 +08:00
|
|
|
|
type Record map[string]Value
|
2018-05-01 10:09:57 +08:00
|
|
|
|
|
|
|
|
|
// 返回数据表记录List
|
2018-08-08 09:09:28 +08:00
|
|
|
|
type Result []Record
|
2017-11-23 10:21:28 +08:00
|
|
|
|
|
2018-05-25 18:20:40 +08:00
|
|
|
|
// 关联数组,绑定一条数据表记录(使用别名)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
type Map = map[string]interface{}
|
2018-05-25 18:20:40 +08:00
|
|
|
|
|
|
|
|
|
// 关联数组列表(索引从0开始的数组),绑定多条记录(使用别名)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
type List = []Map
|
2017-11-23 10:21:28 +08:00
|
|
|
|
|
2018-12-28 21:56:27 +08:00
|
|
|
|
const (
|
2019-09-29 14:27:09 +08:00
|
|
|
|
gINSERT_OPTION_DEFAULT = 0
|
|
|
|
|
gINSERT_OPTION_REPLACE = 1
|
|
|
|
|
gINSERT_OPTION_SAVE = 2
|
|
|
|
|
gINSERT_OPTION_IGNORE = 3
|
2019-06-19 09:06:52 +08:00
|
|
|
|
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
|
|
|
|
|
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
|
2019-04-02 14:37:46 +08:00
|
|
|
|
)
|
2019-02-27 09:38:10 +08:00
|
|
|
|
|
2019-04-02 14:37:46 +08:00
|
|
|
|
var (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// Instance map.
|
2019-07-23 23:20:27 +08:00
|
|
|
|
instances = gmap.NewStrAnyMap(true)
|
2018-12-28 21:56:27 +08:00
|
|
|
|
)
|
|
|
|
|
|
2019-12-10 21:14:15 +08:00
|
|
|
|
// New creates and returns an ORM object with global configurations.
|
2019-06-11 20:57:43 +08:00
|
|
|
|
// The parameter <name> specifies the configuration group name,
|
2019-04-03 23:39:31 +08:00
|
|
|
|
// which is DEFAULT_GROUP_NAME in default.
|
|
|
|
|
func New(name ...string) (db DB, err error) {
|
2019-04-02 14:37:46 +08:00
|
|
|
|
group := configs.defaultGroup
|
2019-07-28 17:37:13 +08:00
|
|
|
|
if len(name) > 0 && name[0] != "" {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
group = name[0]
|
2018-08-08 09:09:28 +08:00
|
|
|
|
}
|
2019-04-02 14:37:46 +08:00
|
|
|
|
configs.RLock()
|
|
|
|
|
defer configs.RUnlock()
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2019-04-02 14:37:46 +08:00
|
|
|
|
if len(configs.config) < 1 {
|
2018-08-08 09:09:28 +08:00
|
|
|
|
return nil, errors.New("empty database configuration")
|
|
|
|
|
}
|
2019-04-02 14:37:46 +08:00
|
|
|
|
if _, ok := configs.config[group]; ok {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if node, err := getConfigNodeByGroup(group, true); err == nil {
|
|
|
|
|
base := &dbBase{
|
2019-08-26 19:39:04 +08:00
|
|
|
|
group: group,
|
|
|
|
|
debug: gtype.NewBool(),
|
|
|
|
|
cache: gcache.New(),
|
|
|
|
|
schema: gtype.NewString(),
|
2019-12-04 16:04:52 +08:00
|
|
|
|
logger: glog.New(),
|
2019-12-10 21:14:15 +08:00
|
|
|
|
prefix: node.Prefix,
|
2019-08-26 19:39:04 +08:00
|
|
|
|
maxConnLifetime: gDEFAULT_CONN_MAX_LIFE_TIME,
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
switch node.Type {
|
|
|
|
|
case "mysql":
|
|
|
|
|
base.db = &dbMysql{dbBase: base}
|
|
|
|
|
case "pgsql":
|
|
|
|
|
base.db = &dbPgsql{dbBase: base}
|
|
|
|
|
case "mssql":
|
|
|
|
|
base.db = &dbMssql{dbBase: base}
|
|
|
|
|
case "sqlite":
|
|
|
|
|
base.db = &dbSqlite{dbBase: base}
|
|
|
|
|
case "oracle":
|
|
|
|
|
base.db = &dbOracle{dbBase: base}
|
|
|
|
|
default:
|
|
|
|
|
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
|
|
|
|
|
}
|
|
|
|
|
return base.db, nil
|
|
|
|
|
} else {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-08-08 09:09:28 +08:00
|
|
|
|
} else {
|
2019-12-19 15:14:05 +08:00
|
|
|
|
return nil, errors.New(fmt.Sprintf(`database configuration node "%s" is not found`, group))
|
2018-08-08 09:09:28 +08:00
|
|
|
|
}
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-03 23:39:31 +08:00
|
|
|
|
// Instance returns an instance for DB operations.
|
2019-06-11 20:57:43 +08:00
|
|
|
|
// The parameter <name> specifies the configuration group name,
|
2019-04-03 23:39:31 +08:00
|
|
|
|
// which is DEFAULT_GROUP_NAME in default.
|
|
|
|
|
func Instance(name ...string) (db DB, err error) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
group := configs.defaultGroup
|
2019-10-17 20:31:03 +08:00
|
|
|
|
if len(name) > 0 && name[0] != "" {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
group = name[0]
|
|
|
|
|
}
|
|
|
|
|
v := instances.GetOrSetFuncLock(group, func() interface{} {
|
|
|
|
|
db, err = New(group)
|
|
|
|
|
return db
|
|
|
|
|
})
|
|
|
|
|
if v != nil {
|
|
|
|
|
return v.(DB), nil
|
|
|
|
|
}
|
|
|
|
|
return
|
2019-04-02 14:37:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 20:29:27 +08:00
|
|
|
|
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
|
|
|
|
|
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if list, ok := configs.config[group]; ok {
|
|
|
|
|
// 将master, slave集群列表拆分出来
|
|
|
|
|
masterList := make(ConfigGroup, 0)
|
|
|
|
|
slaveList := make(ConfigGroup, 0)
|
|
|
|
|
for i := 0; i < len(list); i++ {
|
|
|
|
|
if list[i].Role == "slave" {
|
|
|
|
|
slaveList = append(slaveList, list[i])
|
|
|
|
|
} else {
|
|
|
|
|
masterList = append(masterList, list[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(masterList) < 1 {
|
|
|
|
|
return nil, errors.New("at least one master node configuration's need to make sense")
|
|
|
|
|
}
|
|
|
|
|
if len(slaveList) < 1 {
|
|
|
|
|
slaveList = masterList
|
|
|
|
|
}
|
|
|
|
|
if master {
|
2019-07-06 16:14:45 +08:00
|
|
|
|
return getConfigNodeByWeight(masterList), nil
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
2019-07-06 16:14:45 +08:00
|
|
|
|
return getConfigNodeByWeight(slaveList), nil
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
|
|
|
|
}
|
2018-10-13 20:29:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
|
2018-04-27 11:38:26 +08:00
|
|
|
|
// 算法说明举例,
|
|
|
|
|
// 1、假如2个节点的priority都是1,那么随机大小范围为[0, 199];
|
|
|
|
|
// 2、那么节点1的权重范围为[0, 99],节点2的权重范围为[100, 199],比例为1:1;
|
|
|
|
|
// 3、假如计算出的随机数为99;
|
|
|
|
|
// 4、那么选择的配置为节点1;
|
2019-07-06 16:14:45 +08:00
|
|
|
|
func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
|
2018-08-08 09:09:28 +08:00
|
|
|
|
if len(cg) < 2 {
|
|
|
|
|
return &cg[0]
|
|
|
|
|
}
|
|
|
|
|
var total int
|
|
|
|
|
for i := 0; i < len(cg); i++ {
|
2019-07-06 16:14:45 +08:00
|
|
|
|
total += cg[i].Weight * 100
|
2018-08-08 09:09:28 +08:00
|
|
|
|
}
|
2018-12-16 23:11:15 +08:00
|
|
|
|
// 如果total为0表示所有连接都没有配置priority属性,那么默认都是1
|
|
|
|
|
if total == 0 {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
for i := 0; i < len(cg); i++ {
|
2019-07-06 16:14:45 +08:00
|
|
|
|
cg[i].Weight = 1
|
|
|
|
|
total += cg[i].Weight * 100
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-08 09:09:28 +08:00
|
|
|
|
// 不能取到末尾的边界点
|
2019-09-06 16:59:38 +08:00
|
|
|
|
r := grand.N(0, total)
|
2018-08-08 09:09:28 +08:00
|
|
|
|
if r > 0 {
|
|
|
|
|
r -= 1
|
|
|
|
|
}
|
|
|
|
|
min := 0
|
|
|
|
|
max := 0
|
|
|
|
|
for i := 0; i < len(cg); i++ {
|
2019-07-06 16:14:45 +08:00
|
|
|
|
max = min + cg[i].Weight*100
|
2018-08-08 09:09:28 +08:00
|
|
|
|
//fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
|
|
|
|
|
if r >= min && r < max {
|
|
|
|
|
return &cg[i]
|
|
|
|
|
} else {
|
|
|
|
|
min = max
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// getSqlDb retrieves and returns a underlying database connection object.
|
|
|
|
|
// The parameter <master> specifies whether retrieves master node connection if
|
|
|
|
|
// master-slave nodes are configured.
|
|
|
|
|
func (bs *dbBase) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
|
|
|
|
|
// Load balance.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
node, err := getConfigNodeByGroup(bs.group, master)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// Default value checks.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if node.Charset == "" {
|
|
|
|
|
node.Charset = "utf8"
|
|
|
|
|
}
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// Changes the schema.
|
|
|
|
|
nodeSchema := bs.schema.Val()
|
|
|
|
|
if len(schema) > 0 && schema[0] != "" {
|
|
|
|
|
nodeSchema = schema[0]
|
|
|
|
|
}
|
|
|
|
|
if nodeSchema != "" {
|
|
|
|
|
// Value copy.
|
|
|
|
|
n := *node
|
|
|
|
|
n.Name = nodeSchema
|
|
|
|
|
node = &n
|
|
|
|
|
}
|
|
|
|
|
// Cache the underlying connection object by node.
|
2019-06-19 09:06:52 +08:00
|
|
|
|
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
|
|
|
|
|
sqlDb, err = bs.db.Open(node)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2019-08-26 19:39:04 +08:00
|
|
|
|
if bs.maxIdleConnCount > 0 {
|
|
|
|
|
sqlDb.SetMaxIdleConns(bs.maxIdleConnCount)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else if node.MaxIdleConnCount > 0 {
|
|
|
|
|
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 19:39:04 +08:00
|
|
|
|
if bs.maxOpenConnCount > 0 {
|
|
|
|
|
sqlDb.SetMaxOpenConns(bs.maxOpenConnCount)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else if node.MaxOpenConnCount > 0 {
|
|
|
|
|
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 19:39:04 +08:00
|
|
|
|
if bs.maxConnLifetime > 0 {
|
2019-10-15 21:20:38 +08:00
|
|
|
|
sqlDb.SetConnMaxLifetime(bs.maxConnLifetime * time.Second)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else if node.MaxConnLifetime > 0 {
|
2019-10-15 21:20:38 +08:00
|
|
|
|
sqlDb.SetConnMaxLifetime(node.MaxConnLifetime * time.Second)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
return sqlDb
|
|
|
|
|
}, 0)
|
|
|
|
|
if v != nil && sqlDb == nil {
|
|
|
|
|
sqlDb = v.(*sql.DB)
|
|
|
|
|
}
|
2019-07-15 19:54:13 +08:00
|
|
|
|
if node.Debug {
|
|
|
|
|
bs.db.SetDebug(node.Debug)
|
2019-07-15 17:40:21 +08:00
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return
|
2018-10-13 20:29:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// SetSchema changes the schema for this database connection object.
|
|
|
|
|
// Importantly note that when schema configuration changed for the database,
|
|
|
|
|
// it affects all operations on the database object in the future.
|
2018-12-17 19:26:59 +08:00
|
|
|
|
func (bs *dbBase) SetSchema(schema string) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
bs.schema.Set(schema)
|
2018-12-17 19:26:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// Master creates and returns a connection from master node if master-slave configured.
|
|
|
|
|
// It returns the default connection if master-slave not configured.
|
2018-12-15 15:50:39 +08:00
|
|
|
|
func (bs *dbBase) Master() (*sql.DB, error) {
|
2020-01-07 22:14:32 +08:00
|
|
|
|
return bs.getSqlDb(true, bs.schema.Val())
|
2018-10-13 20:29:27 +08:00
|
|
|
|
}
|
2018-08-08 09:09:28 +08:00
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
|
// Slave creates and returns a connection from slave node if master-slave configured.
|
|
|
|
|
// It returns the default connection if master-slave not configured.
|
2018-12-15 15:50:39 +08:00
|
|
|
|
func (bs *dbBase) Slave() (*sql.DB, error) {
|
2020-01-07 22:14:32 +08:00
|
|
|
|
return bs.getSqlDb(false, bs.schema.Val())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getMaster acts like function Master but with additional <schema> parameter specifying
|
|
|
|
|
// the schema for the connection. It is defined for internal usage.
|
|
|
|
|
// Also see Master.
|
|
|
|
|
func (bs *dbBase) getMaster(schema ...string) (*sql.DB, error) {
|
|
|
|
|
return bs.getSqlDb(true, schema...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getSlave acts like function Slave but with additional <schema> parameter specifying
|
|
|
|
|
// the schema for the connection. It is defined for internal usage.
|
|
|
|
|
// Also see Slave.
|
|
|
|
|
func (bs *dbBase) getSlave(schema ...string) (*sql.DB, error) {
|
|
|
|
|
return bs.getSqlDb(false, schema...)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|