github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/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 "net/http" 8 9 "github.com/mattermost/gorp" 10 "github.com/vnforks/kid/v5/mlog" 11 "github.com/vnforks/kid/v5/model" 12 "github.com/vnforks/kid/v5/store" 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, *model.AppError) { 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, model.NewAppError("SqlReactionStore.Save", "store.sql_reaction.save.begin.app_error", nil, err.Error(), http.StatusInternalServerError) 41 } 42 defer finalizeTransaction(transaction) 43 appErr := saveReactionAndUpdatePost(transaction, reaction) 44 if appErr != nil { 45 // We don't consider duplicated save calls as an error 46 if !IsUniqueConstraintError(appErr, []string{"reactions_pkey", "PRIMARY"}) { 47 return nil, model.NewAppError("SqlPreferenceStore.Save", "store.sql_reaction.save.save.app_error", nil, appErr.Error(), http.StatusBadRequest) 48 } 49 } else { 50 if err := transaction.Commit(); err != nil { 51 return nil, model.NewAppError("SqlPreferenceStore.Save", "store.sql_reaction.save.commit.app_error", nil, err.Error(), http.StatusInternalServerError) 52 } 53 } 54 55 return reaction, nil 56 } 57 58 func (s *SqlReactionStore) Delete(reaction *model.Reaction) (*model.Reaction, *model.AppError) { 59 transaction, err := s.GetMaster().Begin() 60 if err != nil { 61 return nil, model.NewAppError("SqlReactionStore.Delete", "store.sql_reaction.delete.begin.app_error", nil, err.Error(), http.StatusInternalServerError) 62 } 63 defer finalizeTransaction(transaction) 64 65 appErr := deleteReactionAndUpdatePost(transaction, reaction) 66 if appErr != nil { 67 return nil, model.NewAppError("SqlPreferenceStore.Delete", "store.sql_reaction.delete.app_error", nil, appErr.Error(), http.StatusInternalServerError) 68 } 69 70 if err := transaction.Commit(); err != nil { 71 return nil, model.NewAppError("SqlPreferenceStore.Delete", "store.sql_reaction.delete.commit.app_error", nil, err.Error(), http.StatusInternalServerError) 72 } 73 74 return reaction, nil 75 } 76 77 func (s *SqlReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, *model.AppError) { 78 var reactions []*model.Reaction 79 80 if _, err := s.GetReplica().Select(&reactions, 81 `SELECT 82 * 83 FROM 84 Reactions 85 WHERE 86 PostId = :PostId 87 ORDER BY 88 CreateAt`, map[string]interface{}{"PostId": postId}); err != nil { 89 return nil, model.NewAppError("SqlReactionStore.GetForPost", "store.sql_reaction.get_for_post.app_error", nil, "", http.StatusInternalServerError) 90 } 91 92 return reactions, nil 93 } 94 95 func (s *SqlReactionStore) BulkGetForPosts(postIds []string) ([]*model.Reaction, *model.AppError) { 96 keys, params := MapStringsToQueryParams(postIds, "postId") 97 var reactions []*model.Reaction 98 99 if _, err := s.GetReplica().Select(&reactions, `SELECT 100 * 101 FROM 102 Reactions 103 WHERE 104 PostId IN `+keys+` 105 ORDER BY 106 CreateAt`, params); err != nil { 107 return nil, model.NewAppError("SqlReactionStore.GetForPost", "store.sql_reaction.bulk_get_for_post_ids.app_error", nil, "", http.StatusInternalServerError) 108 } 109 return reactions, nil 110 } 111 112 func (s *SqlReactionStore) DeleteAllWithEmojiName(emojiName string) *model.AppError { 113 var reactions []*model.Reaction 114 115 if _, err := s.GetReplica().Select(&reactions, 116 `SELECT 117 * 118 FROM 119 Reactions 120 WHERE 121 EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}); err != nil { 122 return model.NewAppError("SqlReactionStore.DeleteAllWithEmojiName", 123 "store.sql_reaction.delete_all_with_emoji_name.get_reactions.app_error", nil, 124 "emoji_name="+emojiName+", error="+err.Error(), http.StatusInternalServerError) 125 } 126 127 if _, err := s.GetMaster().Exec( 128 `DELETE FROM 129 Reactions 130 WHERE 131 EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}); err != nil { 132 return model.NewAppError("SqlReactionStore.DeleteAllWithEmojiName", 133 "store.sql_reaction.delete_all_with_emoji_name.delete_reactions.app_error", nil, 134 "emoji_name="+emojiName+", error="+err.Error(), http.StatusInternalServerError) 135 } 136 137 for _, reaction := range reactions { 138 if _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, 139 map[string]interface{}{"PostId": reaction.PostId, "UpdateAt": model.GetMillis()}); err != nil { 140 mlog.Warn("Unable to update Post.HasReactions while removing reactions", mlog.String("post_id", reaction.PostId), mlog.Err(err)) 141 } 142 } 143 144 return nil 145 } 146 147 func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) { 148 var query string 149 if s.DriverName() == "postgres" { 150 query = "DELETE from Reactions WHERE CreateAt = any (array (SELECT CreateAt FROM Reactions WHERE CreateAt < :EndTime LIMIT :Limit))" 151 } else { 152 query = "DELETE from Reactions WHERE CreateAt < :EndTime LIMIT :Limit" 153 } 154 155 sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit}) 156 if err != nil { 157 return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 158 } 159 160 rowsAffected, err := sqlResult.RowsAffected() 161 if err != nil { 162 return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) 163 } 164 return rowsAffected, nil 165 } 166 167 func saveReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error { 168 if err := transaction.Insert(reaction); err != nil { 169 return err 170 } 171 172 return updatePostForReactionsOnInsert(transaction, reaction.PostId) 173 } 174 175 func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error { 176 if _, err := transaction.Exec( 177 `DELETE FROM 178 Reactions 179 WHERE 180 PostId = :PostId AND 181 UserId = :UserId AND 182 EmojiName = :EmojiName`, 183 map[string]interface{}{"PostId": reaction.PostId, "UserId": reaction.UserId, "EmojiName": reaction.EmojiName}); err != nil { 184 return err 185 } 186 187 return updatePostForReactionsOnDelete(transaction, reaction.PostId) 188 } 189 190 const ( 191 UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY = `UPDATE 192 Posts 193 SET 194 UpdateAt = :UpdateAt, 195 HasReactions = (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId) 196 WHERE 197 Id = :PostId` 198 ) 199 200 func updatePostForReactionsOnDelete(transaction *gorp.Transaction, postId string) error { 201 updateAt := model.GetMillis() 202 _, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": updateAt}) 203 return err 204 } 205 206 func updatePostForReactionsOnInsert(transaction *gorp.Transaction, postId string) error { 207 _, err := transaction.Exec( 208 `UPDATE 209 Posts 210 SET 211 HasReactions = True, 212 UpdateAt = :UpdateAt 213 WHERE 214 Id = :PostId`, 215 map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()}) 216 217 return err 218 }