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,
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 (
2020-11-29 23:47:57 +08:00
"context"
2019-06-19 09:06:52 +08:00
"database/sql"
2022-09-26 22:11:13 +08:00
"fmt"
2021-05-19 21:11:51 +08:00
"time"
2022-09-26 22:11:13 +08:00
"github.com/gogf/gf/v2/container/garray"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gtype"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/os/gcache"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/os/gcmd"
2022-03-14 23:47:55 +08:00
"github.com/gogf/gf/v2/os/gctx"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/os/glog"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/util/grand"
2022-09-26 22:11:13 +08:00
"github.com/gogf/gf/v2/util/gutil"
2017-11-23 10:21:28 +08:00
)
2020-03-09 21:53:58 +08:00
// DB defines the interfaces for ORM operations.
2018-12-14 18:35:51 +08:00
type DB interface {
2020-07-08 20:48:29 +08:00
// ===========================================================================
// Model creation.
// ===========================================================================
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")
2021-04-04 12:01:22 +08:00
// 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.
2021-04-04 12:01:22 +08:00
// Also see Core.Schema.
2020-07-08 20:48:29 +08:00
Schema ( schema string ) * Schema
2022-02-16 00:26:06 +08:00
// With creates and returns an ORM model based on metadata of given object.
2021-04-04 12:01:22 +08:00
// Also see Core.With.
2021-05-02 22:35:47 +08:00
With ( objects ... interface { } ) * Model
2021-02-08 17:57:21 +08:00
2019-12-23 23:14:54 +08:00
// Open creates a raw connection object for database with given node configuration.
2022-02-16 00:26:06 +08:00
// Note that it is not recommended using the function manually.
2021-04-04 12:01:22 +08:00
// Also see DriverMysql.Open.
2019-06-19 09:06:52 +08:00
Open ( config * ConfigNode ) ( * sql . DB , error )
2018-12-15 15:50:39 +08:00
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.
2021-04-04 12:01:22 +08:00
// Also see Core.Ctx.
2020-11-29 23:47:57 +08:00
Ctx ( ctx context . Context ) DB
2021-08-24 22:59:25 +08:00
// 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
2020-07-08 20:48:29 +08:00
// ===========================================================================
2019-12-23 23:14:54 +08:00
// Query APIs.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2021-09-29 20:39:02 +08:00
Query ( ctx context . Context , sql string , args ... interface { } ) ( Result , error ) // See Core.Query.
Exec ( ctx context . Context , sql string , args ... interface { } ) ( sql . Result , error ) // See Core.Exec.
Prepare ( ctx context . Context , sql string , execOnMaster ... bool ) ( * Stmt , error ) // See Core.Prepare.
2018-08-08 09:09:28 +08:00
2020-07-08 20:48:29 +08:00
// ===========================================================================
// Common APIs for CURD.
// ===========================================================================
2021-09-29 20:39:02 +08:00
Insert ( ctx context . Context , table string , data interface { } , batch ... int ) ( sql . Result , error ) // See Core.Insert.
InsertIgnore ( ctx context . Context , table string , data interface { } , batch ... int ) ( sql . Result , error ) // See Core.InsertIgnore.
InsertAndGetId ( ctx context . Context , table string , data interface { } , batch ... int ) ( int64 , error ) // See Core.InsertAndGetId.
Replace ( ctx context . Context , table string , data interface { } , batch ... int ) ( sql . Result , error ) // See Core.Replace.
Save ( ctx context . Context , table string , data interface { } , batch ... int ) ( sql . Result , error ) // See Core.Save.
Update ( ctx context . Context , table string , data interface { } , condition interface { } , args ... interface { } ) ( sql . Result , error ) // See Core.Update.
Delete ( ctx context . Context , table string , condition interface { } , args ... interface { } ) ( sql . Result , error ) // See Core.Delete.
2020-07-08 20:48:29 +08:00
2021-06-04 09:54:19 +08:00
// ===========================================================================
2021-08-24 22:59:25 +08:00
// Internal APIs for CURD, which can be overwritten by custom CURD implements.
2021-06-04 09:54:19 +08:00
// ===========================================================================
2022-03-14 19:41:32 +08:00
DoSelect ( ctx context . Context , link Link , sql string , args ... interface { } ) ( result Result , err error ) // See Core.DoSelect.
2021-06-16 21:44:31 +08:00
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.
2022-03-14 19:36:43 +08:00
DoQuery ( ctx context . Context , link Link , sql string , args ... interface { } ) ( result Result , err error ) // See Core.DoQuery.
DoExec ( ctx context . Context , link Link , sql string , args ... interface { } ) ( result sql . Result , err error ) // See Core.DoExec.
DoFilter ( ctx context . Context , link Link , sql string , args [ ] interface { } ) ( newSql string , newArgs [ ] interface { } , err error ) // See Core.DoFilter.
DoCommit ( ctx context . Context , in DoCommitInput ) ( out DoCommitOutput , 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
2020-07-08 20:48:29 +08:00
// ===========================================================================
2019-12-23 23:14:54 +08:00
// Query APIs for convenience purpose.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2021-09-29 20:39:02 +08:00
GetAll ( ctx context . Context , sql string , args ... interface { } ) ( Result , error ) // See Core.GetAll.
GetOne ( ctx context . Context , sql string , args ... interface { } ) ( Record , error ) // See Core.GetOne.
GetValue ( ctx context . Context , sql string , args ... interface { } ) ( Value , error ) // See Core.GetValue.
GetArray ( ctx context . Context , sql string , args ... interface { } ) ( [ ] Value , error ) // See Core.GetArray.
2022-11-07 17:52:25 +08:00
GetCount ( ctx context . Context , sql string , args ... interface { } ) ( int64 , error ) // See Core.GetCount.
2021-09-29 20:39:02 +08:00
GetScan ( ctx context . Context , objPointer interface { } , sql string , args ... interface { } ) error // See Core.GetScan.
Union ( unions ... * Model ) * Model // See Core.Union.
UnionAll ( unions ... * Model ) * Model // See Core.UnionAll.
2018-12-15 15:50:39 +08:00
2020-07-08 20:48:29 +08:00
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Master/Slave specification support.
2020-07-08 20:48:29 +08:00
// ===========================================================================
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-07-08 20:48:29 +08:00
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Ping-Pong.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2021-04-04 12:01:22 +08:00
PingMaster ( ) error // See Core.PingMaster.
PingSlave ( ) error // See Core.PingSlave.
2018-08-08 09:09:28 +08:00
2020-07-08 20:48:29 +08:00
// ===========================================================================
2019-12-23 23:14:54 +08:00
// Transaction.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2021-09-29 20:39:02 +08:00
Begin ( ctx context . Context ) ( * TX , error ) // See Core.Begin.
2021-05-19 21:11:51 +08:00
Transaction ( ctx context . Context , f func ( ctx context . Context , tx * TX ) error ) error // See Core.Transaction.
2018-08-08 09:09:28 +08:00
2020-07-08 20:48:29 +08:00
// ===========================================================================
2019-12-23 23:14:54 +08:00
// Configuration methods.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2021-04-04 12:01:22 +08:00
GetCache ( ) * gcache . Cache // See Core.GetCache.
SetDebug ( debug bool ) // See Core.SetDebug.
GetDebug ( ) bool // See Core.GetDebug.
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.
2022-07-07 21:16:26 +08:00
SetLogger ( logger glog . ILogger ) // See Core.SetLogger.
GetLogger ( ) glog . ILogger // See Core.GetLogger.
2021-04-04 12:01:22 +08:00
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-07-08 20:48:29 +08:00
// ===========================================================================
2020-03-08 00:17:42 +08:00
// Utility methods.
2020-07-08 20:48:29 +08:00
// ===========================================================================
2022-07-07 21:16:26 +08:00
GetCtx ( ) context . Context // See Core.GetCtx.
GetCore ( ) * Core // See Core.GetCore
GetChars ( ) ( charLeft string , charRight string ) // See Core.GetChars.
2022-09-26 22:11:13 +08:00
Tables ( ctx context . Context , schema ... string ) ( tables [ ] string , err error ) // See Core.Tables. The driver must implement this function.
TableFields ( ctx context . Context , table string , schema ... string ) ( map [ string ] * TableField , error ) // See Core.TableFields. The driver must implement this function.
2022-07-07 21:16:26 +08:00
ConvertDataForRecord ( ctx context . Context , data interface { } ) ( map [ string ] interface { } , error ) // See Core.ConvertDataForRecord
ConvertValueForLocal ( ctx context . Context , fieldType string , fieldValue interface { } ) ( interface { } , error ) // See Core.ConvertValueForLocal
2022-08-11 21:47:35 +08:00
CheckLocalTypeForField ( ctx context . Context , fieldType string , fieldValue interface { } ) ( string , error ) // See Core.CheckLocalTypeForField
2018-12-15 15:50:39 +08:00
}
2020-03-08 00:17:42 +08:00
// Core is the base struct for database management.
type Core struct {
2022-11-03 20:22:36 +08:00
db DB // DB interface object.
ctx context . Context // Context for chaining operation only. Do not set a default value in Core initialization.
group string // Configuration group name.
schema string // Custom schema for this object.
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.
logger glog . ILogger // Logger for logging functionality.
config * ConfigNode // Current config node.
dynamicConfig dynamicConfig // Dynamic configurations, which can be changed in runtime.
}
type dynamicConfig struct {
MaxIdleConnCount int
MaxOpenConnCount int
MaxConnLifeTime time . Duration
2018-07-05 19:36:35 +08:00
}
2021-12-27 20:51:26 +08:00
// DoCommitInput is the input parameters for function DoCommit.
type DoCommitInput struct {
2022-01-01 15:47:17 +08:00
Db * sql . DB
Tx * sql . Tx
Stmt * sql . Stmt
Link Link
Sql string
Args [ ] interface { }
Type string
IsTransaction bool
2021-12-27 20:51:26 +08:00
}
// DoCommitOutput is the output parameters for function DoCommit.
type DoCommitOutput struct {
2022-01-01 15:47:17 +08:00
Result sql . Result // Result is the result of exec statement.
Records [ ] Record // Records is the result of query statement.
Stmt * Stmt // Stmt is the Statement object result for Prepare.
Tx * TX // Tx is the transaction object result for Begin.
RawResult interface { } // RawResult is the underlying result, which might be sql.Result/*sql.Rows/*sql.Row.
2021-12-27 20:51:26 +08:00
}
2020-03-08 00:17:42 +08:00
// Driver is the interface for integrating sql drivers into package gdb.
type Driver interface {
2020-03-08 11:03:18 +08:00
// 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.
2022-03-21 21:17:48 +08:00
// Note that, any operation using `Link` will have no SQL logging.
2021-05-21 15:30:21 +08:00
type Link interface {
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 )
2022-03-21 21:17:48 +08:00
IsOnMaster ( ) bool
2021-05-21 15:30:21 +08:00
IsTransaction ( ) bool
}
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 {
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.
2022-11-03 20:22:36 +08:00
Schema string // Schema is the schema name of the configuration that the sql is executed from.
2021-05-21 15:30:21 +08:00
IsTransaction bool // IsTransaction marks whether this sql is executed in transaction.
2021-09-29 20:39:02 +08:00
RowsAffected int64 // RowsAffected marks retrieved or affected number with current sql statement.
2017-11-23 10:21:28 +08:00
}
2021-06-16 21:44:31 +08:00
// DoInsertOption is the input struct for function DoInsert.
type DoInsertOption struct {
2022-03-14 19:36:43 +08:00
OnDuplicateStr string // Custom string for `on duplicated` statement.
OnDuplicateMap map [ string ] interface { } // Custom key-value map from `OnDuplicateEx` function for `on duplicated` statement.
InsertOption int // Insert operation in constant value.
BatchCount int // Batch count for batch inserting.
2021-06-16 21:44:31 +08:00
}
2020-02-08 23:46:10 +08:00
// TableField is the struct for table field.
2019-09-02 15:48:25 +08:00
type TableField struct {
2020-02-08 23:46:10 +08:00
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.
2021-11-27 12:59:41 +08:00
Key string // The index information(empty if it's not an index).
2020-02-08 23:46:10 +08:00
Default interface { } // Default value for the field.
Extra string // Extra information.
2021-11-27 12:59:41 +08:00
Comment string // Field comment.
2019-09-02 15:48:25 +08:00
}
2020-11-28 13:52:17 +08:00
// Counter is the type for update count.
type Counter struct {
Field string
Value float64
}
2020-04-08 21:26:14 +08:00
type (
2020-12-09 01:29:23 +08:00
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.
2020-04-08 21:26:14 +08:00
)
2017-11-23 10:21:28 +08:00
2022-09-26 22:11:13 +08:00
type CatchSQLManager struct {
SQLArray * garray . StrArray
DoCommit bool
}
2022-11-16 10:04:49 +08:00
type queryType int
2018-12-28 21:56:27 +08:00
const (
2022-11-16 10:04:49 +08:00
defaultModelSafe = false
defaultCharset = ` utf8 `
defaultProtocol = ` tcp `
queryTypeNormal queryType = 0
queryTypeCount queryType = 1
queryTypeValue queryType = 2
unionTypeNormal = 0
unionTypeAll = 1
defaultMaxIdleConnCount = 10 // Max idle connection count in pool.
defaultMaxOpenConnCount = 0 // Max open connection count in pool. Default is no limit.
defaultMaxConnLifeTime = 30 * time . Second // Max lifetime for per connection in pool in seconds.
ctxTimeoutTypeExec = 0
ctxTimeoutTypeQuery = 1
ctxTimeoutTypePrepare = 2
2022-09-26 22:11:13 +08:00
cachePrefixTableFields = ` TableFields: `
cachePrefixSelectCache = ` SelectCache: `
commandEnvKeyForDryRun = "gf.gdb.dryrun"
modelForDaoSuffix = ` ForDao `
dbRoleSlave = ` slave `
ctxKeyForDB gctx . StrKey = ` CtxKeyForDB `
ctxKeyCatchSQL gctx . StrKey = ` CtxKeyCatchSQL `
ctxKeyInternalProducedSQL gctx . StrKey = ` CtxKeyInternalProducedSQL `
2022-09-30 15:41:51 +08:00
// type:[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
2022-12-22 17:33:51 +08:00
linkPattern = ` (\w+):([\w\-]*):(.*?)@(\w+?)\((.+?)\)/ { 0,1}([^\?]*)\? { 0,1}(.*) `
2019-04-02 14:37:46 +08:00
)
2019-02-27 09:38:10 +08:00
2022-01-21 17:31:48 +08:00
const (
InsertOptionDefault = 0
InsertOptionReplace = 1
InsertOptionSave = 2
InsertOptionIgnore = 3
)
2021-12-27 20:51:26 +08:00
const (
2022-01-01 15:47:17 +08:00
SqlTypeBegin = "DB.Begin"
SqlTypeTXCommit = "TX.Commit"
SqlTypeTXRollback = "TX.Rollback"
SqlTypeExecContext = "DB.ExecContext"
SqlTypeQueryContext = "DB.QueryContext"
SqlTypePrepareContext = "DB.PrepareContext"
SqlTypeStmtExecContext = "DB.Statement.ExecContext"
SqlTypeStmtQueryContext = "DB.Statement.QueryContext"
SqlTypeStmtQueryRowContext = "DB.Statement.QueryRowContext"
2021-12-27 20:51:26 +08:00
)
2022-08-11 21:47:35 +08:00
const (
LocalTypeString = "string"
LocalTypeDate = "date"
LocalTypeDatetime = "datetime"
LocalTypeInt = "int"
LocalTypeUint = "uint"
LocalTypeInt64 = "int64"
LocalTypeUint64 = "uint64"
LocalTypeIntSlice = "[]int"
LocalTypeInt64Slice = "[]int64"
LocalTypeUint64Slice = "[]uint64"
LocalTypeInt64Bytes = "int64-bytes"
LocalTypeUint64Bytes = "uint64-bytes"
LocalTypeFloat32 = "float32"
LocalTypeFloat64 = "float64"
LocalTypeBytes = "[]byte"
LocalTypeBool = "bool"
LocalTypeJson = "json"
LocalTypeJsonb = "jsonb"
)
2019-04-02 14:37:46 +08:00
var (
2020-03-08 00:17:42 +08:00
// instances is the management map for instances.
2019-07-23 23:20:27 +08:00
instances = gmap . NewStrAnyMap ( true )
2020-06-28 23:03:41 +08:00
2020-03-08 00:17:42 +08:00
// driverMap manages all custom registered driver.
2022-05-10 16:31:56 +08:00
driverMap = map [ string ] Driver { }
2020-06-28 23:03:41 +08:00
// 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\.\-]+$ `
2020-07-30 23:08:27 +08:00
2021-01-20 13:09:59 +08:00
// 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\-]+$ `
2021-01-20 13:09:59 +08:00
2020-07-30 23:08:27 +08:00
// allDryRun sets dry-run feature for all database connections.
// It is commonly used for command options for convenience.
allDryRun = false
2022-09-26 22:11:13 +08:00
// tableFieldsMap caches the table information retrieved from database.
tableFieldsMap = gmap . NewStrAnyMap ( true )
2018-12-28 21:56:27 +08:00
)
2020-07-30 23:08:27 +08:00
func init ( ) {
// allDryRun is initialized from environment or command options.
2021-06-26 12:08:18 +08:00
allDryRun = gcmd . GetOptWithEnv ( commandEnvKeyForDryRun , false ) . Bool ( )
2020-07-30 23:08:27 +08:00
}
2020-03-08 00:17:42 +08:00
// Register registers custom database driver to gdb.
func Register ( name string , driver Driver ) error {
2022-09-26 22:11:13 +08:00
driverMap [ name ] = newDriverWrapper ( driver )
2020-03-08 00:17:42 +08:00
return nil
}
2022-01-12 21:57:46 +08:00
// New creates and returns an ORM object with given configuration node.
func New ( node ConfigNode ) ( db DB , err error ) {
2022-09-26 22:11:13 +08:00
return newDBByConfigNode ( & node , "" )
2022-01-12 21:57:46 +08:00
}
// NewByGroup 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.
2022-01-12 21:57:46 +08:00
func NewByGroup ( group ... string ) ( db DB , err error ) {
2020-08-31 15:39:27 +08:00
groupName := configs . group
if len ( group ) > 0 && group [ 0 ] != "" {
groupName = group [ 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 {
2021-07-20 23:02:02 +08:00
return nil , gerror . NewCode (
2021-08-24 21:18:59 +08:00
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
}
2020-08-31 15:39:27 +08:00
if _ , ok := configs . config [ groupName ] ; ok {
2022-01-12 21:57:46 +08:00
var node * ConfigNode
if node , err = getConfigNodeByGroup ( groupName , true ) ; err == nil {
2022-09-26 22:11:13 +08:00
return newDBByConfigNode ( node , groupName )
2019-06-19 09:06:52 +08:00
}
2022-07-12 21:26:18 +08:00
return nil , err
2018-08-08 09:09:28 +08:00
}
2022-07-12 21:26:18 +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 ,
)
2017-11-23 10:21:28 +08:00
}
2022-09-26 22:11:13 +08:00
// newDBByConfigNode creates and returns an ORM object with given configuration node and group name.
2022-11-03 20:22:36 +08:00
//
// Very Note:
// The parameter `node` is used for DB creation, not for underlying connection creation.
// So all db type configurations in the same group should be the same.
2022-09-26 22:11:13 +08:00
func newDBByConfigNode ( node * ConfigNode , group string ) ( db DB , err error ) {
2022-09-30 15:41:51 +08:00
if node . Link != "" {
node = parseConfigNodeLink ( node )
}
2022-01-12 21:57:46 +08:00
c := & Core {
group : group ,
debug : gtype . NewBool ( ) ,
cache : gcache . New ( ) ,
links : gmap . NewStrAnyMap ( true ) ,
logger : glog . New ( ) ,
2022-09-26 22:11:13 +08:00
config : node ,
2022-11-03 20:22:36 +08:00
dynamicConfig : dynamicConfig {
MaxIdleConnCount : node . MaxIdleConnCount ,
MaxOpenConnCount : node . MaxOpenConnCount ,
MaxConnLifeTime : node . MaxConnLifeTime ,
} ,
2022-01-12 21:57:46 +08:00
}
if v , ok := driverMap [ node . Type ] ; ok {
2022-09-26 22:11:13 +08:00
if c . db , err = v . New ( c , node ) ; err != nil {
2022-01-12 21:57:46 +08:00
return nil , err
}
return c . db , nil
}
2022-02-22 10:43:31 +08:00
errorMsg := ` cannot find database driver for specified database type "%s" `
2022-03-10 14:29:49 +08:00
errorMsg += ` , did you misspell type name "%s" or forget importing the database driver? `
errorMsg += ` possible reference: https://github.com/gogf/gf/tree/master/contrib/drivers `
2022-02-22 10:43:31 +08:00
return nil , gerror . NewCodef ( gcode . CodeInvalidConfiguration , errorMsg , node . Type , node . Type )
2022-01-12 21:57:46 +08:00
}
2019-04-03 23:39:31 +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.
2019-04-03 23:39:31 +08:00
func Instance ( name ... string ) ( db DB , err error ) {
2020-02-10 20:37:53 +08:00
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 { } {
2022-01-12 21:57:46 +08:00
db , err = NewByGroup ( group )
2019-06-19 09:06:52 +08:00
return db
} )
if v != nil {
return v . ( DB ) , nil
}
return
2019-04-02 14:37:46 +08:00
}
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.
2020-02-08 23:46:10 +08:00
//
2021-02-08 17:57:21 +08:00
// The parameter `master` specifies whether retrieving a master node, or else a slave node
2020-02-08 23:46:10 +08:00
// if master-slave configured.
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 {
2020-02-08 23:46:10 +08:00
// Separates master and slave configuration nodes array.
2021-12-21 22:59:14 +08:00
var (
masterList = make ( ConfigGroup , 0 )
slaveList = make ( ConfigGroup , 0 )
)
2019-06-19 09:06:52 +08:00
for i := 0 ; i < len ( list ) ; i ++ {
2021-12-21 22:59:14 +08:00
if list [ i ] . Role == dbRoleSlave {
2019-06-19 09:06:52 +08:00
slaveList = append ( slaveList , list [ i ] )
} else {
masterList = append ( masterList , list [ i ] )
}
}
if len ( masterList ) < 1 {
2021-12-04 13:57:38 +08:00
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
}
}
2022-07-12 21:26:18 +08:00
return nil , gerror . NewCodef (
gcode . CodeInvalidConfiguration ,
"empty database configuration for item name '%s'" ,
group ,
)
2018-10-13 20:29:27 +08:00
}
2020-02-08 23:46:10 +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;
2021-11-16 17:21:13 +08:00
// 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
}
2021-08-24 22:59:25 +08:00
// If total is 0 means all the nodes have no weight attribute configured.
2020-02-08 23:46:10 +08:00
// It then defaults each node's weight attribute to 1.
2018-12-16 23:11:15 +08:00
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
}
}
2020-02-08 23:46:10 +08:00
// Exclude the right border value.
2022-01-22 13:48:09 +08:00
var (
min = 0
max = 0
random = grand . N ( 0 , total - 1 )
)
2018-08-08 09:09:28 +08:00
for i := 0 ; i < len ( cg ) ; i ++ {
2019-07-06 16:14:45 +08:00
max = min + cg [ i ] . Weight * 100
2022-01-22 13:48:09 +08:00
if random >= min && random < max {
2022-11-03 20:22:36 +08:00
// ====================================================
// Return a COPY of the ConfigNode.
// ====================================================
2022-07-12 21:26:18 +08:00
node := ConfigNode { }
node = cg [ i ]
return & node
2018-08-08 09:09:28 +08:00
}
2022-07-12 21:26:18 +08:00
min = max
2018-08-08 09:09:28 +08:00
}
return nil
2017-11-23 10:21:28 +08:00
}
2022-01-12 21:57:46 +08:00
// getSqlDb retrieves and returns an 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 ) {
2022-11-03 20:22:36 +08:00
var (
node * ConfigNode
ctx = c . db . GetCtx ( )
)
2022-01-12 21:57:46 +08:00
if c . group != "" {
2022-11-03 20:22:36 +08:00
// Load balance.
2022-07-12 21:26:18 +08:00
configs . RLock ( )
defer configs . RUnlock ( )
2022-11-03 20:22:36 +08:00
// Value COPY for node.
2022-01-12 21:57:46 +08:00
node , err = getConfigNodeByGroup ( c . group , master )
if err != nil {
return nil , err
}
} else {
2022-11-03 20:22:36 +08:00
// Value COPY for node.
n := * c . db . GetConfig ( )
node = & n
2019-06-19 09:06:52 +08:00
}
if node . Charset == "" {
2021-12-04 13:57:38 +08:00
node . Charset = defaultCharset
2019-06-19 09:06:52 +08:00
}
2020-01-07 22:14:32 +08:00
// Changes the schema.
2022-09-26 22:11:13 +08:00
nodeSchema := gutil . GetOrDefaultStr ( c . schema , schema ... )
2020-01-07 22:14:32 +08:00
if nodeSchema != "" {
2022-11-03 20:22:36 +08:00
node . Name = nodeSchema
}
// Update the configuration object in internal data.
internalData := c . GetInternalCtxDataFromCtx ( ctx )
if internalData != nil {
internalData . ConfigNode = node
2020-01-07 22:14:32 +08:00
}
2020-03-13 17:21:30 +08:00
// Cache the underlying connection pool object by node.
2022-11-03 20:22:36 +08:00
instanceNameByNode := fmt . Sprintf ( ` %+v ` , node )
instanceValue := c . links . GetOrSetFuncLock ( instanceNameByNode , func ( ) interface { } {
2021-12-21 22:59:14 +08:00
if sqlDb , err = c . db . Open ( node ) ; err != nil {
2021-08-24 22:59:25 +08:00
return nil
2019-06-19 09:06:52 +08:00
}
2022-05-10 16:31:56 +08:00
if sqlDb == nil {
return nil
}
2022-11-03 20:22:36 +08:00
if c . dynamicConfig . MaxIdleConnCount > 0 {
sqlDb . SetMaxIdleConns ( c . dynamicConfig . MaxIdleConnCount )
2020-12-09 01:29:23 +08:00
} else {
sqlDb . SetMaxIdleConns ( defaultMaxIdleConnCount )
2019-06-19 09:06:52 +08:00
}
2022-11-03 20:22:36 +08:00
if c . dynamicConfig . MaxOpenConnCount > 0 {
sqlDb . SetMaxOpenConns ( c . dynamicConfig . MaxOpenConnCount )
2020-12-09 01:29:23 +08:00
} else {
sqlDb . SetMaxOpenConns ( defaultMaxOpenConnCount )
2019-06-19 09:06:52 +08:00
}
2022-11-03 20:22:36 +08:00
if c . dynamicConfig . MaxConnLifeTime > 0 {
sqlDb . SetConnMaxLifetime ( c . dynamicConfig . MaxConnLifeTime )
2020-12-09 01:29:23 +08:00
} else {
sqlDb . SetConnMaxLifetime ( defaultMaxConnLifeTime )
2019-06-19 09:06:52 +08:00
}
2021-08-24 22:59:25 +08:00
return sqlDb
} )
2022-11-03 20:22:36 +08:00
if instanceValue != nil && sqlDb == nil {
// It reads from instance map.
sqlDb = instanceValue . ( * sql . DB )
2019-06-19 09:06:52 +08:00
}
2019-07-15 19:54:13 +08:00
if node . Debug {
2021-02-08 17:57:21 +08:00
c . db . SetDebug ( node . Debug )
2019-07-15 17:40:21 +08:00
}
2021-02-03 22:46:59 +08:00
if node . DryRun {
2021-02-08 17:57:21 +08:00
c . db . SetDryRun ( node . DryRun )
2020-04-02 20:52:37 +08:00
}
2019-06-19 09:06:52 +08:00
return
2018-10-13 20:29:27 +08:00
}