github.com/spline-fu/mattermost-server@v4.10.10+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) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
   107  	result := store.NewSupplierResult()
   108  
   109  	var reactions []*model.Reaction
   110  
   111  	if _, err := s.GetReplica().Select(&reactions,
   112  		`SELECT
   113  				*
   114  			FROM
   115  				Reactions
   116  			WHERE
   117  				EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}); err != nil {
   118  		result.Err = model.NewAppError("SqlReactionStore.DeleteAllWithEmojiName",
   119  			"store.sql_reaction.delete_all_with_emoji_name.get_reactions.app_error", nil,
   120  			"emoji_name="+emojiName+", error="+err.Error(), http.StatusInternalServerError)
   121  		return result
   122  	}
   123  
   124  	if _, err := s.GetMaster().Exec(
   125  		`DELETE FROM
   126  				Reactions
   127  			WHERE
   128  				EmojiName = :EmojiName`, map[string]interface{}{"EmojiName": emojiName}); err != nil {
   129  		result.Err = model.NewAppError("SqlReactionStore.DeleteAllWithEmojiName",
   130  			"store.sql_reaction.delete_all_with_emoji_name.delete_reactions.app_error", nil,
   131  			"emoji_name="+emojiName+", error="+err.Error(), http.StatusInternalServerError)
   132  		return result
   133  	}
   134  
   135  	for _, reaction := range reactions {
   136  		if _, err := s.GetMaster().Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY,
   137  			map[string]interface{}{"PostId": reaction.PostId, "UpdateAt": model.GetMillis()}); err != nil {
   138  			mlog.Warn(fmt.Sprintf("Unable to update Post.HasReactions while removing reactions post_id=%v, error=%v", reaction.PostId, err.Error()))
   139  		}
   140  	}
   141  
   142  	return result
   143  }
   144  
   145  func (s *SqlSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
   146  	result := store.NewSupplierResult()
   147  
   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  		result.Err = model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   158  	} else {
   159  		rowsAffected, err1 := sqlResult.RowsAffected()
   160  		if err1 != nil {
   161  			result.Err = model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   162  			result.Data = int64(0)
   163  		} else {
   164  			result.Data = rowsAffected
   165  		}
   166  	}
   167  
   168  	return result
   169  }
   170  
   171  func saveReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error {
   172  	if err := transaction.Insert(reaction); err != nil {
   173  		return err
   174  	}
   175  
   176  	return updatePostForReactionsOnInsert(transaction, reaction.PostId)
   177  }
   178  
   179  func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.Reaction) error {
   180  	if _, err := transaction.Exec(
   181  		`DELETE FROM
   182  			Reactions
   183  		WHERE
   184  			PostId = :PostId AND
   185  			UserId = :UserId AND
   186  			EmojiName = :EmojiName`,
   187  		map[string]interface{}{"PostId": reaction.PostId, "UserId": reaction.UserId, "EmojiName": reaction.EmojiName}); err != nil {
   188  		return err
   189  	}
   190  
   191  	return updatePostForReactionsOnDelete(transaction, reaction.PostId)
   192  }
   193  
   194  const (
   195  	// Set HasReactions = true if and only if the post has reactions, update UpdateAt only if HasReactions changes
   196  	UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY = `UPDATE
   197  			Posts
   198  		SET
   199  			UpdateAt = (CASE
   200  				WHEN HasReactions != (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId) THEN :UpdateAt
   201  				ELSE UpdateAt
   202  			END),
   203  			HasReactions = (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId)
   204  		WHERE
   205  			Id = :PostId`
   206  )
   207  
   208  func updatePostForReactionsOnDelete(transaction *gorp.Transaction, postId string) error {
   209  	_, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
   210  
   211  	return err
   212  }
   213  
   214  func updatePostForReactionsOnInsert(transaction *gorp.Transaction, postId string) error {
   215  	_, err := transaction.Exec(
   216  		`UPDATE
   217  			Posts
   218  		SET
   219  			HasReactions = True,
   220  			UpdateAt = :UpdateAt
   221  		WHERE
   222  			Id = :PostId AND HasReactions = False`,
   223  		map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
   224  
   225  	return err
   226  }