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.

session_delete.go 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. "errors"
  7. "fmt"
  8. "strconv"
  9. "xorm.io/xorm/caches"
  10. "xorm.io/xorm/schemas"
  11. )
  12. var (
  13. // ErrNeedDeletedCond delete needs less one condition error
  14. ErrNeedDeletedCond = errors.New("Delete action needs at least one condition")
  15. // ErrNotImplemented not implemented
  16. ErrNotImplemented = errors.New("Not implemented")
  17. )
  18. func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error {
  19. if table == nil ||
  20. session.tx != nil {
  21. return ErrCacheFailed
  22. }
  23. for _, filter := range session.engine.dialect.Filters() {
  24. sqlStr = filter.Do(sqlStr)
  25. }
  26. newsql := session.statement.ConvertIDSQL(sqlStr)
  27. if newsql == "" {
  28. return ErrCacheFailed
  29. }
  30. cacher := session.engine.cacherMgr.GetCacher(tableName)
  31. pkColumns := table.PKColumns()
  32. ids, err := caches.GetCacheSql(cacher, tableName, newsql, args)
  33. if err != nil {
  34. resultsSlice, err := session.queryBytes(newsql, args...)
  35. if err != nil {
  36. return err
  37. }
  38. ids = make([]schemas.PK, 0)
  39. if len(resultsSlice) > 0 {
  40. for _, data := range resultsSlice {
  41. var id int64
  42. var pk schemas.PK = make([]interface{}, 0)
  43. for _, col := range pkColumns {
  44. if v, ok := data[col.Name]; !ok {
  45. return errors.New("no id")
  46. } else if col.SQLType.IsText() {
  47. pk = append(pk, string(v))
  48. } else if col.SQLType.IsNumeric() {
  49. id, err = strconv.ParseInt(string(v), 10, 64)
  50. if err != nil {
  51. return err
  52. }
  53. pk = append(pk, id)
  54. } else {
  55. return errors.New("not supported primary key type")
  56. }
  57. }
  58. ids = append(ids, pk)
  59. }
  60. }
  61. }
  62. for _, id := range ids {
  63. session.engine.logger.Debugf("[cache] delete cache obj: %v, %v", tableName, id)
  64. sid, err := id.ToString()
  65. if err != nil {
  66. return err
  67. }
  68. cacher.DelBean(tableName, sid)
  69. }
  70. session.engine.logger.Debugf("[cache] clear cache table: %v", tableName)
  71. cacher.ClearIds(tableName)
  72. return nil
  73. }
  74. // Delete records, bean's non-empty fields are conditions
  75. func (session *Session) Delete(beans ...interface{}) (int64, error) {
  76. if session.isAutoClose {
  77. defer session.Close()
  78. }
  79. if session.statement.LastError != nil {
  80. return 0, session.statement.LastError
  81. }
  82. var (
  83. condSQL string
  84. condArgs []interface{}
  85. err error
  86. bean interface{}
  87. )
  88. if len(beans) > 0 {
  89. bean = beans[0]
  90. if err = session.statement.SetRefBean(bean); err != nil {
  91. return 0, err
  92. }
  93. executeBeforeClosures(session, bean)
  94. if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
  95. processor.BeforeDelete()
  96. }
  97. condSQL, condArgs, err = session.statement.GenConds(bean)
  98. } else {
  99. condSQL, condArgs, err = session.statement.GenCondSQL(session.statement.Conds())
  100. }
  101. if err != nil {
  102. return 0, err
  103. }
  104. pLimitN := session.statement.LimitN
  105. if len(condSQL) == 0 && (pLimitN == nil || *pLimitN == 0) {
  106. return 0, ErrNeedDeletedCond
  107. }
  108. var tableNameNoQuote = session.statement.TableName()
  109. var tableName = session.engine.Quote(tableNameNoQuote)
  110. var table = session.statement.RefTable
  111. var deleteSQL string
  112. if len(condSQL) > 0 {
  113. deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
  114. } else {
  115. deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName)
  116. }
  117. var orderSQL string
  118. if len(session.statement.OrderStr) > 0 {
  119. orderSQL += fmt.Sprintf(" ORDER BY %s", session.statement.OrderStr)
  120. }
  121. if pLimitN != nil && *pLimitN > 0 {
  122. limitNValue := *pLimitN
  123. orderSQL += fmt.Sprintf(" LIMIT %d", limitNValue)
  124. }
  125. if len(orderSQL) > 0 {
  126. switch session.engine.dialect.URI().DBType {
  127. case schemas.POSTGRES:
  128. inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
  129. if len(condSQL) > 0 {
  130. deleteSQL += " AND " + inSQL
  131. } else {
  132. deleteSQL += " WHERE " + inSQL
  133. }
  134. case schemas.SQLITE:
  135. inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
  136. if len(condSQL) > 0 {
  137. deleteSQL += " AND " + inSQL
  138. } else {
  139. deleteSQL += " WHERE " + inSQL
  140. }
  141. // TODO: how to handle delete limit on mssql?
  142. case schemas.MSSQL:
  143. return 0, ErrNotImplemented
  144. default:
  145. deleteSQL += orderSQL
  146. }
  147. }
  148. var realSQL string
  149. argsForCache := make([]interface{}, 0, len(condArgs)*2)
  150. if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled
  151. realSQL = deleteSQL
  152. copy(argsForCache, condArgs)
  153. argsForCache = append(condArgs, argsForCache...)
  154. } else {
  155. // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for caches.
  156. copy(argsForCache, condArgs)
  157. argsForCache = append(condArgs, argsForCache...)
  158. deletedColumn := table.DeletedColumn()
  159. realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
  160. session.engine.Quote(session.statement.TableName()),
  161. session.engine.Quote(deletedColumn.Name),
  162. condSQL)
  163. if len(orderSQL) > 0 {
  164. switch session.engine.dialect.URI().DBType {
  165. case schemas.POSTGRES:
  166. inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
  167. if len(condSQL) > 0 {
  168. realSQL += " AND " + inSQL
  169. } else {
  170. realSQL += " WHERE " + inSQL
  171. }
  172. case schemas.SQLITE:
  173. inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
  174. if len(condSQL) > 0 {
  175. realSQL += " AND " + inSQL
  176. } else {
  177. realSQL += " WHERE " + inSQL
  178. }
  179. // TODO: how to handle delete limit on mssql?
  180. case schemas.MSSQL:
  181. return 0, ErrNotImplemented
  182. default:
  183. realSQL += orderSQL
  184. }
  185. }
  186. // !oinume! Insert nowTime to the head of session.statement.Params
  187. condArgs = append(condArgs, "")
  188. paramsLen := len(condArgs)
  189. copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
  190. val, t, err := session.engine.nowTime(deletedColumn)
  191. if err != nil {
  192. return 0, err
  193. }
  194. condArgs[0] = val
  195. var colName = deletedColumn.Name
  196. session.afterClosures = append(session.afterClosures, func(bean interface{}) {
  197. col := table.GetColumn(colName)
  198. setColumnTime(bean, col, t)
  199. })
  200. }
  201. if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache {
  202. session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...)
  203. }
  204. session.statement.RefTable = table
  205. res, err := session.exec(realSQL, condArgs...)
  206. if err != nil {
  207. return 0, err
  208. }
  209. if bean != nil {
  210. // handle after delete processors
  211. if session.isAutoCommit {
  212. for _, closure := range session.afterClosures {
  213. closure(bean)
  214. }
  215. if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok {
  216. processor.AfterDelete()
  217. }
  218. } else {
  219. lenAfterClosures := len(session.afterClosures)
  220. if lenAfterClosures > 0 && len(beans) > 0 {
  221. if value, has := session.afterDeleteBeans[beans[0]]; has && value != nil {
  222. *value = append(*value, session.afterClosures...)
  223. } else {
  224. afterClosures := make([]func(interface{}), lenAfterClosures)
  225. copy(afterClosures, session.afterClosures)
  226. session.afterDeleteBeans[bean] = &afterClosures
  227. }
  228. } else {
  229. if _, ok := interface{}(bean).(AfterDeleteProcessor); ok {
  230. session.afterDeleteBeans[bean] = nil
  231. }
  232. }
  233. }
  234. }
  235. cleanupProcessorsClosures(&session.afterClosures)
  236. // --
  237. return res.RowsAffected()
  238. }