jointbase.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package db
  15. import (
  16. "context"
  17. "database/sql"
  18. "fmt"
  19. "reflect"
  20. "strings"
  21. "time"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/util/rbacscope"
  26. "yunion.io/x/pkg/util/reflectutils"
  27. "yunion.io/x/sqlchemy"
  28. "yunion.io/x/onecloud/pkg/apis"
  29. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/util/stringutils2"
  32. )
  33. type SJointResourceBase struct {
  34. SResourceBase
  35. RowId int64 `primary:"true" auto_increment:"true" list:"user"`
  36. }
  37. type SJointResourceBaseManager struct {
  38. SResourceBaseManager
  39. _master IStandaloneModelManager
  40. _slave IStandaloneModelManager
  41. }
  42. func NewJointResourceBaseManager(dt interface{}, tableName string, keyword string, keywordPlural string, master IStandaloneModelManager, slave IStandaloneModelManager) SJointResourceBaseManager {
  43. log.Debugf("Initialize %s", keywordPlural)
  44. if master == nil {
  45. msg := fmt.Sprintf("%s master is nil, retry initialization later...", keywordPlural)
  46. log.Errorf("%s", msg)
  47. panic(msg)
  48. }
  49. if slave == nil {
  50. msg := fmt.Sprintf("%s slave is nil, retry initialization later...", keywordPlural)
  51. log.Errorf("%s", msg)
  52. panic(msg)
  53. }
  54. return SJointResourceBaseManager{
  55. SResourceBaseManager: NewResourceBaseManager(dt, tableName, keyword, keywordPlural),
  56. _master: master,
  57. _slave: slave,
  58. }
  59. }
  60. func (manager *SJointResourceBaseManager) GetIJointModelManager() IJointModelManager {
  61. return manager.GetVirtualObject().(IJointModelManager)
  62. }
  63. func (manager *SJointResourceBaseManager) GetMasterManager() IStandaloneModelManager {
  64. return manager._master
  65. }
  66. func (manager *SJointResourceBaseManager) GetSlaveManager() IStandaloneModelManager {
  67. return manager._slave
  68. }
  69. func (manager *SJointResourceBaseManager) CreateByInsertOrUpdate() bool {
  70. return false
  71. }
  72. /*
  73. func queryField(q *sqlchemy.SQuery, manager IModelManager) sqlchemy.IQueryField {
  74. field := q.Field(fmt.Sprintf("%s_id", manager.Keyword()))
  75. if field == nil && len(manager.Alias()) > 0 {
  76. field = q.Field(fmt.Sprintf("%s_id", manager.Alias()))
  77. }
  78. return field
  79. }
  80. func (manager *SJointResourceBaseManager) MasterField(q *sqlchemy.SQuery) sqlchemy.IQueryField {
  81. return queryField(q, manager.GetMasterManager())
  82. }
  83. func (manager *SJointResourceBaseManager) SlaveField(q *sqlchemy.SQuery) sqlchemy.IQueryField {
  84. return queryField(q, manager.GetSlaveManager())
  85. }
  86. */
  87. func (manager *SJointResourceBaseManager) FilterByParams(q *sqlchemy.SQuery, params jsonutils.JSONObject) *sqlchemy.SQuery {
  88. return q
  89. }
  90. func JointModelExtra(jointModel IJointModel) (string, string) {
  91. masterName, slaveName := "", ""
  92. master := JointMaster(jointModel)
  93. if master != nil {
  94. masterName = master.GetName()
  95. }
  96. slave := JointSlave(jointModel)
  97. if slave != nil {
  98. slaveName = slave.GetName()
  99. }
  100. return masterName, slaveName
  101. }
  102. func (joint *SJointResourceBase) GetJointModelManager() IJointModelManager {
  103. return joint.SResourceBase.GetModelManager().(IJointModelManager)
  104. }
  105. func getFieldValue(joint IJointModel, keyword string, alias string, fieldIdKey string) string {
  106. jointValue := reflect.Indirect(reflect.ValueOf(joint))
  107. for _, valKey := range []string{
  108. fmt.Sprintf("%s_id", keyword),
  109. fmt.Sprintf("%s_id", alias),
  110. fieldIdKey,
  111. } {
  112. idStr, ok := reflectutils.FindStructFieldInterface(jointValue, valKey)
  113. if ok {
  114. return idStr.(string)
  115. }
  116. }
  117. return ""
  118. }
  119. func JointMasterID(joint IJointModel) string {
  120. jointMan := joint.GetJointModelManager()
  121. masterMan := jointMan.GetMasterManager()
  122. return getFieldValue(joint, masterMan.Keyword(), masterMan.Alias(), jointMan.GetMasterFieldName())
  123. }
  124. func JointSlaveID(joint IJointModel) string {
  125. jointMan := joint.GetJointModelManager()
  126. slaveMan := jointMan.GetSlaveManager()
  127. return getFieldValue(joint, slaveMan.Keyword(), slaveMan.Alias(), jointMan.GetSlaveFieldName())
  128. }
  129. func JointMaster(joint IJointModel) IStandaloneModel {
  130. masterMan := joint.GetJointModelManager().GetMasterManager()
  131. masterId := JointMasterID(joint)
  132. //log.Debugf("MasterID: %s %s", masterId, masterMan.KeywordPlural())
  133. if len(masterId) > 0 {
  134. master, _ := masterMan.FetchById(masterId)
  135. if master != nil {
  136. return master.(IStandaloneModel)
  137. }
  138. }
  139. return nil
  140. }
  141. func JointSlave(joint IJointModel) IStandaloneModel {
  142. slaveMan := joint.GetJointModelManager().GetSlaveManager()
  143. slaveId := JointSlaveID(joint)
  144. //log.Debugf("SlaveID: %s %s", slaveId, slaveMan.KeywordPlural())
  145. if len(slaveId) > 0 {
  146. slave, _ := slaveMan.FetchById(slaveId)
  147. if slave != nil {
  148. return slave.(IStandaloneModel)
  149. }
  150. }
  151. return nil
  152. }
  153. func (joint *SJointResourceBase) GetIJointModel() IJointModel {
  154. return joint.GetVirtualObject().(IJointModel)
  155. }
  156. func (manager *SJointResourceBaseManager) ResourceScope() rbacscope.TRbacScope {
  157. return manager.GetMasterManager().ResourceScope()
  158. }
  159. func (manager *SJointResourceBaseManager) NamespaceScope() rbacscope.TRbacScope {
  160. return rbacscope.ScopeSystem
  161. }
  162. func (manager *SJointResourceBaseManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input apis.JoinResourceBaseCreateInput) (apis.JoinResourceBaseCreateInput, error) {
  163. var err error
  164. input.ResourceBaseCreateInput, err = manager.SResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.ResourceBaseCreateInput)
  165. if err != nil {
  166. return input, err
  167. }
  168. return input, nil
  169. }
  170. func (manager *SJointResourceBaseManager) FetchCustomizeColumns(
  171. ctx context.Context,
  172. userCred mcclient.TokenCredential,
  173. query jsonutils.JSONObject,
  174. objs []interface{},
  175. fields stringutils2.SSortedStrings,
  176. isList bool,
  177. ) []apis.JointResourceBaseDetails {
  178. ret := make([]apis.JointResourceBaseDetails, len(objs))
  179. upperRet := manager.SResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  180. for i := range objs {
  181. ret[i] = apis.JointResourceBaseDetails{
  182. ResourceBaseDetails: upperRet[i],
  183. }
  184. }
  185. return ret
  186. }
  187. func (manager *SJointResourceBaseManager) ListItemFilter(
  188. ctx context.Context,
  189. q *sqlchemy.SQuery,
  190. userCred mcclient.TokenCredential,
  191. query apis.JointResourceBaseListInput,
  192. ) (*sqlchemy.SQuery, error) {
  193. var err error
  194. q, err = manager.SResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ResourceBaseListInput)
  195. if err != nil {
  196. return nil, errors.Wrap(err, "SResourceBaseManager.ListItemFilter")
  197. }
  198. return q, nil
  199. }
  200. func (manager *SJointResourceBaseManager) OrderByExtraFields(
  201. ctx context.Context,
  202. q *sqlchemy.SQuery,
  203. userCred mcclient.TokenCredential,
  204. query apis.JointResourceBaseListInput,
  205. ) (*sqlchemy.SQuery, error) {
  206. var err error
  207. q, err = manager.SResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ResourceBaseListInput)
  208. if err != nil {
  209. return nil, errors.Wrap(err, "SResourceBaseManager.ListItemFilter")
  210. }
  211. return q, nil
  212. }
  213. func (model *SJointResourceBase) ValidateUpdateData(
  214. ctx context.Context,
  215. userCred mcclient.TokenCredential,
  216. query jsonutils.JSONObject,
  217. input apis.JointResourceBaseUpdateInput,
  218. ) (apis.JointResourceBaseUpdateInput, error) {
  219. var err error
  220. input.ResourceBaseUpdateInput, err = model.SResourceBase.ValidateUpdateData(ctx, userCred, query, input.ResourceBaseUpdateInput)
  221. if err != nil {
  222. return input, errors.Wrap(err, "SResourceBase.ValidateUpdateData")
  223. }
  224. return input, nil
  225. }
  226. func (manager *SJointResourceBaseManager) HistoryDataClean(ctx context.Context, timeBefor time.Time) (int, error) {
  227. q := manager.RawQuery("row_id").IsTrue("deleted").LE("deleted_at", timeBefor)
  228. rows, err := q.Rows()
  229. if err != nil {
  230. if errors.Cause(err) == sql.ErrNoRows {
  231. return 0, nil
  232. }
  233. return 0, errors.Wrap(err, "Query")
  234. }
  235. defer rows.Close()
  236. ids := []string{}
  237. for rows.Next() {
  238. var id string
  239. err := rows.Scan(&id)
  240. if err != nil {
  241. return 0, errors.Wrap(err, "rows.Scan")
  242. }
  243. ids = append(ids, id)
  244. }
  245. var purge = func(ids []string) error {
  246. vars := []interface{}{}
  247. placeholders := make([]string, len(ids))
  248. for i := range placeholders {
  249. placeholders[i] = "?"
  250. vars = append(vars, ids[i])
  251. }
  252. placeholder := strings.Join(placeholders, ",")
  253. sql := fmt.Sprintf(
  254. "delete from %s where row_id in (%s)",
  255. manager.TableSpec().Name(), placeholder,
  256. )
  257. lockman.LockRawObject(ctx, manager.Keyword(), "purge")
  258. defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "purge")
  259. _, err = sqlchemy.GetDB().Exec(
  260. sql, vars...,
  261. )
  262. if err != nil {
  263. return errors.Wrapf(err, strings.ReplaceAll(sql, "?", "%s"), vars...)
  264. }
  265. return nil
  266. }
  267. var splitByLen = func(data []string, splitLen int) [][]string {
  268. var result [][]string
  269. for i := 0; i < len(data); i += splitLen {
  270. end := i + splitLen
  271. if end > len(data) {
  272. end = len(data)
  273. }
  274. result = append(result, data[i:end])
  275. }
  276. return result
  277. }
  278. idsArr := splitByLen(ids, 100)
  279. for i := range idsArr {
  280. err = purge(idsArr[i])
  281. if err != nil {
  282. return 0, err
  283. }
  284. }
  285. return len(ids), nil
  286. }