gdb.go 22 KB

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