Simple and Powerful ORM for Go, support mysql,postgres,tidb,sqlite3,mssql,oracle https://xorm.io
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

359 lines
8.5 KiB

  1. // Copyright 2016 The Xorm Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package xorm
  5. import (
  6. "database/sql"
  7. "errors"
  8. "fmt"
  9. "reflect"
  10. "strconv"
  11. "xorm.io/xorm/caches"
  12. "xorm.io/xorm/internal/utils"
  13. "xorm.io/xorm/schemas"
  14. )
  15. // Get retrieve one record from database, bean's non-empty fields
  16. // will be as conditions
  17. func (session *Session) Get(bean interface{}) (bool, error) {
  18. if session.isAutoClose {
  19. defer session.Close()
  20. }
  21. return session.get(bean)
  22. }
  23. func (session *Session) get(bean interface{}) (bool, error) {
  24. defer session.resetStatement()
  25. if session.statement.LastError != nil {
  26. return false, session.statement.LastError
  27. }
  28. beanValue := reflect.ValueOf(bean)
  29. if beanValue.Kind() != reflect.Ptr {
  30. return false, errors.New("needs a pointer to a value")
  31. } else if beanValue.Elem().Kind() == reflect.Ptr {
  32. return false, errors.New("a pointer to a pointer is not allowed")
  33. }
  34. if beanValue.Elem().Kind() == reflect.Struct {
  35. if err := session.statement.SetRefBean(bean); err != nil {
  36. return false, err
  37. }
  38. }
  39. var sqlStr string
  40. var args []interface{}
  41. var err error
  42. if session.statement.RawSQL == "" {
  43. if len(session.statement.TableName()) <= 0 {
  44. return false, ErrTableNotFound
  45. }
  46. session.statement.Limit(1)
  47. sqlStr, args, err = session.statement.GenGetSQL(bean)
  48. if err != nil {
  49. return false, err
  50. }
  51. } else {
  52. sqlStr = session.statement.GenRawSQL()
  53. args = session.statement.RawParams
  54. }
  55. table := session.statement.RefTable
  56. if session.statement.ColumnMap.IsEmpty() && session.canCache() && beanValue.Elem().Kind() == reflect.Struct {
  57. if cacher := session.engine.GetCacher(session.statement.TableName()); cacher != nil &&
  58. !session.statement.GetUnscoped() {
  59. has, err := session.cacheGet(bean, sqlStr, args...)
  60. if err != ErrCacheFailed {
  61. return has, err
  62. }
  63. }
  64. }
  65. context := session.statement.Context
  66. if context != nil {
  67. res := context.Get(fmt.Sprintf("%v-%v", sqlStr, args))
  68. if res != nil {
  69. session.engine.logger.Debugf("hit context cache: %s", sqlStr)
  70. structValue := reflect.Indirect(reflect.ValueOf(bean))
  71. structValue.Set(reflect.Indirect(reflect.ValueOf(res)))
  72. session.lastSQL = ""
  73. session.lastSQLArgs = nil
  74. return true, nil
  75. }
  76. }
  77. has, err := session.nocacheGet(beanValue.Elem().Kind(), table, bean, sqlStr, args...)
  78. if err != nil || !has {
  79. return has, err
  80. }
  81. if context != nil {
  82. context.Put(fmt.Sprintf("%v-%v", sqlStr, args), bean)
  83. }
  84. return true, nil
  85. }
  86. func (session *Session) nocacheGet(beanKind reflect.Kind, table *schemas.Table, bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
  87. rows, err := session.queryRows(sqlStr, args...)
  88. if err != nil {
  89. return false, err
  90. }
  91. defer rows.Close()
  92. if !rows.Next() {
  93. if rows.Err() != nil {
  94. return false, rows.Err()
  95. }
  96. return false, nil
  97. }
  98. switch bean.(type) {
  99. case sql.NullInt64, sql.NullBool, sql.NullFloat64, sql.NullString:
  100. return true, rows.Scan(&bean)
  101. case *sql.NullInt64, *sql.NullBool, *sql.NullFloat64, *sql.NullString:
  102. return true, rows.Scan(bean)
  103. case *string:
  104. var res sql.NullString
  105. if err := rows.Scan(&res); err != nil {
  106. return true, err
  107. }
  108. if res.Valid {
  109. *(bean.(*string)) = res.String
  110. }
  111. return true, nil
  112. case *int:
  113. var res sql.NullInt64
  114. if err := rows.Scan(&res); err != nil {
  115. return true, err
  116. }
  117. if res.Valid {
  118. *(bean.(*int)) = int(res.Int64)
  119. }
  120. return true, nil
  121. case *int8:
  122. var res sql.NullInt64
  123. if err := rows.Scan(&res); err != nil {
  124. return true, err
  125. }
  126. if res.Valid {
  127. *(bean.(*int8)) = int8(res.Int64)
  128. }
  129. return true, nil
  130. case *int16:
  131. var res sql.NullInt64
  132. if err := rows.Scan(&res); err != nil {
  133. return true, err
  134. }
  135. if res.Valid {
  136. *(bean.(*int16)) = int16(res.Int64)
  137. }
  138. return true, nil
  139. case *int32:
  140. var res sql.NullInt64
  141. if err := rows.Scan(&res); err != nil {
  142. return true, err
  143. }
  144. if res.Valid {
  145. *(bean.(*int32)) = int32(res.Int64)
  146. }
  147. return true, nil
  148. case *int64:
  149. var res sql.NullInt64
  150. if err := rows.Scan(&res); err != nil {
  151. return true, err
  152. }
  153. if res.Valid {
  154. *(bean.(*int64)) = int64(res.Int64)
  155. }
  156. return true, nil
  157. case *uint:
  158. var res sql.NullInt64
  159. if err := rows.Scan(&res); err != nil {
  160. return true, err
  161. }
  162. if res.Valid {
  163. *(bean.(*uint)) = uint(res.Int64)
  164. }
  165. return true, nil
  166. case *uint8:
  167. var res sql.NullInt64
  168. if err := rows.Scan(&res); err != nil {
  169. return true, err
  170. }
  171. if res.Valid {
  172. *(bean.(*uint8)) = uint8(res.Int64)
  173. }
  174. return true, nil
  175. case *uint16:
  176. var res sql.NullInt64
  177. if err := rows.Scan(&res); err != nil {
  178. return true, err
  179. }
  180. if res.Valid {
  181. *(bean.(*uint16)) = uint16(res.Int64)
  182. }
  183. return true, nil
  184. case *uint32:
  185. var res sql.NullInt64
  186. if err := rows.Scan(&res); err != nil {
  187. return true, err
  188. }
  189. if res.Valid {
  190. *(bean.(*uint32)) = uint32(res.Int64)
  191. }
  192. return true, nil
  193. case *uint64:
  194. var res sql.NullInt64
  195. if err := rows.Scan(&res); err != nil {
  196. return true, err
  197. }
  198. if res.Valid {
  199. *(bean.(*uint64)) = uint64(res.Int64)
  200. }
  201. return true, nil
  202. case *bool:
  203. var res sql.NullBool
  204. if err := rows.Scan(&res); err != nil {
  205. return true, err
  206. }
  207. if res.Valid {
  208. *(bean.(*bool)) = res.Bool
  209. }
  210. return true, nil
  211. }
  212. switch beanKind {
  213. case reflect.Struct:
  214. fields, err := rows.Columns()
  215. if err != nil {
  216. // WARN: Alougth rows return true, but get fields failed
  217. return true, err
  218. }
  219. scanResults, err := session.row2Slice(rows, fields, bean)
  220. if err != nil {
  221. return false, err
  222. }
  223. // close it before convert data
  224. rows.Close()
  225. dataStruct := utils.ReflectValue(bean)
  226. _, err = session.slice2Bean(scanResults, fields, bean, &dataStruct, table)
  227. if err != nil {
  228. return true, err
  229. }
  230. return true, session.executeProcessors()
  231. case reflect.Slice:
  232. err = rows.ScanSlice(bean)
  233. case reflect.Map:
  234. err = rows.ScanMap(bean)
  235. case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
  236. reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  237. err = rows.Scan(bean)
  238. default:
  239. err = rows.Scan(bean)
  240. }
  241. return true, err
  242. }
  243. func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
  244. // if has no reftable, then don't use cache currently
  245. if !session.canCache() {
  246. return false, ErrCacheFailed
  247. }
  248. for _, filter := range session.engine.dialect.Filters() {
  249. sqlStr = filter.Do(sqlStr)
  250. }
  251. newsql := session.statement.ConvertIDSQL(sqlStr)
  252. if newsql == "" {
  253. return false, ErrCacheFailed
  254. }
  255. tableName := session.statement.TableName()
  256. cacher := session.engine.cacherMgr.GetCacher(tableName)
  257. session.engine.logger.Debugf("[cache] Get SQL: %s, %v", newsql, args)
  258. table := session.statement.RefTable
  259. ids, err := caches.GetCacheSql(cacher, tableName, newsql, args)
  260. if err != nil {
  261. var res = make([]string, len(table.PrimaryKeys))
  262. rows, err := session.NoCache().queryRows(newsql, args...)
  263. if err != nil {
  264. return false, err
  265. }
  266. defer rows.Close()
  267. if rows.Next() {
  268. err = rows.ScanSlice(&res)
  269. if err != nil {
  270. return false, err
  271. }
  272. } else {
  273. return false, ErrCacheFailed
  274. }
  275. var pk schemas.PK = make([]interface{}, len(table.PrimaryKeys))
  276. for i, col := range table.PKColumns() {
  277. if col.SQLType.IsText() {
  278. pk[i] = res[i]
  279. } else if col.SQLType.IsNumeric() {
  280. n, err := strconv.ParseInt(res[i], 10, 64)
  281. if err != nil {
  282. return false, err
  283. }
  284. pk[i] = n
  285. } else {
  286. return false, errors.New("unsupported")
  287. }
  288. }
  289. ids = []schemas.PK{pk}
  290. session.engine.logger.Debugf("[cache] cache ids: %s, %v", newsql, ids)
  291. err = caches.PutCacheSql(cacher, ids, tableName, newsql, args)
  292. if err != nil {
  293. return false, err
  294. }
  295. } else {
  296. session.engine.logger.Debugf("[cache] cache hit: %s, %v", newsql, ids)
  297. }
  298. if len(ids) > 0 {
  299. structValue := reflect.Indirect(reflect.ValueOf(bean))
  300. id := ids[0]
  301. session.engine.logger.Debugf("[cache] get bean: %s, %v", tableName, id)
  302. sid, err := id.ToString()
  303. if err != nil {
  304. return false, err
  305. }
  306. cacheBean := cacher.GetBean(tableName, sid)
  307. if cacheBean == nil {
  308. cacheBean = bean
  309. has, err = session.nocacheGet(reflect.Struct, table, cacheBean, sqlStr, args...)
  310. if err != nil || !has {
  311. return has, err
  312. }
  313. session.engine.logger.Debugf("[cache] cache bean: %s, %v, %v", tableName, id, cacheBean)
  314. cacher.PutBean(tableName, sid, cacheBean)
  315. } else {
  316. session.engine.logger.Debugf("[cache] cache hit: %s, %v, %v", tableName, id, cacheBean)
  317. has = true
  318. }
  319. structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean)))
  320. return has, nil
  321. }
  322. return false, nil
  323. }