snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
400
server/channels/store/sqlstore/reaction_store.go
Normal file
400
server/channels/store/sqlstore/reaction_store.go
Normal file
@@ -0,0 +1,400 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
sq "github.com/mattermost/squirrel"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SqlReactionStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func newSqlReactionStore(sqlStore *SqlStore) store.ReactionStore {
|
||||
return &SqlReactionStore{sqlStore}
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) Save(reaction *model.Reaction) (re *model.Reaction, err error) {
|
||||
reaction.PreSave()
|
||||
if err := reaction.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transaction, err := s.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "begin_transaction")
|
||||
}
|
||||
defer finalizeTransactionX(transaction, &err)
|
||||
if reaction.ChannelId == "" {
|
||||
// get channelId, if not already populated
|
||||
var channelIds []string
|
||||
query := "SELECT ChannelId from Posts where Id = ?"
|
||||
err = transaction.Select(&channelIds, query, reaction.PostId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed while getting channelId from Posts")
|
||||
}
|
||||
|
||||
if len(channelIds) == 0 {
|
||||
return nil, store.NewErrNotFound("Post", reaction.PostId)
|
||||
}
|
||||
|
||||
reaction.ChannelId = channelIds[0]
|
||||
}
|
||||
err = s.saveReactionAndUpdatePost(transaction, reaction)
|
||||
if err != nil {
|
||||
// We don't consider duplicated save calls as an error
|
||||
if !IsUniqueConstraintError(err, []string{"reactions_pkey", "PRIMARY"}) {
|
||||
return nil, errors.Wrap(err, "failed while saving reaction or updating post")
|
||||
}
|
||||
} else {
|
||||
if err := transaction.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
}
|
||||
|
||||
return reaction, nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) Delete(reaction *model.Reaction) (re *model.Reaction, err error) {
|
||||
reaction.PreUpdate()
|
||||
|
||||
transaction, err := s.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "begin_transaction")
|
||||
}
|
||||
defer finalizeTransactionX(transaction, &err)
|
||||
|
||||
if err := deleteReactionAndUpdatePost(transaction, reaction); err != nil {
|
||||
return nil, errors.Wrap(err, "deleteReactionAndUpdatePost")
|
||||
}
|
||||
|
||||
if err := transaction.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return reaction, nil
|
||||
}
|
||||
|
||||
// GetForPost returns all reactions associated with `postId` that are not deleted.
|
||||
func (s *SqlReactionStore) GetForPost(postId string, allowFromCache bool) ([]*model.Reaction, error) {
|
||||
builder := s.getQueryBuilder().
|
||||
Select("UserId", "PostId", "EmojiName", "CreateAt", "COALESCE(UpdateAt, CreateAt) As UpdateAt",
|
||||
"COALESCE(DeleteAt, 0) As DeleteAt", "RemoteId", "ChannelId").
|
||||
From("Reactions").
|
||||
Where(sq.Eq{"PostId": postId}).
|
||||
Where(sq.Eq{"COALESCE(DeleteAt, 0)": 0}).
|
||||
OrderBy("CreateAt")
|
||||
|
||||
var reactions []*model.Reaction
|
||||
if err := s.GetReplica().SelectBuilder(&reactions, builder); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get Reactions with postId=%s", postId)
|
||||
}
|
||||
return reactions, nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) ExistsOnPost(postId string, emojiName string) (bool, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("1").
|
||||
From("Reactions").
|
||||
Where(sq.Eq{"PostId": postId}).
|
||||
Where(sq.Eq{"EmojiName": emojiName}).
|
||||
Where(sq.Eq{"COALESCE(DeleteAt, 0)": 0})
|
||||
|
||||
var hasRows bool
|
||||
if err := s.GetReplica().GetBuilder(&hasRows, query); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
return false, errors.Wrap(err, "failed to check for existing reaction")
|
||||
}
|
||||
|
||||
return hasRows, nil
|
||||
}
|
||||
|
||||
// GetForPostSince returns all reactions associated with `postId` updated after `since`.
|
||||
func (s *SqlReactionStore) GetForPostSince(postId string, since int64, excludeRemoteId string, inclDeleted bool) ([]*model.Reaction, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("UserId", "PostId", "EmojiName", "CreateAt", "COALESCE(UpdateAt, CreateAt) As UpdateAt",
|
||||
"COALESCE(DeleteAt, 0) As DeleteAt", "RemoteId").
|
||||
From("Reactions").
|
||||
Where(sq.Eq{"PostId": postId}).
|
||||
Where(sq.Gt{"UpdateAt": since}).
|
||||
OrderBy("CreateAt")
|
||||
|
||||
if excludeRemoteId != "" {
|
||||
query = query.Where(sq.NotEq{"COALESCE(RemoteId, '')": excludeRemoteId})
|
||||
}
|
||||
|
||||
if !inclDeleted {
|
||||
query = query.Where(sq.Eq{"COALESCE(DeleteAt, 0)": 0})
|
||||
}
|
||||
|
||||
var reactions []*model.Reaction
|
||||
if err := s.GetReplica().SelectBuilder(&reactions, query); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find reactions")
|
||||
}
|
||||
return reactions, nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) GetUniqueCountForPost(postId string) (int, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("COUNT(DISTINCT EmojiName)").
|
||||
From("Reactions").
|
||||
Where(sq.Eq{"PostId": postId}).
|
||||
Where(sq.Eq{"DeleteAt": 0})
|
||||
|
||||
var count int64
|
||||
err := s.GetReplica().GetBuilder(&count, query)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to count Reactions")
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) GetSingle(userID, postID, remoteID, emojiName string) (*model.Reaction, error) {
|
||||
query := s.getQueryBuilder().
|
||||
Select("UserId", "PostId", "EmojiName", "CreateAt",
|
||||
"COALESCE(UpdateAt, CreateAt) As UpdateAt", "COALESCE(DeleteAt, 0) As DeleteAt",
|
||||
"RemoteId", "ChannelId").
|
||||
From("Reactions").
|
||||
Where(sq.Eq{"UserId": userID}).
|
||||
Where(sq.Eq{"PostId": postID}).
|
||||
Where(sq.Eq{"COALESCE(RemoteId, '')": remoteID}).
|
||||
Where(sq.Eq{"EmojiName": emojiName})
|
||||
|
||||
var reactions []*model.Reaction
|
||||
if err := s.GetReplica().SelectBuilder(&reactions, query); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to find reaction")
|
||||
}
|
||||
if len(reactions) == 0 {
|
||||
return nil, store.NewErrNotFound("Reaction", fmt.Sprintf("user_id=%s, post_id=%s, remote_id=%s, emoji_name=%s",
|
||||
userID, postID, remoteID, emojiName))
|
||||
}
|
||||
return reactions[0], nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) DeleteAllWithEmojiName(rctx request.CTX, emojiName string) error {
|
||||
var reactions []*model.Reaction
|
||||
now := model.GetMillis()
|
||||
|
||||
if err := s.GetReplica().Select(&reactions,
|
||||
`SELECT
|
||||
UserId,
|
||||
PostId,
|
||||
EmojiName,
|
||||
CreateAt,
|
||||
COALESCE(UpdateAt, CreateAt) As UpdateAt,
|
||||
COALESCE(DeleteAt, 0) As DeleteAt,
|
||||
RemoteId
|
||||
FROM
|
||||
Reactions
|
||||
WHERE
|
||||
EmojiName = ? AND COALESCE(DeleteAt, 0) = 0`, emojiName); err != nil {
|
||||
return errors.Wrapf(err, "failed to get Reactions with emojiName=%s", emojiName)
|
||||
}
|
||||
|
||||
_, err := s.GetMaster().Exec(
|
||||
`UPDATE
|
||||
Reactions
|
||||
SET
|
||||
UpdateAt = ?, DeleteAt = ?
|
||||
WHERE
|
||||
EmojiName = ? AND COALESCE(DeleteAt, 0) = 0`, now, now, emojiName)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete Reactions with emojiName=%s", emojiName)
|
||||
}
|
||||
|
||||
for _, reaction := range reactions {
|
||||
_, err := s.GetMaster().Exec(UpdatePostHasReactionsOnDeleteQuery, now, reaction.PostId, reaction.PostId)
|
||||
if err != nil {
|
||||
rctx.Logger().Warn("Unable to update Post.HasReactions while removing reactions",
|
||||
mlog.String("post_id", reaction.PostId),
|
||||
mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) permanentDeleteReactions(userId string) ([]string, error) {
|
||||
txn, err := s.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer finalizeTransactionX(txn, &err)
|
||||
|
||||
postIds := []string{}
|
||||
err = txn.Select(&postIds, "SELECT PostId FROM Reactions WHERE UserId = ?", userId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get Reactions with userId=%s", userId)
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Delete("Reactions").
|
||||
Where(sq.And{
|
||||
sq.Eq{"PostId": postIds},
|
||||
sq.Eq{"UserId": userId},
|
||||
})
|
||||
|
||||
_, err = txn.ExecBuilder(query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to delete reactions with userId=%s", userId)
|
||||
}
|
||||
if err = txn.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return postIds, nil
|
||||
}
|
||||
|
||||
func (s SqlReactionStore) PermanentDeleteByUser(rctx request.CTX, userId string) error {
|
||||
now := model.GetMillis()
|
||||
|
||||
postIds, err := s.permanentDeleteReactions(userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transaction, err := s.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer finalizeTransactionX(transaction, &err)
|
||||
|
||||
for _, postId := range postIds {
|
||||
_, err = transaction.Exec(UpdatePostHasReactionsOnDeleteQuery, now, postId, postId)
|
||||
if err != nil {
|
||||
rctx.Logger().Warn("Unable to update Post.HasReactions while removing reactions",
|
||||
mlog.String("post_id", postId),
|
||||
mlog.Err(err))
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if err = transaction.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) DeleteOrphanedRowsByIds(r *model.RetentionIdsForDeletion) (int64, error) {
|
||||
txn, err := s.GetMaster().Begin()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer finalizeTransactionX(txn, &err)
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Delete("Reactions").
|
||||
Where(
|
||||
sq.Eq{"PostId": r.Ids},
|
||||
)
|
||||
|
||||
sqlResult, err := txn.ExecBuilder(query)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "failed to delete orphaned reactions with RetentionIdsForDeletion Id=%s", r.Id)
|
||||
}
|
||||
err = deleteFromRetentionIdsTx(txn, r.Id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err = txn.Commit(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rowsAffected, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "unable to retrieve rows affected")
|
||||
}
|
||||
|
||||
return rowsAffected, nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) {
|
||||
query := "DELETE from Reactions WHERE CreateAt = any (array (SELECT CreateAt FROM Reactions WHERE CreateAt < ? LIMIT ?))"
|
||||
|
||||
sqlResult, err := s.GetMaster().Exec(query, endTime, limit)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to delete Reactions")
|
||||
}
|
||||
|
||||
rowsAffected, err := sqlResult.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "unable to get rows affected for deleted Reactions")
|
||||
}
|
||||
return rowsAffected, nil
|
||||
}
|
||||
|
||||
func (s *SqlReactionStore) saveReactionAndUpdatePost(transaction *sqlxTxWrapper, reaction *model.Reaction) error {
|
||||
reaction.DeleteAt = 0
|
||||
|
||||
if _, err := transaction.NamedExec(
|
||||
`INSERT INTO
|
||||
Reactions
|
||||
(UserId, PostId, EmojiName, CreateAt, UpdateAt, DeleteAt, RemoteId, ChannelId)
|
||||
VALUES
|
||||
(:UserId, :PostId, :EmojiName, :CreateAt, :UpdateAt, :DeleteAt, :RemoteId, :ChannelId)
|
||||
ON CONFLICT (UserId, PostId, EmojiName)
|
||||
DO UPDATE SET UpdateAt = :UpdateAt, DeleteAt = :DeleteAt, RemoteId = :RemoteId, ChannelId = :ChannelId`, reaction); err != nil {
|
||||
return err
|
||||
}
|
||||
return updatePostForReactionsOnInsert(transaction, reaction.PostId)
|
||||
}
|
||||
|
||||
func deleteReactionAndUpdatePost(transaction *sqlxTxWrapper, reaction *model.Reaction) error {
|
||||
if _, err := transaction.Exec(
|
||||
`UPDATE
|
||||
Reactions
|
||||
SET
|
||||
UpdateAt = ?, DeleteAt = ?, RemoteId = ?
|
||||
WHERE
|
||||
PostId = ? AND
|
||||
UserId = ? AND
|
||||
EmojiName = ?`, reaction.UpdateAt, reaction.UpdateAt, reaction.RemoteId, reaction.PostId, reaction.UserId, reaction.EmojiName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updatePostForReactionsOnDelete(transaction, reaction.PostId)
|
||||
}
|
||||
|
||||
const (
|
||||
UpdatePostHasReactionsOnDeleteQuery = `UPDATE
|
||||
Posts
|
||||
SET
|
||||
UpdateAt = ?,
|
||||
HasReactions = (SELECT count(0) > 0 FROM Reactions WHERE PostId = ? AND COALESCE(DeleteAt, 0) = 0)
|
||||
WHERE
|
||||
Id = ?`
|
||||
)
|
||||
|
||||
func updatePostForReactionsOnDelete(transaction *sqlxTxWrapper, postId string) error {
|
||||
updateAt := model.GetMillis()
|
||||
_, err := transaction.Exec(UpdatePostHasReactionsOnDeleteQuery, updateAt, postId, postId)
|
||||
return err
|
||||
}
|
||||
|
||||
func updatePostForReactionsOnInsert(transaction *sqlxTxWrapper, postId string) error {
|
||||
_, err := transaction.Exec(
|
||||
`UPDATE
|
||||
Posts
|
||||
SET
|
||||
HasReactions = True,
|
||||
UpdateAt = ?
|
||||
WHERE
|
||||
Id = ?`,
|
||||
model.GetMillis(),
|
||||
postId,
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user