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