chainable_api.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. package gorm
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "gorm.io/gorm/clause"
  7. "gorm.io/gorm/utils"
  8. )
  9. // Model specify the model you would like to run db operations
  10. //
  11. // // update all users's name to `hello`
  12. // db.Model(&User{}).Update("name", "hello")
  13. // // if user's primary key is non-blank, will use it as condition, then will only update that user's name to `hello`
  14. // db.Model(&user).Update("name", "hello")
  15. func (db *DB) Model(value interface{}) (tx *DB) {
  16. tx = db.getInstance()
  17. tx.Statement.Model = value
  18. return
  19. }
  20. // Clauses Add clauses
  21. //
  22. // This supports both standard clauses (clause.OrderBy, clause.Limit, clause.Where) and more
  23. // advanced techniques like specifying lock strength and optimizer hints. See the
  24. // [docs] for more depth.
  25. //
  26. // // add a simple limit clause
  27. // db.Clauses(clause.Limit{Limit: 1}).Find(&User{})
  28. // // tell the optimizer to use the `idx_user_name` index
  29. // db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{})
  30. // // specify the lock strength to UPDATE
  31. // db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
  32. //
  33. // [docs]: https://gorm.io/docs/sql_builder.html#Clauses
  34. func (db *DB) Clauses(conds ...clause.Expression) (tx *DB) {
  35. tx = db.getInstance()
  36. var whereConds []interface{}
  37. for _, cond := range conds {
  38. if c, ok := cond.(clause.Interface); ok {
  39. tx.Statement.AddClause(c)
  40. } else if optimizer, ok := cond.(StatementModifier); ok {
  41. optimizer.ModifyStatement(tx.Statement)
  42. } else {
  43. whereConds = append(whereConds, cond)
  44. }
  45. }
  46. if len(whereConds) > 0 {
  47. tx.Statement.AddClause(clause.Where{Exprs: tx.Statement.BuildCondition(whereConds[0], whereConds[1:]...)})
  48. }
  49. return
  50. }
  51. var tableRegexp = regexp.MustCompile(`(?i)(?:.+? AS (\w+)\s*(?:$|,)|^\w+\s+(\w+)$)`)
  52. // Table specify the table you would like to run db operations
  53. //
  54. // // Get a user
  55. // db.Table("users").Take(&result)
  56. func (db *DB) Table(name string, args ...interface{}) (tx *DB) {
  57. tx = db.getInstance()
  58. if strings.Contains(name, " ") || strings.Contains(name, "`") || len(args) > 0 {
  59. tx.Statement.TableExpr = &clause.Expr{SQL: name, Vars: args}
  60. if results := tableRegexp.FindStringSubmatch(name); len(results) == 3 {
  61. if results[1] != "" {
  62. tx.Statement.Table = results[1]
  63. } else {
  64. tx.Statement.Table = results[2]
  65. }
  66. }
  67. } else if tables := strings.Split(name, "."); len(tables) == 2 {
  68. tx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}
  69. tx.Statement.Table = tables[1]
  70. } else if name != "" {
  71. tx.Statement.TableExpr = &clause.Expr{SQL: tx.Statement.Quote(name)}
  72. tx.Statement.Table = name
  73. } else {
  74. tx.Statement.TableExpr = nil
  75. tx.Statement.Table = ""
  76. }
  77. return
  78. }
  79. // Distinct specify distinct fields that you want querying
  80. //
  81. // // Select distinct names of users
  82. // db.Distinct("name").Find(&results)
  83. // // Select distinct name/age pairs from users
  84. // db.Distinct("name", "age").Find(&results)
  85. func (db *DB) Distinct(args ...interface{}) (tx *DB) {
  86. tx = db.getInstance()
  87. tx.Statement.Distinct = true
  88. if len(args) > 0 {
  89. tx = tx.Select(args[0], args[1:]...)
  90. }
  91. return
  92. }
  93. // Select specify fields that you want when querying, creating, updating
  94. //
  95. // Use Select when you only want a subset of the fields. By default, GORM will select all fields.
  96. // Select accepts both string arguments and arrays.
  97. //
  98. // // Select name and age of user using multiple arguments
  99. // db.Select("name", "age").Find(&users)
  100. // // Select name and age of user using an array
  101. // db.Select([]string{"name", "age"}).Find(&users)
  102. func (db *DB) Select(query interface{}, args ...interface{}) (tx *DB) {
  103. tx = db.getInstance()
  104. switch v := query.(type) {
  105. case []string:
  106. tx.Statement.Selects = v
  107. for _, arg := range args {
  108. switch arg := arg.(type) {
  109. case string:
  110. tx.Statement.Selects = append(tx.Statement.Selects, arg)
  111. case []string:
  112. tx.Statement.Selects = append(tx.Statement.Selects, arg...)
  113. default:
  114. tx.AddError(fmt.Errorf("unsupported select args %v %v", query, args))
  115. return
  116. }
  117. }
  118. if clause, ok := tx.Statement.Clauses["SELECT"]; ok {
  119. clause.Expression = nil
  120. tx.Statement.Clauses["SELECT"] = clause
  121. }
  122. case string:
  123. if strings.Count(v, "?") >= len(args) && len(args) > 0 {
  124. tx.Statement.AddClause(clause.Select{
  125. Distinct: db.Statement.Distinct,
  126. Expression: clause.Expr{SQL: v, Vars: args},
  127. })
  128. } else if strings.Count(v, "@") > 0 && len(args) > 0 {
  129. tx.Statement.AddClause(clause.Select{
  130. Distinct: db.Statement.Distinct,
  131. Expression: clause.NamedExpr{SQL: v, Vars: args},
  132. })
  133. } else {
  134. tx.Statement.Selects = []string{v}
  135. for _, arg := range args {
  136. switch arg := arg.(type) {
  137. case string:
  138. tx.Statement.Selects = append(tx.Statement.Selects, arg)
  139. case []string:
  140. tx.Statement.Selects = append(tx.Statement.Selects, arg...)
  141. default:
  142. tx.Statement.AddClause(clause.Select{
  143. Distinct: db.Statement.Distinct,
  144. Expression: clause.Expr{SQL: v, Vars: args},
  145. })
  146. return
  147. }
  148. }
  149. if clause, ok := tx.Statement.Clauses["SELECT"]; ok {
  150. clause.Expression = nil
  151. tx.Statement.Clauses["SELECT"] = clause
  152. }
  153. }
  154. default:
  155. tx.AddError(fmt.Errorf("unsupported select args %v %v", query, args))
  156. }
  157. return
  158. }
  159. // Omit specify fields that you want to ignore when creating, updating and querying
  160. func (db *DB) Omit(columns ...string) (tx *DB) {
  161. tx = db.getInstance()
  162. if len(columns) == 1 && strings.ContainsRune(columns[0], ',') {
  163. tx.Statement.Omits = strings.FieldsFunc(columns[0], utils.IsValidDBNameChar)
  164. } else {
  165. tx.Statement.Omits = columns
  166. }
  167. return
  168. }
  169. // MapColumns modify the column names in the query results to facilitate align to the corresponding structural fields
  170. func (db *DB) MapColumns(m map[string]string) (tx *DB) {
  171. tx = db.getInstance()
  172. tx.Statement.ColumnMapping = m
  173. return
  174. }
  175. // Where add conditions
  176. //
  177. // See the [docs] for details on the various formats that where clauses can take. By default, where clauses chain with AND.
  178. //
  179. // // Find the first user with name jinzhu
  180. // db.Where("name = ?", "jinzhu").First(&user)
  181. // // Find the first user with name jinzhu and age 20
  182. // db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
  183. // // Find the first user with name jinzhu and age not equal to 20
  184. // db.Where("name = ?", "jinzhu").Where("age <> ?", "20").First(&user)
  185. //
  186. // [docs]: https://gorm.io/docs/query.html#Conditions
  187. func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
  188. tx = db.getInstance()
  189. if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
  190. tx.Statement.AddClause(clause.Where{Exprs: conds})
  191. }
  192. return
  193. }
  194. // Not add NOT conditions
  195. //
  196. // Not works similarly to where, and has the same syntax.
  197. //
  198. // // Find the first user with name not equal to jinzhu
  199. // db.Not("name = ?", "jinzhu").First(&user)
  200. func (db *DB) Not(query interface{}, args ...interface{}) (tx *DB) {
  201. tx = db.getInstance()
  202. if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
  203. tx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Not(conds...)}})
  204. }
  205. return
  206. }
  207. // Or add OR conditions
  208. //
  209. // Or is used to chain together queries with an OR.
  210. //
  211. // // Find the first user with name equal to jinzhu or john
  212. // db.Where("name = ?", "jinzhu").Or("name = ?", "john").First(&user)
  213. func (db *DB) Or(query interface{}, args ...interface{}) (tx *DB) {
  214. tx = db.getInstance()
  215. if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
  216. tx.Statement.AddClause(clause.Where{Exprs: []clause.Expression{clause.Or(clause.And(conds...))}})
  217. }
  218. return
  219. }
  220. // Joins specify Joins conditions
  221. //
  222. // db.Joins("Account").Find(&user)
  223. // db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
  224. // db.Joins("Account", DB.Select("id").Where("user_id = users.id AND name = ?", "someName").Model(&Account{}))
  225. func (db *DB) Joins(query string, args ...interface{}) (tx *DB) {
  226. return joins(db, clause.LeftJoin, query, args...)
  227. }
  228. // InnerJoins specify inner joins conditions
  229. // db.InnerJoins("Account").Find(&user)
  230. func (db *DB) InnerJoins(query string, args ...interface{}) (tx *DB) {
  231. return joins(db, clause.InnerJoin, query, args...)
  232. }
  233. func joins(db *DB, joinType clause.JoinType, query string, args ...interface{}) (tx *DB) {
  234. tx = db.getInstance()
  235. if len(args) == 1 {
  236. if db, ok := args[0].(*DB); ok {
  237. j := join{
  238. Name: query, Conds: args, Selects: db.Statement.Selects,
  239. Omits: db.Statement.Omits, JoinType: joinType,
  240. }
  241. if where, ok := db.Statement.Clauses["WHERE"].Expression.(clause.Where); ok {
  242. j.On = &where
  243. }
  244. tx.Statement.Joins = append(tx.Statement.Joins, j)
  245. return
  246. }
  247. }
  248. tx.Statement.Joins = append(tx.Statement.Joins, join{Name: query, Conds: args, JoinType: joinType})
  249. return
  250. }
  251. // Group specify the group method on the find
  252. //
  253. // // Select the sum age of users with given names
  254. // db.Model(&User{}).Select("name, sum(age) as total").Group("name").Find(&results)
  255. func (db *DB) Group(name string) (tx *DB) {
  256. tx = db.getInstance()
  257. fields := strings.FieldsFunc(name, utils.IsValidDBNameChar)
  258. tx.Statement.AddClause(clause.GroupBy{
  259. Columns: []clause.Column{{Name: name, Raw: len(fields) != 1}},
  260. })
  261. return
  262. }
  263. // Having specify HAVING conditions for GROUP BY
  264. //
  265. // // Select the sum age of users with name jinzhu
  266. // db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "jinzhu").Find(&result)
  267. func (db *DB) Having(query interface{}, args ...interface{}) (tx *DB) {
  268. tx = db.getInstance()
  269. tx.Statement.AddClause(clause.GroupBy{
  270. Having: tx.Statement.BuildCondition(query, args...),
  271. })
  272. return
  273. }
  274. // Order specify order when retrieving records from database
  275. //
  276. // db.Order("name DESC")
  277. // db.Order(clause.OrderByColumn{Column: clause.Column{Name: "name"}, Desc: true})
  278. // db.Order(clause.OrderBy{Columns: []clause.OrderByColumn{
  279. // {Column: clause.Column{Name: "name"}, Desc: true},
  280. // {Column: clause.Column{Name: "age"}, Desc: true},
  281. // }})
  282. func (db *DB) Order(value interface{}) (tx *DB) {
  283. tx = db.getInstance()
  284. switch v := value.(type) {
  285. case clause.OrderBy:
  286. tx.Statement.AddClause(v)
  287. case clause.OrderByColumn:
  288. tx.Statement.AddClause(clause.OrderBy{
  289. Columns: []clause.OrderByColumn{v},
  290. })
  291. case string:
  292. if v != "" {
  293. tx.Statement.AddClause(clause.OrderBy{
  294. Columns: []clause.OrderByColumn{{
  295. Column: clause.Column{Name: v, Raw: true},
  296. }},
  297. })
  298. }
  299. }
  300. return
  301. }
  302. // Limit specify the number of records to be retrieved
  303. //
  304. // Limit conditions can be cancelled by using `Limit(-1)`.
  305. //
  306. // // retrieve 3 users
  307. // db.Limit(3).Find(&users)
  308. // // retrieve 3 users into users1, and all users into users2
  309. // db.Limit(3).Find(&users1).Limit(-1).Find(&users2)
  310. func (db *DB) Limit(limit int) (tx *DB) {
  311. tx = db.getInstance()
  312. tx.Statement.AddClause(clause.Limit{Limit: &limit})
  313. return
  314. }
  315. // Offset specify the number of records to skip before starting to return the records
  316. //
  317. // Offset conditions can be cancelled by using `Offset(-1)`.
  318. //
  319. // // select the third user
  320. // db.Offset(2).First(&user)
  321. // // select the first user by cancelling an earlier chained offset
  322. // db.Offset(5).Offset(-1).First(&user)
  323. func (db *DB) Offset(offset int) (tx *DB) {
  324. tx = db.getInstance()
  325. tx.Statement.AddClause(clause.Limit{Offset: offset})
  326. return
  327. }
  328. // Scopes pass current database connection to arguments `func(DB) DB`, which could be used to add conditions dynamically
  329. //
  330. // func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
  331. // return db.Where("amount > ?", 1000)
  332. // }
  333. //
  334. // func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
  335. // return func (db *gorm.DB) *gorm.DB {
  336. // return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
  337. // }
  338. // }
  339. //
  340. // db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
  341. func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
  342. tx = db.getInstance()
  343. tx.Statement.scopes = append(tx.Statement.scopes, funcs...)
  344. return tx
  345. }
  346. func (db *DB) executeScopes() (tx *DB) {
  347. scopes := db.Statement.scopes
  348. db.Statement.scopes = nil
  349. for _, scope := range scopes {
  350. db = scope(db)
  351. }
  352. return db
  353. }
  354. // Preload preload associations with given conditions
  355. //
  356. // // get all users, and preload all non-cancelled orders
  357. // db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
  358. func (db *DB) Preload(query string, args ...interface{}) (tx *DB) {
  359. tx = db.getInstance()
  360. if tx.Statement.Preloads == nil {
  361. tx.Statement.Preloads = map[string][]interface{}{}
  362. }
  363. tx.Statement.Preloads[query] = args
  364. return
  365. }
  366. // Attrs provide attributes used in [FirstOrCreate] or [FirstOrInit]
  367. //
  368. // Attrs only adds attributes if the record is not found.
  369. //
  370. // // assign an email if the record is not found
  371. // db.Where(User{Name: "non_existing"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
  372. // // user -> User{Name: "non_existing", Email: "fake@fake.org"}
  373. //
  374. // // assign an email if the record is not found, otherwise ignore provided email
  375. // db.Where(User{Name: "jinzhu"}).Attrs(User{Email: "fake@fake.org"}).FirstOrInit(&user)
  376. // // user -> User{Name: "jinzhu", Age: 20}
  377. //
  378. // [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
  379. // [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
  380. func (db *DB) Attrs(attrs ...interface{}) (tx *DB) {
  381. tx = db.getInstance()
  382. tx.Statement.attrs = attrs
  383. return
  384. }
  385. // Assign provide attributes used in [FirstOrCreate] or [FirstOrInit]
  386. //
  387. // Assign adds attributes even if the record is found. If using FirstOrCreate, this means that
  388. // records will be updated even if they are found.
  389. //
  390. // // assign an email regardless of if the record is not found
  391. // db.Where(User{Name: "non_existing"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
  392. // // user -> User{Name: "non_existing", Email: "fake@fake.org"}
  393. //
  394. // // assign email regardless of if record is found
  395. // db.Where(User{Name: "jinzhu"}).Assign(User{Email: "fake@fake.org"}).FirstOrInit(&user)
  396. // // user -> User{Name: "jinzhu", Age: 20, Email: "fake@fake.org"}
  397. //
  398. // [FirstOrCreate]: https://gorm.io/docs/advanced_query.html#FirstOrCreate
  399. // [FirstOrInit]: https://gorm.io/docs/advanced_query.html#FirstOrInit
  400. func (db *DB) Assign(attrs ...interface{}) (tx *DB) {
  401. tx = db.getInstance()
  402. tx.Statement.assigns = attrs
  403. return
  404. }
  405. // Unscoped disables the global scope of soft deletion in a query.
  406. // By default, GORM uses soft deletion, marking records as "deleted"
  407. // by setting a timestamp on a specific field (e.g., `deleted_at`).
  408. // Unscoped allows queries to include records marked as deleted,
  409. // overriding the soft deletion behavior.
  410. // Example:
  411. // var users []User
  412. // db.Unscoped().Find(&users)
  413. // // Retrieves all users, including deleted ones.
  414. func (db *DB) Unscoped() (tx *DB) {
  415. tx = db.getInstance()
  416. tx.Statement.Unscoped = true
  417. return
  418. }
  419. func (db *DB) Raw(sql string, values ...interface{}) (tx *DB) {
  420. tx = db.getInstance()
  421. tx.Statement.SQL = strings.Builder{}
  422. if strings.Contains(sql, "@") {
  423. clause.NamedExpr{SQL: sql, Vars: values}.Build(tx.Statement)
  424. } else {
  425. clause.Expr{SQL: sql, Vars: values}.Build(tx.Statement)
  426. }
  427. return
  428. }