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