gdb.go 26 KB

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