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  }