github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/sqlstore/supplier_reactions.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "github.com/mattermost/mattermost-server/v5/mlog" 8 "github.com/mattermost/mattermost-server/v5/model" 9 "github.com/mattermost/mattermost-server/v5/store" 10 11 "github.com/mattermost/gorp" 12 "github.com/pkg/errors" 13 ) 14 15 type SqlReactionStore struct { 16 SqlStore 17 } 18 19 func newSqlReactionStore(sqlStore SqlStore) store.ReactionStore { 20 s := &SqlReactionStore{sqlStore} 21 22 for _, db := range sqlStore.GetAllConns() { 23 table := db.AddTableWithName(model.Reaction{}, "Reactions").SetKeys(false, "PostId", "UserId", "EmojiName") 24 table.ColMap("UserId").SetMaxSize(26) 25 table.ColMap("PostId").SetMaxSize(26) 26 table.ColMap("EmojiName").SetMaxSize(64) 27 } 28 29 return s 30 } 31 32 func (s *SqlReactionStore) Save(reaction *model.Reaction) (*model.Reaction, error) { 33 reaction.PreSave() 34 if err := reaction.IsValid(); err != nil { 35 return nil, err 36 } 37 38 transaction, err := s.GetMaster().Begin() 39 if err != nil { 40 return nil, errors.Wrap(err, "begin_transaction") 41 } 42 defer finalizeTransaction(transaction) 43 err = saveReactionAndUpdatePost(transaction, reaction) 44 if err != nil { 45 // We don't consider duplicated save calls as an error 46 if !IsUniqueConstraintError(err, []string{"reactions_pkey", "PRIMARY"}) { 47 return nil, errors.Wrap(err, "failed while saving reaction or updating post") 48 } 49 } else { 50 if err := transaction.Commit(); err != nil { 51 return nil, errors.Wrap(err, "commit_transaction") 52 } 53 } 54 55 return reaction, nil 56 } 57 58 func (s *SqlReactionStore) Delete(reaction *model.Reaction) (*model.Reaction, error) { 59 err := store.WithDeadlockRetry(func() error { 60 transaction, err := s.GetMaster().Begin() 61 if err != nil { 62 return errors.Wrap(err, "begin_transaction") 63 } 64 defer finalizeTransaction(transaction) 65 66 if err := deleteReactionAndUpdatePost(transaction, reaction); err != nil { 67 return errors.Wrap(err, "deleteReactionAndUpdatePost") 68 } 69 70 if err := transaction.Commit(); err != nil { 71 return errors.Wrap(err, "commit_transaction") 72 } 73 return nil 74 }) 75 if err != nil { 76 return nil, errors.Wrap(err, "failed to delete reaction") 77 } 78 79 return reaction, nil 80 } 81 82 func (s *SqlReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, error) { 83 var reactions []*model.Reaction 84 85 if _, err := s.GetReplica().Select(&reactions, 86 `SELECT 87 * 88 FROM 89 Reactions 90 WHERE 91 PostId = :PostId 92 ORDER BY 93 CreateAt`, map[string]interface{}{"PostId": postId}); err != nil { 94 return nil, errors.Wrapf(err, "failed to get Reactions with postId=%s", postId) 95 } 96 97 return reactions, nil 98 } 99 100 func (s *SqlReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, error) { 101 keys, params := MapStringsToQueryParams(postIds, "postId") 102 var reactions []*model.Reaction 103 104 if _, err := s.GetReplica().Select(&reactions, `SELECT 105 * 106 FROM 107 Reactions 108 WHERE 109 PostId IN `+keys+` 110 ORDER BY 111 CreateAt`, params); err != nil { 112 return nil, errors.Wrap(err, "failed to get Reactions") 113 } 114 return reactions, nil 115 } 116 117 func (s *SqlReactionStore) DeleteAllWithEmojiName(emojiName string) error { 118 var reactions []*model.Reaction 119 120 if _, err := s.GetReplica().Select(&reactions, 121 `SELECT 122 * 123 FROM 124 Reactions 125 WHERE 126 EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}); err != nil { 127 return errors.Wrapf(err, "failed to get Reactions with emojiName=%s", emojiName) 128 } 129 130 err := store.WithDeadlockRetry(func() error { 131 _, err := s.GetMaster().Exec( 132 `DELETE FROM 133 Reactions 134 WHERE 135 EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}) 136 return err 137 }) 138 if err != nil { 139 return errors.Wrapf(err, "failed to delete Reactions with emojiName=%s", emojiName) 140 } 141 142 for _, reaction := range reactions { 143 reaction := reaction 144 err := store.WithDeadlockRetry(func() error { 145 _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, 146 map[string]interface{}{ 147 "PostId": reaction.PostId, 148 "UpdateAt": model.GetMillis(), 149 }) 150 return err 151 }) 152 if err != nil { 153 mlog.Warn("Unable to update Post.HasReactions while removing reactions", 154 mlog.String("post_id", reaction.PostId), 155 mlog.Err(err)) 156 } 157 } 158 159 return nil 160 } 161 162 func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) { 163 var query string 164 if s.DriverName() == "postgres" { 165 query = "DELETE from Reactions WHERE CreateAt = any (array (SELECT CreateAt FROM Reactions WHERE CreateAt < :EndTime LIMIT :Limit))" 166 } else { 167 query = "DELETE from Reactions WHERE CreateAt < :EndTime LIMIT :Limit" 168 } 169 170 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 171 if err != nil { 172 return 0, errors.Wrap(err, "failed to delete Reactions") 173 } 174 175 rowsAffected, err := sqlResult.RowsAffected() 176 if err != nil { 177 return 0, errors.Wrap(err, "unable to get rows affected for deleted Reactions") 178 } 179 return rowsAffected, nil 180 } 181 182 func saveReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error { 183 if err := transaction.Insert(reaction); err != nil { 184 return err 185 } 186 187 return updatePostForReactionsOnInsert(transaction, reaction.PostId) 188 } 189 190 func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error { 191 if _, err := transaction.Exec( 192 `DELETE FROM 193 Reactions 194 WHERE 195 PostId = :PostId AND 196 UserId = :UserId AND 197 EmojiName = :EmojiName`, 198 map[string]interface{}{"PostId": reaction.PostId, "UserId": reaction.UserId, "EmojiName": reaction.EmojiName}); err != nil { 199 return err 200 } 201 202 return updatePostForReactionsOnDelete(transaction, reaction.PostId) 203 } 204 205 const ( 206 UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY = `UPDATE 207 Posts 208 SET 209 UpdateAt = :UpdateAt, 210 HasReactions = (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId) 211 WHERE 212 Id = :PostId` 213 ) 214 215 func updatePostForReactionsOnDelete(transaction *gorp.Transaction, postId string) error { 216 updateAt := model.GetMillis() 217 _, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": updateAt}) 218 return err 219 } 220 221 func updatePostForReactionsOnInsert(transaction *gorp.Transaction, postId string) error { 222 _, err := transaction.Exec( 223 `UPDATE 224 Posts 225 SET 226 HasReactions = True, 227 UpdateAt = :UpdateAt 228 WHERE 229 Id = :PostId`, 230 map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()}) 231 232 return err 233 }