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  }