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