gf/database/gdb/gdb.go

558 lines
22 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). 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,
// 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 (
2020-11-29 23:47:57 +08:00
"context"
2019-06-19 09:06:52 +08:00
"database/sql"
"github.com/gogf/gf/errors/gcode"
"time"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gcmd"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/internal/intlog"
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/os/gcache"
"github.com/gogf/gf/util/grand"
2017-11-23 10:21:28 +08:00
)
// DB defines the interfaces for ORM operations.
2018-12-14 18:35:51 +08:00
type DB interface {
// ===========================================================================
// Model creation.
// ===========================================================================
// Table function is deprecated, use Model instead.
// The DB interface is designed not only for
// relational databases but also for NoSQL databases in the future. The name
// "Table" is not proper for that purpose any more.
// Also see Core.Table.
2021-06-18 15:20:27 +08:00
// Deprecated.
2021-05-02 22:35:47 +08:00
Table(tableNameOrStruct ...interface{}) *Model
2021-02-08 17:57:21 +08:00
// Model creates and returns a new ORM model from given schema.
// The parameter `table` can be more than one table names, and also alias name, like:
// 1. Model names:
// Model("user")
// Model("user u")
// Model("user, user_detail")
// Model("user u, user_detail ud")
// 2. Model name with alias: Model("user", "u")
// Also see Core.Model.
2021-05-02 22:35:47 +08:00
Model(tableNameOrStruct ...interface{}) *Model
2021-02-08 17:57:21 +08:00
2021-06-18 15:20:27 +08:00
// Raw creates and returns a model based on a raw sql not a table.
Raw(rawSql string, args ...interface{}) *Model
2021-02-08 17:57:21 +08:00
// Schema creates and returns a schema.
// Also see Core.Schema.
Schema(schema string) *Schema
2021-02-08 17:57:21 +08:00
// With creates and returns an ORM model based on meta data of given object.
// Also see Core.With.
2021-05-02 22:35:47 +08:00
With(objects ...interface{}) *Model
2021-02-08 17:57:21 +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.
// Also see DriverMysql.Open.
2019-06-19 09:06:52 +08:00
Open(config *ConfigNode) (*sql.DB, error)
2020-11-29 23:47:57 +08:00
// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
// of current DB object and with given context in it.
// Also see Core.Ctx.
2020-11-29 23:47:57 +08:00
Ctx(ctx context.Context) DB
// Close closes the database and prevents new queries from starting.
// Close then waits for all queries that have started processing on the server
// to finish.
//
// It is rare to Close a DB, as the DB handle is meant to be
// long-lived and shared between many goroutines.
Close(ctx context.Context) error
// ===========================================================================
// Query APIs.
// ===========================================================================
Query(sql string, args ...interface{}) (*sql.Rows, error) // See Core.Query.
Exec(sql string, args ...interface{}) (sql.Result, error) // See Core.Exec.
Prepare(sql string, execOnMaster ...bool) (*Stmt, error) // See Core.Prepare.
2018-08-08 09:09:28 +08:00
// ===========================================================================
// Common APIs for CURD.
// ===========================================================================
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId.
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update.
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete.
2021-06-04 09:54:19 +08:00
// ===========================================================================
// Internal APIs for CURD, which can be overwritten by custom CURD implements.
2021-06-04 09:54:19 +08:00
// ===========================================================================
DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert.
2021-06-04 09:54:19 +08:00
DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
2021-06-26 18:20:55 +08:00
DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoCommit.
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
2021-06-04 09:54:19 +08:00
// ===========================================================================
// Query APIs for convenience purpose.
// ===========================================================================
2021-06-26 18:20:55 +08:00
GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll.
GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne.
GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue.
GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray.
GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount.
GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
Union(unions ...*Model) *Model // See Core.Union.
UnionAll(unions ...*Model) *Model // See Core.UnionAll.
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Master/Slave specification support.
// ===========================================================================
2021-05-21 15:30:21 +08:00
Master(schema ...string) (*sql.DB, error) // See Core.Master.
Slave(schema ...string) (*sql.DB, error) // See Core.Slave.
2018-08-08 09:09:28 +08:00
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Ping-Pong.
// ===========================================================================
PingMaster() error // See Core.PingMaster.
PingSlave() error // See Core.PingSlave.
2018-08-08 09:09:28 +08:00
// ===========================================================================
// Transaction.
// ===========================================================================
Begin() (*TX, error) // See Core.Begin.
Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) error // See Core.Transaction.
2018-08-08 09:09:28 +08:00
// ===========================================================================
// Configuration methods.
// ===========================================================================
GetCache() *gcache.Cache // See Core.GetCache.
SetDebug(debug bool) // See Core.SetDebug.
GetDebug() bool // See Core.GetDebug.
SetSchema(schema string) // See Core.SetSchema.
GetSchema() string // See Core.GetSchema.
GetPrefix() string // See Core.GetPrefix.
GetGroup() string // See Core.GetGroup.
SetDryRun(enabled bool) // See Core.SetDryRun.
GetDryRun() bool // See Core.GetDryRun.
SetLogger(logger *glog.Logger) // See Core.SetLogger.
GetLogger() *glog.Logger // See Core.GetLogger.
GetConfig() *ConfigNode // See Core.GetConfig.
SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount.
SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount.
SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime.
2020-03-08 00:17:42 +08:00
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Utility methods.
// ===========================================================================
GetCtx() context.Context // See Core.GetCtx.
GetCore() *Core // See Core.GetCore
GetChars() (charLeft string, charRight string) // See Core.GetChars.
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
2021-06-26 18:20:55 +08:00
FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server.
}
2020-03-08 00:17:42 +08:00
// Core is the base struct for database management.
type Core struct {
2021-02-08 17:57:21 +08:00
db DB // DB interface object.
ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization.
2020-11-29 23:47:57 +08:00
group string // Configuration group name.
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
links *gmap.StrAnyMap // links caches all created links by node.
2020-11-29 23:47:57 +08:00
schema *gtype.String // Custom schema for this object.
logger *glog.Logger // Logger for logging functionality.
2020-11-29 23:47:57 +08:00
config *ConfigNode // Current config node.
}
2020-03-08 00:17:42 +08:00
// Driver is the interface for integrating sql drivers into package gdb.
type Driver interface {
// New creates and returns a database object for specified database server.
2020-03-08 00:17:42 +08:00
New(core *Core, node *ConfigNode) (DB, error)
}
2021-05-21 15:30:21 +08:00
// Link is a common database function wrapper interface.
type Link interface {
Query(sql string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error)
ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error)
IsTransaction() bool
}
// Logger is the logging interface for DB.
type Logger interface {
Error(ctx context.Context, s string)
Debug(ctx context.Context, s string)
}
2020-01-07 22:14:32 +08:00
// Sql is the sql recording struct.
type Sql struct {
2021-05-21 15:30:21 +08:00
Sql string // SQL string(may contain reserved char '?').
Type string // SQL operation type.
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.
Group string // Group is the group name of the configuration that the sql is executed from.
IsTransaction bool // IsTransaction marks whether this sql is executed in transaction.
2017-11-23 10:21:28 +08:00
}
// DoInsertOption is the input struct for function DoInsert.
type DoInsertOption struct {
OnDuplicateStr string
OnDuplicateMap map[string]interface{}
InsertOption int // Insert operation.
BatchCount int // Batch count for batch inserting.
}
// TableField is the struct for table field.
type TableField struct {
Index int // For ordering purpose as map is unordered.
Name string // Field name.
Type string // Field type.
Null bool // Field can be null or not.
Key string // The index information(empty if it's not a index).
Default interface{} // Default value for the field.
Extra string // Extra information.
Comment string // Comment.
}
2020-11-28 13:52:17 +08:00
// Counter is the type for update count.
type Counter struct {
Field string
Value float64
}
type (
Raw string // Raw is a raw sql that will not be treated as argument but as a direct sql part.
Value = *gvar.Var // Value is the field value type.
Record map[string]Value // Record is the row record of the table.
Result []Record // Result is the row record array.
Map = map[string]interface{} // Map is alias of map[string]interface{}, which is the most common usage map type.
List = []Map // List is type of map array.
)
2017-11-23 10:21:28 +08:00
const (
defaultModelSafe = false
queryTypeNormal = 0
queryTypeCount = 1
unionTypeNormal = 0
unionTypeAll = 1
insertOptionDefault = 0
insertOptionReplace = 1
insertOptionSave = 2
insertOptionIgnore = 3
defaultBatchNumber = 10 // Per count for batch insert/replace/save.
defaultMaxIdleConnCount = 10 // Max idle connection count in pool.
defaultMaxOpenConnCount = 100 // Max open connection count in pool.
defaultMaxConnLifeTime = 30 * time.Second // Max life time for per connection in pool in seconds.
ctxTimeoutTypeExec = iota
ctxTimeoutTypeQuery
ctxTimeoutTypePrepare
commandEnvKeyForDryRun = "gf.gdb.dryrun"
2021-06-26 18:20:55 +08:00
ctxStrictKeyName = "gf.gdb.CtxStrictEnabled"
ctxStrictErrorStr = "context is required for database operation, did you missing call function Ctx"
)
var (
2020-03-08 00:17:42 +08:00
// instances is the management map for instances.
instances = gmap.NewStrAnyMap(true)
2020-03-08 00:17:42 +08:00
// driverMap manages all custom registered driver.
driverMap = map[string]Driver{
"mysql": &DriverMysql{},
"mssql": &DriverMssql{},
"pgsql": &DriverPgsql{},
2020-03-08 00:17:42 +08:00
"oracle": &DriverOracle{},
"sqlite": &DriverSqlite{},
}
// lastOperatorRegPattern is the regular expression pattern for a string
// which has operator at its tail.
lastOperatorRegPattern = `[<>=]+\s*$`
// regularFieldNameRegPattern is the regular expression pattern for a string
// which is a regular field name of table.
2021-05-27 22:18:16 +08:00
regularFieldNameRegPattern = `^[\w\.\-]+$`
// regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'.
// Note that, although some databases allow char '.' in the field name, but it here does not allow '.'
// in the field name as it conflicts with "db.table.field" pattern in SOME situations.
2021-05-27 22:18:16 +08:00
regularFieldNameWithoutDotRegPattern = `^[\w\-]+$`
// tableFieldsMap caches the table information retrived from database.
tableFieldsMap = gmap.New(true)
// allDryRun sets dry-run feature for all database connections.
// It is commonly used for command options for convenience.
allDryRun = false
)
func init() {
// allDryRun is initialized from environment or command options.
allDryRun = gcmd.GetOptWithEnv(commandEnvKeyForDryRun, false).Bool()
}
2020-03-08 00:17:42 +08:00
// Register registers custom database driver to gdb.
func Register(name string, driver Driver) error {
driverMap[name] = driver
return nil
}
2019-12-10 21:14:15 +08:00
// New creates and returns an ORM object with global configurations.
2021-02-08 17:57:21 +08:00
// The parameter `name` specifies the configuration group name,
2020-12-14 19:34:02 +08:00
// which is DefaultGroupName in default.
func New(group ...string) (db DB, err error) {
groupName := configs.group
if len(group) > 0 && group[0] != "" {
groupName = group[0]
2018-08-08 09:09:28 +08:00
}
configs.RLock()
defer configs.RUnlock()
2018-08-08 09:09:28 +08:00
if len(configs.config) < 1 {
2021-07-20 23:02:02 +08:00
return nil, gerror.NewCode(
gcode.CodeInvalidConfiguration,
2021-07-20 23:02:02 +08:00
"database configuration is empty, please set the database configuration before using",
)
2018-08-08 09:09:28 +08:00
}
if _, ok := configs.config[groupName]; ok {
if node, err := getConfigNodeByGroup(groupName, true); err == nil {
2020-03-08 00:17:42 +08:00
c := &Core{
2020-11-27 13:24:05 +08:00
group: groupName,
debug: gtype.NewBool(),
cache: gcache.New(),
links: gmap.NewStrAnyMap(true),
2020-11-27 13:24:05 +08:00
schema: gtype.NewString(),
logger: glog.New(),
2020-11-27 13:24:05 +08:00
config: node,
2019-06-19 09:06:52 +08:00
}
2020-03-08 00:17:42 +08:00
if v, ok := driverMap[node.Type]; ok {
2021-02-08 17:57:21 +08:00
c.db, err = v.New(c, node)
2020-03-08 00:17:42 +08:00
if err != nil {
return nil, err
}
2021-02-08 17:57:21 +08:00
return c.db, nil
2020-03-08 00:17:42 +08:00
} else {
2021-07-20 23:02:02 +08:00
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
`cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`,
node.Type, node.Type,
)
2019-06-19 09:06:52 +08:00
}
} else {
return nil, err
}
2018-08-08 09:09:28 +08:00
} else {
2021-07-20 23:02:02 +08:00
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
groupName, groupName,
)
2018-08-08 09:09:28 +08:00
}
2017-11-23 10:21:28 +08:00
}
// Instance returns an instance for DB operations.
2021-02-08 17:57:21 +08:00
// The parameter `name` specifies the configuration group name,
2020-12-14 19:34:02 +08:00
// which is DefaultGroupName in default.
func Instance(name ...string) (db DB, err error) {
group := configs.group
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
}
2020-02-26 11:54:46 +08:00
// getConfigNodeByGroup calculates and returns a configuration node of given group. It
// calculates the value internally using weight algorithm for load balance.
//
2021-02-08 17:57:21 +08:00
// The parameter `master` specifies whether retrieving a master node, or else a slave node
// if master-slave configured.
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
2019-06-19 09:06:52 +08:00
if list, ok := configs.config[group]; ok {
// Separates master and slave configuration nodes array.
2019-06-19 09:06:52 +08:00
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, gerror.NewCode(gcode.CodeInvalidConfiguration, "at least one master node configuration's need to make sense")
2019-06-19 09:06:52 +08:00
}
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, gerror.NewCodef(gcode.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group)
2019-06-19 09:06:52 +08:00
}
}
// getConfigNodeByWeight calculates the configuration weights and randomly returns a node.
//
// Calculation algorithm brief:
// 1. If we have 2 nodes, and their weights are both 1, then the weight range is [0, 199];
// 2. Node1 weight range is [0, 99], and node2 weight range is [100, 199], ratio is 1:1;
// 3. If the random number is 99, it then chooses and returns node1;
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
}
// If total is 0 means all the nodes have no weight attribute configured.
// It then defaults each node's weight attribute to 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
}
}
// Exclude the right border value.
r := grand.N(0, total-1)
2018-08-08 09:09:28 +08:00
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.
2021-02-08 17:57:21 +08:00
// The parameter `master` specifies whether retrieves master node connection if
2020-01-07 22:14:32 +08:00
// master-slave nodes are configured.
2020-03-08 00:17:42 +08:00
func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
2020-01-07 22:14:32 +08:00
// Load balance.
2020-03-08 00:17:42 +08:00
node, err := getConfigNodeByGroup(c.group, master)
2019-06-19 09:06:52 +08:00
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.
2020-03-08 00:17:42 +08:00
nodeSchema := c.schema.Val()
2020-01-07 22:14:32 +08:00
if len(schema) > 0 && schema[0] != "" {
nodeSchema = schema[0]
}
if nodeSchema != "" {
// Value copy.
n := *node
n.Name = nodeSchema
node = &n
}
// Cache the underlying connection pool object by node.
v := c.links.GetOrSetFuncLock(node.String(), func() interface{} {
intlog.Printf(
c.db.GetCtx(),
`open new connection, master:%#v, config:%#v, node:%#v`,
master, c.config, node,
)
defer func() {
if err != nil {
intlog.Printf(c.db.GetCtx(), `open new connection failed: %v, %#v`, err, node)
} else {
intlog.Printf(
c.db.GetCtx(),
`open new connection success, master:%#v, config:%#v, node:%#v`,
master, c.config, node,
)
}
}()
2021-02-08 17:57:21 +08:00
sqlDb, err = c.db.Open(node)
2019-06-19 09:06:52 +08:00
if err != nil {
return nil
2019-06-19 09:06:52 +08:00
}
2020-11-27 13:24:05 +08:00
if c.config.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount)
} else {
sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount)
2019-06-19 09:06:52 +08:00
}
2020-11-27 13:24:05 +08:00
if c.config.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(c.config.MaxOpenConnCount)
} else {
sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount)
2019-06-19 09:06:52 +08:00
}
if c.config.MaxConnLifeTime > 0 {
2020-11-27 13:24:05 +08:00
// Automatically checks whether MaxConnLifetime is configured using string like: "30s", "60s", etc.
// Or else it is configured just using number, which means value in seconds.
if c.config.MaxConnLifeTime > time.Second {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime)
2020-11-27 13:24:05 +08:00
} else {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime * time.Second)
2020-11-27 13:24:05 +08:00
}
} else {
sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
2019-06-19 09:06:52 +08:00
}
return sqlDb
})
2019-06-19 09:06:52 +08:00
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}
if node.Debug {
2021-02-08 17:57:21 +08:00
c.db.SetDebug(node.Debug)
2019-07-15 17:40:21 +08:00
}
if node.DryRun {
2021-02-08 17:57:21 +08:00
c.db.SetDryRun(node.DryRun)
}
2019-06-19 09:06:52 +08:00
return
}