From b37c07ec410f631db51957ff5f6573152467c529 Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Fri, 14 Jan 2022 10:43:54 +0530 Subject: [PATCH] Migrate channel_store to sqlx (#19247) * Migrate channel_store to sqlx https://community-daily.mattermost.com/boards/workspace/zyoahc9uapdn3xdptac6jb69ic/285b80a3-257d-41f6-8cf4-ed80ca9d92e5/495cdb4d-c13a-4992-8eb9-80cfee2819a4/chur9rxkghprg7dz1mcqssj34ic ```release-note NONE ``` --- model/channel.go | 6 +- model/utils.go | 5 + store/sqlstore/channel_store.go | 840 +++++++++++++++++++------------ store/storetest/channel_store.go | 4 + 4 files changed, 527 insertions(+), 328 deletions(-) diff --git a/model/channel.go b/model/channel.go index 2060470071..dfa4034790 100644 --- a/model/channel.go +++ b/model/channel.go @@ -144,9 +144,9 @@ type ChannelSearchOpts struct { } type ChannelMemberCountByGroup struct { - GroupId string `db:"-" json:"group_id"` - ChannelMemberCount int64 `db:"-" json:"channel_member_count"` - ChannelMemberTimezonesCount int64 `db:"-" json:"channel_member_timezones_count"` + GroupId string `json:"group_id"` + ChannelMemberCount int64 `json:"channel_member_count"` + ChannelMemberTimezonesCount int64 `json:"channel_member_timezones_count"` } type ChannelOption func(channel *Channel) diff --git a/model/utils.go b/model/utils.go index 36527ce5cf..2844f7d5f6 100644 --- a/model/utils.go +++ b/model/utils.go @@ -117,6 +117,11 @@ func (m *StringMap) Scan(value interface{}) error { return errors.New("received value is neither a byte slice nor string") } +// Value converts StringMap to database value +func (m StringMap) Value() (driver.Value, error) { + return json.Marshal(m) +} + func (si *StringInterface) Scan(value interface{}) error { if value == nil { return nil diff --git a/store/sqlstore/channel_store.go b/store/sqlstore/channel_store.go index aba1134758..8093153d9e 100644 --- a/store/sqlstore/channel_store.go +++ b/store/sqlstore/channel_store.go @@ -13,7 +13,6 @@ import ( "time" sq "github.com/Masterminds/squirrel" - "github.com/mattermost/gorp" "github.com/pkg/errors" "github.com/mattermost/mattermost-server/v6/einterfaces" @@ -432,6 +431,84 @@ type publicChannel struct { Purpose string `json:"purpose"` } +// channelInternal is a struct without the db:"-" tags +// which does not work with sqlx. This would be removed once we +// move to the new migration system. +type channelInternal struct { + Id string + CreateAt int64 + UpdateAt int64 + DeleteAt int64 + TeamId string + Type model.ChannelType + DisplayName string + Name string + Header string + Purpose string + LastPostAt int64 + TotalMsgCount int64 + ExtraUpdateAt int64 + CreatorId string + SchemeId *string + Props map[string]interface{} + GroupConstrained *bool + Shared *bool + TotalMsgCountRoot int64 + PolicyId *string + LastRootPostAt int64 +} + +func (ci *channelInternal) ToModel() *model.Channel { + return &model.Channel{ + Id: ci.Id, + CreateAt: ci.CreateAt, + UpdateAt: ci.UpdateAt, + DeleteAt: ci.DeleteAt, + TeamId: ci.TeamId, + Type: ci.Type, + DisplayName: ci.DisplayName, + Name: ci.Name, + Header: ci.Header, + Purpose: ci.Purpose, + LastPostAt: ci.LastPostAt, + TotalMsgCount: ci.TotalMsgCount, + ExtraUpdateAt: ci.ExtraUpdateAt, + CreatorId: ci.CreatorId, + SchemeId: ci.SchemeId, + Props: ci.Props, + GroupConstrained: ci.GroupConstrained, + Shared: ci.Shared, + TotalMsgCountRoot: ci.TotalMsgCountRoot, + PolicyID: ci.PolicyId, + LastRootPostAt: ci.LastRootPostAt, + } +} + +type channelWithTeamDataInternal struct { + channelInternal + TeamDisplayName string + TeamName string + TeamUpdateAt int64 +} + +func (ctd *channelWithTeamDataInternal) ToModel() *model.ChannelWithTeamData { + res := &model.ChannelWithTeamData{ + TeamDisplayName: ctd.TeamDisplayName, + TeamName: ctd.TeamName, + TeamUpdateAt: ctd.TeamUpdateAt, + } + res.Channel = *ctd.channelInternal.ToModel() + return res +} + +func channelWithTeamDataSliceToModel(channels []*channelWithTeamDataInternal) model.ChannelListWithTeamData { + res := make(model.ChannelListWithTeamData, 0, len(channels)) + for _, ch := range channels { + res = append(res, ch.ToModel()) + } + return res +} + var allChannelMembersForUserCache = cache.NewLRU(cache.LRUOptions{ Size: AllChannelMembersForUserCacheSize, }) @@ -506,7 +583,7 @@ func newSqlChannelStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface return s } -func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, channel *model.Channel) error { +func (s SqlChannelStore) upsertPublicChannelT(transaction *sqlxTxWrapper, channel *model.Channel) error { publicChannel := &publicChannel{ Id: channel.Id, DeleteAt: channel.DeleteAt, @@ -518,7 +595,7 @@ func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, cha } if channel.Type != model.ChannelTypeOpen { - if _, err := transaction.Delete(publicChannel); err != nil { + if _, err := transaction.Exec(`DELETE FROM PublicChannels WHERE Id=?`, publicChannel.Id); err != nil { return errors.Wrap(err, "failed to delete public channel") } @@ -526,42 +603,42 @@ func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, cha } vals := map[string]interface{}{ - "Id": publicChannel.Id, - "DeleteAt": publicChannel.DeleteAt, - "TeamId": publicChannel.TeamId, - "DisplayName": publicChannel.DisplayName, - "Name": publicChannel.Name, - "Header": publicChannel.Header, - "Purpose": publicChannel.Purpose, + "id": publicChannel.Id, + "deleteat": publicChannel.DeleteAt, + "teamid": publicChannel.TeamId, + "displayname": publicChannel.DisplayName, + "name": publicChannel.Name, + "header": publicChannel.Header, + "purpose": publicChannel.Purpose, } var err error if s.DriverName() == model.DatabaseDriverMysql { - _, err = transaction.Exec(` + _, err = transaction.NamedExec(` INSERT INTO PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose) VALUES - (:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose) + (:id, :deleteat, :teamid, :displayname, :name, :header, :purpose) ON DUPLICATE KEY UPDATE - DeleteAt = :DeleteAt, - TeamId = :TeamId, - DisplayName = :DisplayName, - Name = :Name, - Header = :Header, - Purpose = :Purpose; + DeleteAt = :deleteat, + TeamId = :teamid, + DisplayName = :displayname, + Name = :name, + Header = :header, + Purpose = :purpose; `, vals) } else { - _, err = transaction.Exec(` + _, err = transaction.NamedExec(` INSERT INTO PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose) VALUES - (:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose) + (:id, :deleteat, :teamid, :displayname, :name, :header, :purpose) ON CONFLICT (id) DO UPDATE - SET DeleteAt = :DeleteAt, - TeamId = :TeamId, - DisplayName = :DisplayName, - Name = :Name, - Header = :Header, - Purpose = :Purpose; + SET DeleteAt = :deleteat, + TeamId = :teamid, + DisplayName = :displayname, + Name = :name, + Header = :header, + Purpose = :purpose; `, vals) } if err != nil { @@ -582,11 +659,11 @@ func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) } var newChannel *model.Channel - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) newChannel, err = s.saveChannelT(transaction, channel, maxChannelsPerTeam) if err != nil { @@ -646,11 +723,11 @@ func (s SqlChannelStore) SaveDirectChannel(directChannel *model.Channel, member1 return nil, store.NewErrInvalidInput("Channel", "Type", directChannel.Type) } - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) directChannel.TeamId = "" newChannel, err := s.saveChannelT(transaction, directChannel, 0) @@ -679,7 +756,7 @@ func (s SqlChannelStore) SaveDirectChannel(directChannel *model.Channel, member1 } -func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) { +func (s SqlChannelStore) saveChannelT(transaction *sqlxTxWrapper, channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) { if channel.Id != "" && !channel.IsShared() { return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id) } @@ -690,17 +767,21 @@ func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *mo } if channel.Type != model.ChannelTypeDirect && channel.Type != model.ChannelTypeGroup && maxChannelsPerTeam >= 0 { - if count, err := transaction.SelectInt("SELECT COUNT(0) FROM Channels WHERE TeamId = :TeamId AND DeleteAt = 0 AND (Type = 'O' OR Type = 'P')", map[string]interface{}{"TeamId": channel.TeamId}); err != nil { + var count int64 + if err := transaction.Get(&count, "SELECT COUNT(0) FROM Channels WHERE TeamId = ? AND DeleteAt = 0 AND (Type = 'O' OR Type = 'P')", channel.TeamId); err != nil { return nil, errors.Wrapf(err, "save_channel_count: teamId=%s", channel.TeamId) } else if count >= maxChannelsPerTeam { return nil, store.NewErrLimitExceeded("channels_per_team", int(count), "teamId="+channel.TeamId) } } - if err := transaction.Insert(channel); err != nil { + if _, err := transaction.NamedExec(`INSERT INTO Channels + (Id, CreateAt, UpdateAt, DeleteAt, TeamId, Type, DisplayName, Name, Header, Purpose, LastPostAt, TotalMsgCount, ExtraUpdateAt, CreatorId, SchemeId, GroupConstrained, Shared, TotalMsgCountRoot, LastRootPostAt) + VALUES + (:Id, :CreateAt, :UpdateAt, :DeleteAt, :TeamId, :Type, :DisplayName, :Name, :Header, :Purpose, :LastPostAt, :TotalMsgCount, :ExtraUpdateAt, :CreatorId, :SchemeId, :GroupConstrained, :Shared, :TotalMsgCountRoot, :LastRootPostAt)`, channel); err != nil { if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { dupChannel := model.Channel{} - s.GetMaster().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name = :Name", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) + s.GetMasterX().Get(&dupChannel, "SELECT * FROM Channels WHERE TeamId = ? AND Name = ?", channel.TeamId, channel.Name) return &dupChannel, store.NewErrConflict("Channel", err, "id="+channel.Id) } return nil, errors.Wrapf(err, "save_channel: id=%s", channel.Id) @@ -710,11 +791,11 @@ func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *mo // Update writes the updated channel to the database. func (s SqlChannelStore) Update(channel *model.Channel) (*model.Channel, error) { - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) updatedChannel, err := s.updateChannelT(transaction, channel) if err != nil { @@ -732,7 +813,7 @@ func (s SqlChannelStore) Update(channel *model.Channel) (*model.Channel, error) return updatedChannel, nil } -func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel *model.Channel) (*model.Channel, error) { +func (s SqlChannelStore) updateChannelT(transaction *sqlxTxWrapper, channel *model.Channel) (*model.Channel, error) { channel.PreUpdate() if channel.DeleteAt != 0 { @@ -743,11 +824,30 @@ func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel * return nil, err } - count, err := transaction.Update(channel) + res, err := transaction.NamedExec(`UPDATE Channels + SET CreateAt=:CreateAt, + UpdateAt=:UpdateAt, + DeleteAt=:DeleteAt, + TeamId=:TeamId, + Type=:Type, + DisplayName=:DisplayName, + Name=:Name, + Header=:Header, + Purpose=:Purpose, + LastPostAt=:LastPostAt, + TotalMsgCount=:TotalMsgCount, + ExtraUpdateAt=:ExtraUpdateAt, + CreatorId=:CreatorId, + SchemeId=:SchemeId, + GroupConstrained=:GroupConstrained, + Shared=:Shared, + TotalMsgCountRoot=:TotalMsgCountRoot, + LastRootPostAt=:LastRootPostAt + WHERE Id=:Id`, channel) if err != nil { if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { dupChannel := model.Channel{} - s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) + s.GetReplicaX().Get(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) if dupChannel.DeleteAt > 0 { return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id) } @@ -756,6 +856,10 @@ func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel * return nil, errors.Wrapf(err, "failed to update channel with id=%s", channel.Id) } + count, err := res.RowsAffected() + if err != nil { + return nil, errors.Wrap(err, "error while getting rowsAffected in updateChannelT") + } if count > 1 { return nil, fmt.Errorf("the expected number of channels to be updated is <=1 but was %d", count) } @@ -765,17 +869,17 @@ func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel * func (s SqlChannelStore) GetChannelUnread(channelId, userId string) (*model.ChannelUnread, error) { var unreadChannel model.ChannelUnread - err := s.GetReplica().SelectOne(&unreadChannel, + err := s.GetReplicaX().Get(&unreadChannel, `SELECT Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, (Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot, ChannelMembers.MentionCount MentionCount, ChannelMembers.MentionCountRoot MentionCountRoot, ChannelMembers.NotifyProps NotifyProps FROM Channels, ChannelMembers WHERE Id = ChannelId - AND Id = :ChannelId - AND UserId = :UserId + AND Id = ? + AND UserId = ? AND DeleteAt = 0`, - map[string]interface{}{"ChannelId": channelId, "UserId": userId}) + channelId, userId) if err != nil { if err == sql.ErrNoRows { @@ -805,12 +909,12 @@ func (s SqlChannelStore) Get(id string, allowFromCache bool) (*model.Channel, er func (s SqlChannelStore) GetPinnedPosts(channelId string) (*model.PostList, error) { pl := model.NewPostList() - var posts []*model.Post - if _, err := s.GetReplica().Select(&posts, "SELECT *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE IsPinned = true AND ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt ASC", map[string]interface{}{"ChannelId": channelId}); err != nil { + posts := []*postInternal{} + if err := s.GetReplicaX().Select(&posts, "SELECT *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE IsPinned = true AND ChannelId = ? AND DeleteAt = 0 ORDER BY CreateAt ASC", channelId); err != nil { return nil, errors.Wrap(err, "failed to find Posts") } for _, post := range posts { - pl.AddPost(post) + pl.AddPost(post.ToModel()) pl.AddOrder(post.Id) } return pl, nil @@ -821,25 +925,24 @@ func (s SqlChannelStore) GetFromMaster(id string) (*model.Channel, error) { } func (s SqlChannelStore) get(id string, master bool) (*model.Channel, error) { - var db *gorp.DbMap + var db *sqlxDBWrapper if master { - db = s.GetMaster() + db = s.GetMasterX() } else { - db = s.GetReplica() + db = s.GetReplicaX() } - obj, err := db.Get(model.Channel{}, id) + ch := model.Channel{} + err := db.Get(&ch, `SELECT * FROM Channels WHERE Id=?`, id) if err != nil { + if err == sql.ErrNoRows { + return nil, store.NewErrNotFound("Channel", id) + } return nil, errors.Wrapf(err, "failed to find channel with id = %s", id) } - if obj == nil { - return nil, store.NewErrNotFound("Channel", id) - } - - ch := obj.(*model.Channel) - return ch, nil + return &ch, nil } // Delete records the given deleted timestamp to the channel in question. @@ -856,11 +959,11 @@ func (s SqlChannelStore) Restore(channelId string, time int64) error { func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) error { defer s.InvalidateChannel(channelId) - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return errors.Wrap(err, "SetDeleteAt: begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) err = s.setDeleteAtT(transaction, channelId, deleteAt, updateAt) if err != nil { @@ -872,13 +975,10 @@ func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) UPDATE PublicChannels SET - DeleteAt = :DeleteAt + DeleteAt = ? WHERE - Id = :ChannelId - `, map[string]interface{}{ - "DeleteAt": deleteAt, - "ChannelId": channelId, - }); err != nil { + Id = ? + `, deleteAt, channelId); err != nil { return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId) } @@ -889,8 +989,11 @@ func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) return nil } -func (s SqlChannelStore) setDeleteAtT(transaction *gorp.Transaction, channelId string, deleteAt, updateAt int64) error { - _, err := transaction.Exec("Update Channels SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :ChannelId", map[string]interface{}{"DeleteAt": deleteAt, "UpdateAt": updateAt, "ChannelId": channelId}) +func (s SqlChannelStore) setDeleteAtT(transaction *sqlxTxWrapper, channelId string, deleteAt, updateAt int64) error { + _, err := transaction.Exec(`UPDATE Channels + SET DeleteAt = ?, + UpdateAt = ? + WHERE Id = ?`, deleteAt, updateAt, channelId) if err != nil { return errors.Wrapf(err, "failed to delete channel with id=%s", channelId) } @@ -900,11 +1003,11 @@ func (s SqlChannelStore) setDeleteAtT(transaction *gorp.Transaction, channelId s // PermanentDeleteByTeam removes all channels for the given team from the database. func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) error { - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return errors.Wrap(err, "PermanentDeleteByTeam: begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) if err := s.permanentDeleteByTeamtT(transaction, teamId); err != nil { return errors.Wrap(err, "permanentDeleteByTeamtT") @@ -915,10 +1018,8 @@ func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) error { DELETE FROM PublicChannels WHERE - TeamId = :TeamId - `, map[string]interface{}{ - "TeamId": teamId, - }); err != nil { + TeamId = ? + `, teamId); err != nil { return errors.Wrapf(err, "failed to delete public channels by team with teamId=%s", teamId) } @@ -929,8 +1030,8 @@ func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) error { return nil } -func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *gorp.Transaction, teamId string) error { - if _, err := transaction.Exec("DELETE FROM Channels WHERE TeamId = :TeamId", map[string]interface{}{"TeamId": teamId}); err != nil { +func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *sqlxTxWrapper, teamId string) error { + if _, err := transaction.Exec("DELETE FROM Channels WHERE TeamId = ?", teamId); err != nil { return errors.Wrapf(err, "failed to delete channel by team with teamId=%s", teamId) } @@ -939,11 +1040,11 @@ func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *gorp.Transaction, // PermanentDelete removes the given channel from the database. func (s SqlChannelStore) PermanentDelete(channelId string) error { - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return errors.Wrap(err, "PermanentDelete: begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) if err := s.permanentDeleteT(transaction, channelId); err != nil { return errors.Wrap(err, "permanentDeleteT") @@ -954,10 +1055,8 @@ func (s SqlChannelStore) PermanentDelete(channelId string) error { DELETE FROM PublicChannels WHERE - Id = :ChannelId - `, map[string]interface{}{ - "ChannelId": channelId, - }); err != nil { + Id = ? + `, channelId); err != nil { return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId) } @@ -968,8 +1067,8 @@ func (s SqlChannelStore) PermanentDelete(channelId string) error { return nil } -func (s SqlChannelStore) permanentDeleteT(transaction *gorp.Transaction, channelId string) error { - if _, err := transaction.Exec("DELETE FROM Channels WHERE Id = :ChannelId", map[string]interface{}{"ChannelId": channelId}); err != nil { +func (s SqlChannelStore) permanentDeleteT(transaction *sqlxTxWrapper, channelId string) error { + if _, err := transaction.Exec("DELETE FROM Channels WHERE Id = ?", channelId); err != nil { return errors.Wrapf(err, "failed to delete channel with id=%s", channelId) } @@ -977,7 +1076,7 @@ func (s SqlChannelStore) permanentDeleteT(transaction *gorp.Transaction, channel } func (s SqlChannelStore) PermanentDeleteMembersByChannel(channelId string) error { - _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId}) + _, err := s.GetMasterX().Exec("DELETE FROM ChannelMembers WHERE ChannelId = ?", channelId) if err != nil { return errors.Wrapf(err, "failed to delete Channel with channelId=%s", channelId) } @@ -1018,13 +1117,13 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDelete query = query.Where(sq.Eq{"DeleteAt": 0}) } - var channels model.ChannelList + channels := model.ChannelList{} sql, args, err := query.ToSql() if err != nil { return nil, errors.Wrapf(err, "getchannels_tosql") } - _, err = s.GetReplica().Select(&channels, sql, args...) + err = s.GetReplicaX().Select(&channels, sql, args...) if err != nil { return nil, errors.Wrapf(err, "failed to get channels with TeamId=%s and UserId=%s", teamId, userId) } @@ -1075,8 +1174,8 @@ func (s SqlChannelStore) GetChannelsByUser(userId string, includeDeleted bool, l return nil, errors.Wrapf(err, "getchannels_tosql") } - var channels model.ChannelList - _, err = s.GetReplica().Select(&channels, sql, args...) + channels := model.ChannelList{} + err = s.GetReplicaX().Select(&channels, sql, args...) if err != nil { return nil, errors.Wrapf(err, "failed to get channels with UserId=%s", userId) } @@ -1089,8 +1188,8 @@ func (s SqlChannelStore) GetChannelsByUser(userId string, includeDeleted bool, l } func (s SqlChannelStore) GetAllChannelMembersById(channelID string) ([]string, error) { - var dbMembers channelMemberWithSchemeRolesList - _, err := s.GetReplica().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelID}) + dbMembers := channelMemberWithSchemeRolesList{} + err := s.GetReplicaX().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelId = ?", channelID) if err != nil { return nil, errors.Wrapf(err, "failed to get ChannelMembers with channelID=%s", channelID) } @@ -1106,21 +1205,23 @@ func (s SqlChannelStore) GetAllChannelMembersById(channelID string) ([]string, e func (s SqlChannelStore) GetAllChannels(offset, limit int, opts store.ChannelSearchOpts) (model.ChannelListWithTeamData, error) { query := s.getAllChannelsQuery(opts, false) - query = query.OrderBy("c.DisplayName, Teams.DisplayName").Limit(uint64(limit)).Offset(uint64(offset)) + query = query. + OrderBy("c.DisplayName, Teams.DisplayName"). + Limit(uint64(limit)). + Offset(uint64(offset)) queryString, args, err := query.ToSql() if err != nil { return nil, errors.Wrap(err, "failed to create query") } - var data model.ChannelListWithTeamData - _, err = s.GetReplica().Select(&data, queryString, args...) - + data := []*channelWithTeamDataInternal{} + err = s.GetReplicaX().Select(&data, queryString, args...) if err != nil { return nil, errors.Wrap(err, "failed to get all channels") } - return data, nil + return channelWithTeamDataSliceToModel(data), nil } func (s SqlChannelStore) GetAllChannelsCount(opts store.ChannelSearchOpts) (int64, error) { @@ -1131,7 +1232,8 @@ func (s SqlChannelStore) GetAllChannelsCount(opts store.ChannelSearchOpts) (int6 return 0, errors.Wrap(err, "failed to create query") } - count, err := s.GetReplica().SelectInt(queryString, args...) + var count int64 + err = s.GetReplicaX().Get(&count, queryString, args...) if err != nil { return 0, errors.Wrap(err, "failed to count all channels") } @@ -1182,8 +1284,8 @@ func (s SqlChannelStore) getAllChannelsQuery(opts store.ChannelSearchOpts, forCo } func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) (model.ChannelList, error) { - var channels model.ChannelList - _, err := s.GetReplica().Select(&channels, ` + channels := model.ChannelList{} + err := s.GetReplicaX().Select(&channels, ` SELECT Channels.* FROM @@ -1191,7 +1293,7 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset in JOIN PublicChannels c ON (c.Id = Channels.Id) WHERE - c.TeamId = :TeamId + c.TeamId = ? AND c.DeleteAt = 0 AND c.Id NOT IN ( SELECT @@ -1201,20 +1303,15 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset in JOIN ChannelMembers cm ON (cm.ChannelId = c.Id) WHERE - c.TeamId = :TeamId - AND cm.UserId = :UserId + c.TeamId = ? + AND cm.UserId = ? AND c.DeleteAt = 0 ) ORDER BY c.DisplayName - LIMIT :Limit - OFFSET :Offset - `, map[string]interface{}{ - "TeamId": teamId, - "UserId": userId, - "Limit": limit, - "Offset": offset, - }) + LIMIT ? + OFFSET ? + `, teamId, teamId, userId, limit, offset) if err != nil { return nil, errors.Wrapf(err, "failed getting channels with teamId=%s and userId=%s", teamId, userId) @@ -1224,7 +1321,7 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset in } func (s SqlChannelStore) GetPrivateChannelsForTeam(teamId string, offset int, limit int) (model.ChannelList, error) { - var channels model.ChannelList + channels := model.ChannelList{} builder := s.getQueryBuilder(). Select("*"). @@ -1239,7 +1336,7 @@ func (s SqlChannelStore) GetPrivateChannelsForTeam(teamId string, offset int, li return nil, errors.Wrap(err, "channels_tosql") } - _, err = s.GetReplica().Select(&channels, query, args...) + err = s.GetReplicaX().Select(&channels, query, args...) if err != nil { return nil, errors.Wrapf(err, "failed to find chaneld with teamId=%s", teamId) } @@ -1247,8 +1344,8 @@ func (s SqlChannelStore) GetPrivateChannelsForTeam(teamId string, offset int, li } func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) (model.ChannelList, error) { - var channels model.ChannelList - _, err := s.GetReplica().Select(&channels, ` + channels := model.ChannelList{} + err := s.GetReplicaX().Select(&channels, ` SELECT Channels.* FROM @@ -1256,16 +1353,12 @@ func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, lim JOIN PublicChannels pc ON (pc.Id = Channels.Id) WHERE - pc.TeamId = :TeamId + pc.TeamId = ? AND pc.DeleteAt = 0 ORDER BY pc.DisplayName - LIMIT :Limit - OFFSET :Offset - `, map[string]interface{}{ - "TeamId": teamId, - "Limit": limit, - "Offset": offset, - }) + LIMIT ? + OFFSET ? + `, teamId, limit, offset) if err != nil { return nil, errors.Wrapf(err, "failed to find chaneld with teamId=%s", teamId) @@ -1290,20 +1383,23 @@ func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds } var data model.ChannelList - _, err := s.GetReplica().Select(&data, ` - SELECT - Channels.* - FROM - Channels - JOIN - PublicChannels pc ON (pc.Id = Channels.Id) - WHERE - pc.TeamId = :teamId - AND pc.DeleteAt = 0 - AND pc.Id IN (`+idQuery+`) - ORDER BY pc.DisplayName - `, props) + builder := s.getQueryBuilder(). + Select("Channels.*"). + From("Channels"). + Join("PublicChannels pc ON (pc.Id = Channels.Id)"). + Where(sq.And{ + sq.Eq{"pc.TeamId": teamId}, + sq.Eq{"pc.DeleteAt": 0}, + sq.Eq{"pc.Id": channelIds}, + }). + OrderBy("pc.DisplayName") + + queryString, args, err := builder.ToSql() + if err != nil { + return nil, errors.Wrap(err, "GetPublicChannelsByIdsForTeam to_sql") + } + err = s.GetReplicaX().Select(&data, queryString, args...) if err != nil { return nil, errors.Wrap(err, "failed to find Channels") } @@ -1315,22 +1411,29 @@ func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds return data, nil } -type channelIdWithCountAndUpdateAt struct { - Id string - TotalMsgCount int64 - TotalMsgCountRoot int64 - UpdateAt int64 -} - func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, error) { - var data []channelIdWithCountAndUpdateAt - _, err := s.GetReplica().Select(&data, "SELECT Id, TotalMsgCount, TotalMsgCountRoot, UpdateAt FROM Channels WHERE Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) AND (TeamId = :TeamId OR TeamId = '') AND DeleteAt = 0 ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) + data := []struct { + Id string + TotalMsgCount int64 + TotalMsgCountRoot int64 + UpdateAt int64 + }{} + err := s.GetReplicaX().Select(&data, `SELECT Id, TotalMsgCount, TotalMsgCountRoot, UpdateAt + FROM Channels + WHERE Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = ?) + AND (TeamId = ? OR TeamId = '') + AND DeleteAt = 0 + ORDER BY DisplayName`, userId, teamId) if err != nil { return nil, errors.Wrapf(err, "failed to get channels count with teamId=%s and userId=%s", teamId, userId) } - counts := &model.ChannelCounts{Counts: make(map[string]int64), CountsRoot: make(map[string]int64), UpdateTimes: make(map[string]int64)} + counts := &model.ChannelCounts{ + Counts: make(map[string]int64), + CountsRoot: make(map[string]int64), + UpdateTimes: make(map[string]int64), + } for i := range data { v := data[i] counts.Counts[v.Id] = v.TotalMsgCount @@ -1342,9 +1445,8 @@ func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) (*model. } func (s SqlChannelStore) GetTeamChannels(teamId string) (model.ChannelList, error) { - var data model.ChannelList - _, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId And Type != 'D' ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId}) - + data := model.ChannelList{} + err := s.GetReplicaX().Select(&data, "SELECT * FROM Channels WHERE TeamId = ? And Type != 'D' ORDER BY DisplayName", teamId) if err != nil { return nil, errors.Wrapf(err, "failed to find Channels with teamId=%s", teamId) } @@ -1382,24 +1484,27 @@ func (s SqlChannelStore) GetByNames(teamId string, names []string, allowFromCach } if len(names) > 0 { - props := map[string]interface{}{} - var namePlaceholders []string - for _, name := range names { - key := fmt.Sprintf("Name%v", len(namePlaceholders)) - props[key] = name - namePlaceholders = append(namePlaceholders, ":"+key) + builder := s.getQueryBuilder(). + Select("*"). + From("Channels"). + Where( + sq.And{ + sq.Eq{"Name": names}, + sq.Eq{"DeleteAt": 0}, + }, + ) + + if teamId != "" { + builder = builder.Where(sq.Eq{"TeamId": teamId}) } - var query string - if teamId == "" { - query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0` - } else { - props["TeamId"] = teamId - query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND TeamId = :TeamId AND DeleteAt = 0` + query, args, err := builder.ToSql() + if err != nil { + return nil, errors.Wrap(err, "GetByNames_tosql") } - var dbChannels []*model.Channel - if _, err := s.GetReplica().Select(&dbChannels, query, props); err != nil && err != sql.ErrNoRows { + dbChannels := []*model.Channel{} + if err := s.GetReplicaX().Select(&dbChannels, query, args...); err != nil && err != sql.ErrNoRows { msg := fmt.Sprintf("failed to get channels with names=%v", names) if teamId != "" { msg += fmt.Sprintf("teamId=%s", teamId) @@ -1431,9 +1536,9 @@ func (s SqlChannelStore) GetByNameIncludeDeleted(teamId string, name string, all func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bool, allowFromCache bool) (*model.Channel, error) { var query string if includeDeleted { - query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name" + query = "SELECT * FROM Channels WHERE (TeamId = ? OR TeamId = '') AND Name = ?" } else { - query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt = 0" + query = "SELECT * FROM Channels WHERE (TeamId = ? OR TeamId = '') AND Name = ? AND DeleteAt = 0" } channel := model.Channel{} @@ -1450,7 +1555,7 @@ func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bo } } - if err := s.GetReplica().SelectOne(&channel, query, map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil { + if err := s.GetReplicaX().Get(&channel, query, teamId, name); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s&Name=%s", teamId, name)) } @@ -1464,7 +1569,11 @@ func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bo func (s SqlChannelStore) GetDeletedByName(teamId string, name string) (*model.Channel, error) { channel := model.Channel{} - if err := s.GetReplica().SelectOne(&channel, "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt != 0", map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil { + if err := s.GetReplicaX().Get(&channel, `SELECT * + FROM Channels + WHERE (TeamId = ? OR TeamId = '') + AND Name = ? + AND DeleteAt != 0`, teamId, name); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Channel", fmt.Sprintf("name=%s", name)) } @@ -1475,23 +1584,23 @@ func (s SqlChannelStore) GetDeletedByName(teamId string, name string) (*model.Ch } func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int, userId string) (model.ChannelList, error) { - var channels model.ChannelList + channels := model.ChannelList{} query := ` SELECT * FROM Channels - WHERE (TeamId = :TeamId OR TeamId = '') + WHERE (TeamId = ? OR TeamId = '') AND DeleteAt != 0 AND Type != 'P' UNION SELECT * FROM Channels - WHERE (TeamId = :TeamId OR TeamId = '') + WHERE (TeamId = ? OR TeamId = '') AND DeleteAt != 0 AND Type = 'P' - AND Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) - ORDER BY DisplayName LIMIT :Limit OFFSET :Offset + AND Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = ?) + ORDER BY DisplayName LIMIT ? OFFSET ? ` - if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset, "UserId": userId}); err != nil { + if err := s.GetReplicaX().Select(&channels, query, teamId, teamId, userId, limit, offset); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s,UserId=%s", teamId, userId)) } @@ -1612,13 +1721,13 @@ func (s SqlChannelStore) saveMultipleMembers(members []*model.ChannelMember) ([] return nil, errors.Wrap(err, "channel_roles_tosql") } - var defaultChannelsRoles []struct { + defaultChannelsRoles := []struct { Id string Guest sql.NullString User sql.NullString Admin sql.NullString - } - _, err = s.GetMaster().Select(&defaultChannelsRoles, channelRolesSql, channelRolesArgs...) + }{} + err = s.GetMasterX().Select(&defaultChannelsRoles, channelRolesSql, channelRolesArgs...) if err != nil { return nil, errors.Wrap(err, "default_channel_roles_select") } @@ -1651,13 +1760,13 @@ func (s SqlChannelStore) saveMultipleMembers(members []*model.ChannelMember) ([] return nil, errors.Wrap(err, "team_roles_tosql") } - var defaultTeamsRoles []struct { + defaultTeamsRoles := []struct { Id string Guest sql.NullString User sql.NullString Admin sql.NullString - } - _, err = s.GetMaster().Select(&defaultTeamsRoles, teamRolesSql, teamRolesArgs...) + }{} + err = s.GetMasterX().Select(&defaultTeamsRoles, teamRolesSql, teamRolesArgs...) if err != nil { return nil, errors.Wrap(err, "default_team_roles_select") } @@ -1676,7 +1785,7 @@ func (s SqlChannelStore) saveMultipleMembers(members []*model.ChannelMember) ([] return nil, errors.Wrap(err, "channel_members_tosql") } - if _, err := s.GetMaster().Exec(sql, args...); err != nil { + if _, err := s.GetMasterX().Exec(sql, args...); err != nil { if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey", "PRIMARY"}) { return nil, store.NewErrConflict("ChannelMembers", err, "") } @@ -1725,23 +1834,35 @@ func (s SqlChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ( } } - var transaction *gorp.Transaction + var transaction *sqlxTxWrapper var err error - if transaction, err = s.GetMaster().Begin(); err != nil { + if transaction, err = s.GetMasterX().Beginx(); err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) updatedMembers := []*model.ChannelMember{} for _, member := range members { - if _, err := transaction.Update(NewChannelMemberFromModel(member)); err != nil { + if _, err := transaction.NamedExec(`UPDATE ChannelMembers + SET Roles=:Roles, + LastViewedAt=:LastViewedAt, + MsgCount=:MsgCount, + MentionCount=:MentionCount, + NotifyProps=:NotifyProps, + LastUpdateAt=:LastUpdateAt, + SchemeUser=:SchemeUser, + SchemeAdmin=:SchemeAdmin, + SchemeGuest=:SchemeGuest, + MentionCountRoot=:MentionCountRoot, + MsgCountRoot=:MsgCountRoot + WHERE ChannelId=:ChannelId AND UserId=:UserId`, NewChannelMemberFromModel(member)); err != nil { return nil, errors.Wrap(err, "failed to update ChannelMember") } // TODO: Get this out of the transaction when is possible var dbMember channelMemberWithSchemeRoles - if err := transaction.SelectOne(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil { + if err := transaction.Get(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = ? AND ChannelMembers.UserId = ?", member.ChannelId, member.UserId); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("ChannelMember", fmt.Sprintf("channelId=%s, userId=%s", member.ChannelId, member.UserId)) } @@ -1765,16 +1886,16 @@ func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) (*model.Chann } func (s SqlChannelStore) UpdateMemberNotifyProps(channelID, userID string, props map[string]string) (*model.ChannelMember, error) { - tx, err := s.GetMaster().Begin() + tx, err := s.GetMasterX().Beginx() if err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(tx) + defer finalizeTransactionX(tx) if s.DriverName() == model.DatabaseDriverPostgres { _, err = tx.Exec(`UPDATE channelmembers - SET notifyprops = notifyprops || $1::jsonb - WHERE userid=$2 AND channelid=$3`, model.MapToJSON(props), userID, channelID) + SET notifyprops = notifyprops || ?::jsonb + WHERE userid=? AND channelid=?`, model.MapToJSON(props), userID, channelID) } else { // It's difficult to construct a SQL query for MySQL // to handle a case of empty map. So we just ignore it. @@ -1796,7 +1917,7 @@ func (s SqlChannelStore) UpdateMemberNotifyProps(channelID, userID string, props } var dbMember channelMemberWithSchemeRoles - if err2 := tx.SelectOne(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelID, "UserId": userID}); err2 != nil { + if err2 := tx.Get(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = ? AND ChannelMembers.UserId = ?", channelID, userID); err2 != nil { if err2 == sql.ErrNoRows { return nil, store.NewErrNotFound("ChannelMember", fmt.Sprintf("channelId=%s, userId=%s", channelID, userID)) } @@ -1811,8 +1932,8 @@ func (s SqlChannelStore) UpdateMemberNotifyProps(channelID, userID string, props } func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) (model.ChannelMembers, error) { - var dbMembers channelMemberWithSchemeRolesList - _, err := s.GetReplica().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset}) + dbMembers := channelMemberWithSchemeRolesList{} + err := s.GetReplicaX().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelId = ? LIMIT ? OFFSET ?", channelId, limit, offset) if err != nil { return nil, errors.Wrapf(err, "failed to get ChannelMembers with channelId=%s", channelId) } @@ -1821,16 +1942,16 @@ func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) (model. } func (s SqlChannelStore) GetChannelMembersTimezones(channelId string) ([]model.StringMap, error) { - var dbMembersTimezone []model.StringMap - _, err := s.GetReplica().Select(&dbMembersTimezone, ` + dbMembersTimezone := []model.StringMap{} + err := s.GetReplicaX().Select(&dbMembersTimezone, ` SELECT Users.Timezone FROM ChannelMembers LEFT JOIN Users ON ChannelMembers.UserId = Id - WHERE ChannelId = :ChannelId - `, map[string]interface{}{"ChannelId": channelId}) + WHERE ChannelId = ? + `, channelId) if err != nil { return nil, errors.Wrapf(err, "failed to find user timezones for users in channels with channelId=%s", channelId) @@ -1842,7 +1963,7 @@ func (s SqlChannelStore) GetChannelMembersTimezones(channelId string) ([]model.S func (s SqlChannelStore) GetMember(ctx context.Context, channelId string, userId string) (*model.ChannelMember, error) { var dbMember channelMemberWithSchemeRoles - if err := s.DBFromContext(ctx).SelectOne(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { + if err := s.DBXFromContext(ctx).Get(&dbMember, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.ChannelId = ? AND ChannelMembers.UserId = ?", channelId, userId); err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("ChannelMember", fmt.Sprintf("channelId=%s, userId=%s", channelId, userId)) } @@ -1913,10 +2034,10 @@ func (s SqlChannelStore) GetMemberForPost(postId string, userId string) (*model. LEFT JOIN Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id WHERE - ChannelMembers.UserId = :UserId + ChannelMembers.UserId = ? AND - Posts.Id = :PostId` - if err := s.GetReplica().SelectOne(&dbMember, query, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil { + Posts.Id = ?` + if err := s.GetReplicaX().Get(&dbMember, query, userId, postId); err != nil { return nil, errors.Wrapf(err, "failed to get ChannelMember with postId=%s and userId=%s", postId, userId) } return dbMember.ToModel(), nil @@ -1966,7 +2087,7 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac return nil, errors.Wrap(err, "channel_tosql") } - rows, err := s.GetReplica().Db.Query(queryString, args...) + rows, err := s.GetReplicaX().DB.Query(queryString, args...) if err != nil { return nil, errors.Wrap(err, "failed to find ChannelMembers, TeamScheme and ChannelScheme data") } @@ -2024,12 +2145,11 @@ func (s SqlChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId str s.metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel") } - var data []allChannelMemberNotifyProps - _, err := s.GetReplica().Select(&data, ` + data := []allChannelMemberNotifyProps{} + err := s.GetReplicaX().Select(&data, ` SELECT UserId, NotifyProps FROM ChannelMembers - WHERE ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId}) - + WHERE ChannelId = ?`, channelId) if err != nil { return nil, errors.Wrapf(err, "failed to find data from ChannelMembers with channelId=%s", channelId) } @@ -2055,7 +2175,8 @@ func (s SqlChannelStore) GetMemberCountFromCache(channelId string) int64 { //nolint:unparam func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) (int64, error) { - count, err := s.GetReplica().SelectInt(` + var count int64 + err := s.GetReplicaX().Get(&count, ` SELECT count(*) FROM @@ -2063,8 +2184,8 @@ func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) ( Users WHERE ChannelMembers.UserId = Users.Id - AND ChannelMembers.ChannelId = :ChannelId - AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) + AND ChannelMembers.ChannelId = ? + AND Users.DeleteAt = 0`, channelId) if err != nil { return 0, errors.Wrapf(err, "failed to count ChanenelMembers with channelId=%s", channelId) } @@ -2117,8 +2238,8 @@ func (s SqlChannelStore) GetMemberCountsByGroup(ctx context.Context, channelID s return nil, errors.Wrap(err, "channel_tosql") } - var data []*model.ChannelMemberCountByGroup - if _, err = s.DBFromContext(ctx).Select(&data, queryString, args...); err != nil { + data := []*model.ChannelMemberCountByGroup{} + if err := s.DBXFromContext(ctx).Select(&data, queryString, args...); err != nil { return nil, errors.Wrapf(err, "failed to count ChannelMembers with channelId=%s", channelID) } @@ -2131,13 +2252,14 @@ func (s SqlChannelStore) InvalidatePinnedPostCount(channelId string) { //nolint:unparam func (s SqlChannelStore) GetPinnedPostCount(channelId string, allowFromCache bool) (int64, error) { - count, err := s.GetReplica().SelectInt(` + var count int64 + err := s.GetReplicaX().Get(&count, ` SELECT count(*) FROM Posts WHERE IsPinned = true - AND ChannelId = :ChannelId - AND DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) + AND ChannelId = ? + AND DeleteAt = 0`, channelId) if err != nil { return 0, errors.Wrapf(err, "failed to count pinned Posts with channelId=%s", channelId) @@ -2156,7 +2278,8 @@ func (s SqlChannelStore) GetGuestCount(channelId string, allowFromCache bool) (i if s.DriverName() == model.DatabaseDriverMysql { indexHint = `USE INDEX(idx_channelmembers_channel_id_scheme_guest_user_id)` } - count, err := s.GetReplica().SelectInt(` + var count int64 + err := s.GetReplicaX().Get(&count, ` SELECT count(*) FROM @@ -2164,9 +2287,9 @@ func (s SqlChannelStore) GetGuestCount(channelId string, allowFromCache bool) (i Users WHERE ChannelMembers.UserId = Users.Id - AND ChannelMembers.ChannelId = :ChannelId + AND ChannelMembers.ChannelId = ? AND ChannelMembers.SchemeGuest = TRUE - AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) + AND Users.DeleteAt = 0`, channelId) if err != nil { return 0, errors.Wrapf(err, "failed to count Guests with channelId=%s", channelId) } @@ -2182,7 +2305,7 @@ func (s SqlChannelStore) RemoveMembers(channelId string, userIds []string) error if err != nil { return errors.Wrap(err, "channel_tosql") } - _, err = s.GetMaster().Exec(query, args...) + _, err = s.GetMasterX().Exec(query, args...) if err != nil { return errors.Wrap(err, "failed to delete ChannelMembers") } @@ -2197,7 +2320,7 @@ func (s SqlChannelStore) RemoveMembers(channelId string, userIds []string) error if err != nil { return errors.Wrap(err, "channel_tosql") } - _, err = s.GetMaster().Exec(query, args...) + _, err = s.GetMasterX().Exec(query, args...) if err != nil { return errors.Wrap(err, "failed to delete SidebarChannels") } @@ -2223,10 +2346,10 @@ func (s SqlChannelStore) RemoveAllDeactivatedMembers(channelId string) error { Users.DeleteAt != 0 ) AND - ChannelMembers.ChannelId = :ChannelId + ChannelMembers.ChannelId = ? ` - _, err := s.GetMaster().Exec(query, map[string]interface{}{"ChannelId": channelId}) + _, err := s.GetMasterX().Exec(query, channelId) if err != nil { return errors.Wrapf(err, "failed to delete ChannelMembers with channelId=%s", channelId) } @@ -2234,12 +2357,13 @@ func (s SqlChannelStore) RemoveAllDeactivatedMembers(channelId string) error { } func (s SqlChannelStore) PermanentDeleteMembersByUser(userId string) error { - if _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { + if _, err := s.GetMasterX().Exec("DELETE FROM ChannelMembers WHERE UserId = ?", userId); err != nil { return errors.Wrapf(err, "failed to permanent delete ChannelMembers with userId=%s", userId) } return nil } +// TODO: convert to squirrel func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string, updateThreads bool) (map[string]int64, error) { var threadsToUpdate []string now := model.GetMillis() @@ -2359,20 +2483,30 @@ func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, user model.PostTypeAddToTeam, model.PostTypeRemoveFromTeam, } - query := s.getQueryBuilder().Select("count(*)").From("Posts").Where(sq.Eq{"ChannelId": channelId}).Where(sq.Gt{"CreateAt": timestamp}).Where(sq.NotEq{"Type": joinLeavePostTypes}).Where(sq.Eq{"DeleteAt": 0}) + query := s.getQueryBuilder(). + Select("count(*)"). + From("Posts"). + Where(sq.And{ + sq.Eq{"ChannelId": channelId}, + sq.Gt{"CreateAt": timestamp}, + sq.NotEq{"Type": joinLeavePostTypes}, + sq.Eq{"DeleteAt": 0}, + }) if userId != "" { query = query.Where(sq.Eq{"UserId": userId}) } sql, args, _ := query.ToSql() - unread, err := s.GetReplica().SelectInt(sql, args...) + var unread int64 + err := s.GetReplicaX().Get(&unread, sql, args...) if err != nil { return 0, 0, errors.Wrap(err, "failed to count Posts") } sql2, args2, _ := query.Where(sq.Eq{"RootId": ""}).ToSql() - unreadRoot, err := s.GetReplica().SelectInt(sql2, args2...) + var unreadRoot int64 + err = s.GetReplicaX().Get(&unreadRoot, sql2, args2...) if err != nil { return 0, 0, errors.Wrap(err, "failed to count root Posts") } @@ -2404,13 +2538,13 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s params := map[string]interface{}{ "mentions": mentionCount, - "mentionsRoot": mentionCountRoot, - "unreadCount": unread, - "unreadCountRoot": unreadRoot, - "lastViewedAt": unreadDate, - "userId": userID, - "channelId": unreadPost.ChannelId, - "updatedAt": model.GetMillis(), + "mentionsroot": mentionCountRoot, + "unreadcount": unread, + "unreadcountroot": unreadRoot, + "lastviewedat": unreadDate, + "userid": userID, + "channelid": unreadPost.ChannelId, + "updatedat": model.GetMillis(), } // msg count uses the value from channels to prevent counting on older channels where no. of messages can be high. @@ -2420,16 +2554,16 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s ChannelMembers SET MentionCount = :mentions, - MentionCountRoot = :mentionsRoot, - MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount, - MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelId) - :unreadCountRoot, - LastViewedAt = :lastViewedAt, - LastUpdateAt = :updatedAt + MentionCountRoot = :mentionsroot, + MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelid) - :unreadcount, + MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelid) - :unreadcountroot, + LastViewedAt = :lastviewedat, + LastUpdateAt = :updatedat WHERE - UserId = :userId - AND ChannelId = :channelId + UserId = :userid + AND ChannelId = :channelid ` - _, err = s.GetMaster().Exec(setUnreadQuery, params) + _, err = s.GetMasterX().NamedExec(setUnreadQuery, params) if err != nil { return nil, errors.Wrap(err, "failed to update ChannelMembers") } @@ -2449,12 +2583,12 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s ChannelMembers cm LEFT JOIN Channels c ON c.Id=cm.ChannelId WHERE - cm.UserId = :userId - AND cm.channelId = :channelId + cm.UserId = ? + AND cm.channelId = ? AND c.DeleteAt = 0 ` result := &model.ChannelUnreadAt{} - if err = s.GetMaster().SelectOne(result, chanUnreadQuery, params); err != nil { + if err = s.GetMasterX().Get(result, chanUnreadQuery, userID, unreadPost.ChannelId); err != nil { return nil, errors.Wrapf(err, "failed to get ChannelMember with channelId=%s", unreadPost.ChannelId) } @@ -2478,17 +2612,16 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string, if isRoot { rootInc = 1 } - _, err := s.GetMaster().Exec( + _, err := s.GetMasterX().Exec( `UPDATE ChannelMembers SET MentionCount = MentionCount + 1, - MentionCountRoot = MentionCountRoot + :RootInc, - LastUpdateAt = :LastUpdateAt + MentionCountRoot = MentionCountRoot + ?, + LastUpdateAt = ? WHERE - UserId = :UserId - AND ChannelId = :ChannelId`, - map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": now, "RootInc": rootInc}) + UserId = ? + AND ChannelId = ?`, rootInc, now, userId, channelId) if err != nil { return errors.Wrapf(err, "failed to Update ChannelMembers with channelId=%s and userId=%s", channelId, userId) } @@ -2499,8 +2632,8 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string, } func (s SqlChannelStore) GetAll(teamId string) ([]*model.Channel, error) { - var data []*model.Channel - _, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Type != 'D' ORDER BY Name", map[string]interface{}{"TeamId": teamId}) + data := []*model.Channel{} + err := s.GetReplicaX().Select(&data, "SELECT * FROM Channels WHERE TeamId = ? AND Type != 'D' ORDER BY Name", teamId) if err != nil { return nil, errors.Wrapf(err, "failed to find Channels with teamId=%s", teamId) @@ -2510,15 +2643,23 @@ func (s SqlChannelStore) GetAll(teamId string) ([]*model.Channel, error) { } func (s SqlChannelStore) GetChannelsByIds(channelIds []string, includeDeleted bool) ([]*model.Channel, error) { - keys, params := MapStringsToQueryParams(channelIds, "Channel") - query := `SELECT * FROM Channels WHERE Id IN ` + keys + ` ORDER BY Name` + query := s.getQueryBuilder(). + Select("*"). + From("Channels"). + Where(sq.Eq{"Id": channelIds}). + OrderBy("Name") + if !includeDeleted { - query = `SELECT * FROM Channels WHERE DeleteAt=0 AND Id IN ` + keys + ` ORDER BY Name` + query = query.Where(sq.Eq{"DeleteAt": 0}) } - var channels []*model.Channel - _, err := s.GetReplica().Select(&channels, query, params) + sql, args, err := query.ToSql() + if err != nil { + return nil, errors.Wrap(err, "GetChannelsByIds_tosql") + } + channels := []*model.Channel{} + err = s.GetReplicaX().Select(&channels, sql, args...) if err != nil { return nil, errors.Wrap(err, "failed to find Channels") } @@ -2545,8 +2686,8 @@ func (s SqlChannelStore) GetChannelsWithTeamDataByIds(channelIDs []string, inclu return nil, errors.Wrapf(err, "getChannelsWithTeamData_tosql") } - var channels []*model.ChannelWithTeamData - _, err = s.GetReplica().Select(&channels, sql, args...) + channels := []*model.ChannelWithTeamData{} + err = s.GetReplicaX().Select(&channels, sql, args...) if err != nil { return nil, errors.Wrap(err, "failed to find Channels") } @@ -2554,9 +2695,9 @@ func (s SqlChannelStore) GetChannelsWithTeamDataByIds(channelIDs []string, inclu } func (s SqlChannelStore) GetForPost(postId string) (*model.Channel, error) { - channel := &model.Channel{} - if err := s.GetReplica().SelectOne( - channel, + channel := model.Channel{} + if err := s.GetReplicaX().Get( + &channel, `SELECT Channels.* FROM @@ -2564,21 +2705,31 @@ func (s SqlChannelStore) GetForPost(postId string) (*model.Channel, error) { Posts WHERE Channels.Id = Posts.ChannelId - AND Posts.Id = :PostId`, map[string]interface{}{"PostId": postId}); err != nil { + AND Posts.Id = ?`, postId); err != nil { return nil, errors.Wrapf(err, "failed to get Channel with postId=%s", postId) } - return channel, nil + return &channel, nil } func (s SqlChannelStore) AnalyticsTypeCount(teamId string, channelType model.ChannelType) (int64, error) { - query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType" + + query := s.getQueryBuilder(). + Select("COUNT(Id) AS Value"). + From("Channels"). + Where(sq.Eq{"Type": channelType}) if teamId != "" { - query += " AND TeamId = :TeamId" + query = query.Where(sq.Eq{"TeamId": teamId}) } - value, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) + sql, args, err := query.ToSql() + if err != nil { + return int64(0), errors.Wrap(err, "AnalyticsTypeCount_tosql") + } + + var value int64 + err = s.GetReplicaX().Get(&value, sql, args...) if err != nil { return int64(0), errors.Wrap(err, "failed to count Channels") } @@ -2586,13 +2737,25 @@ func (s SqlChannelStore) AnalyticsTypeCount(teamId string, channelType model.Cha } func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType string) (int64, error) { - query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType AND DeleteAt > 0" + query := s.getQueryBuilder(). + Select("COUNT(Id) AS Value"). + From("Channels"). + Where(sq.And{ + sq.Eq{"Type": channelType}, + sq.Gt{"DeleteAt": 0}, + }) if teamId != "" { - query += " AND TeamId = :TeamId" + query = query.Where(sq.Eq{"TeamId": teamId}) } - v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) + sql, args, err := query.ToSql() + if err != nil { + return int64(0), errors.Wrap(err, "AnalyticsDeletedTypeCount_tosql") + } + + var v int64 + err = s.GetReplicaX().Get(&v, sql, args...) if err != nil { return 0, errors.Wrapf(err, "failed to count Channels with teamId=%s and channelType=%s", teamId, channelType) } @@ -2601,8 +2764,8 @@ func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType st } func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) (model.ChannelMembers, error) { - var dbMembers channelMemberWithSchemeRolesList - _, err := s.GetReplica().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.UserId = :UserId AND (Teams.Id = :TeamId OR Teams.Id = '' OR Teams.Id IS NULL)", map[string]interface{}{"TeamId": teamId, "UserId": userId}) + dbMembers := channelMemberWithSchemeRolesList{} + err := s.GetReplicaX().Select(&dbMembers, channelMembersForTeamWithSchemeSelectQuery+"WHERE ChannelMembers.UserId = ? AND (Teams.Id = ? OR Teams.Id = '' OR Teams.Id IS NULL)", userId, teamId) if err != nil { return nil, errors.Wrapf(err, "failed to find ChannelMembers data with teamId=%s and userId=%s", teamId, userId) } @@ -2611,10 +2774,9 @@ func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) (model. } func (s SqlChannelStore) GetMembersForUserWithPagination(userId string, page, perPage int) (model.ChannelMembersWithTeamData, error) { - var dbMembers channelMemberWithTeamWithSchemeRolesList + dbMembers := channelMemberWithTeamWithSchemeRolesList{} offset := page * perPage - _, err := s.GetReplica().Select(&dbMembers, channelMembersWithSchemeSelectQuery+"WHERE ChannelMembers.UserId = :UserId ORDER BY ChannelId ASC Limit :Limit Offset :Offset", map[string]interface{}{"UserId": userId, "Limit": perPage, "Offset": offset}) - + err := s.GetReplicaX().Select(&dbMembers, channelMembersWithSchemeSelectQuery+"WHERE ChannelMembers.UserId = ? ORDER BY ChannelId ASC Limit ? Offset ?", userId, perPage, offset) if err != nil { return nil, errors.Wrapf(err, "failed to find ChannelMembers data with and userId=%s", userId) } @@ -2639,6 +2801,7 @@ func (s SqlChannelStore) GetTeamMembersForChannel(channelID string) ([]string, e return teamMemberIDs, nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) Autocomplete(userID, term string, includeDeleted bool) (model.ChannelListWithTeamData, error) { deleteFilter := "AND c.DeleteAt = 0" if includeDeleted { @@ -2671,6 +2834,7 @@ func (s SqlChannelStore) Autocomplete(userID, term string, includeDeleted bool) }) } +// TODO: rewrite in squirrel func (s SqlChannelStore) AutocompleteInTeam(teamID, userID, term string, includeDeleted bool) (model.ChannelList, error) { deleteFilter := "AND c.DeleteAt = 0" if includeDeleted { @@ -2702,6 +2866,7 @@ func (s SqlChannelStore) AutocompleteInTeam(teamID, userID, term string, include }) } +// TODO: rewrite in squirrel func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (model.ChannelList, error) { deleteFilter := "AND DeleteAt = 0" if includeDeleted { @@ -2754,6 +2919,7 @@ func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId strin return channels, nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string, term string) ([]*model.Channel, error) { queryFormat := ` SELECT @@ -2796,6 +2962,7 @@ func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string return channels, nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) (model.ChannelList, error) { deleteFilter := "AND c.DeleteAt = 0" if includeDeleted { @@ -2820,6 +2987,7 @@ func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted }) } +// TODO: rewrite in squirrel func (s SqlChannelStore) SearchArchivedInTeam(teamId string, term string, userId string) (model.ChannelList, error) { publicChannels, publicErr := s.performSearch(` SELECT @@ -2875,6 +3043,7 @@ func (s SqlChannelStore) SearchArchivedInTeam(teamId string, term string, userId return output, nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) SearchForUserInTeam(userId string, teamId string, term string, includeDeleted bool) (model.ChannelList, error) { deleteFilter := "AND c.DeleteAt = 0" if includeDeleted { @@ -3013,9 +3182,9 @@ func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearch if err != nil { return nil, 0, errors.Wrap(err, "channel_tosql") } - var channels model.ChannelListWithTeamData - if _, err = s.GetReplica().Select(&channels, queryString, args...); err != nil { - return nil, 0, errors.Wrapf(err, "failed to find Channels with term='%s'", term) + channels := []*channelWithTeamDataInternal{} + if err2 := s.GetReplicaX().Select(&channels, queryString, args...); err2 != nil { + return nil, 0, errors.Wrapf(err2, "failed to find Channels with term='%s'", term) } var totalCount int64 @@ -3027,16 +3196,17 @@ func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearch if err != nil { return nil, 0, errors.Wrap(err, "channel_tosql") } - if totalCount, err = s.GetReplica().SelectInt(queryString, args...); err != nil { - return nil, 0, errors.Wrapf(err, "failed to find Channels with term='%s'", term) + if err2 := s.GetReplicaX().Get(&totalCount, queryString, args...); err2 != nil { + return nil, 0, errors.Wrapf(err2, "failed to find Channels with term='%s'", term) } } else { totalCount = int64(len(channels)) } - return channels, totalCount, nil + return channelWithTeamDataSliceToModel(channels), totalCount, nil } +// TODO: rewrite in squrrel func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) (model.ChannelList, error) { return s.performSearch(` SELECT @@ -3260,6 +3430,7 @@ func (s SqlChannelStore) getSearchGroupChannelsQuery(userId, term string, isPost return query, args } +// TODO: rewrite in squirrel func (s SqlChannelStore) SearchGroupChannels(userId, term string) (model.ChannelList, error) { isPostgreSQL := s.DriverName() == model.DatabaseDriverPostgres queryString, args := s.getSearchGroupChannelsQuery(userId, term, isPostgreSQL) @@ -3271,6 +3442,7 @@ func (s SqlChannelStore) SearchGroupChannels(userId, term string) (model.Channel return groupChannels, nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) (model.ChannelMembers, error) { var dbMembers channelMemberWithSchemeRolesList @@ -3284,6 +3456,7 @@ func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) (mo return dbMembers.ToModel(), nil } +// TODO: rewrite in squirrel func (s SqlChannelStore) GetMembersByChannelIds(channelIds []string, userId string) (model.ChannelMembers, error) { var dbMembers channelMemberWithSchemeRolesList @@ -3298,8 +3471,8 @@ func (s SqlChannelStore) GetMembersByChannelIds(channelIds []string, userId stri } func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) (model.ChannelList, error) { - var channels model.ChannelList - _, err := s.GetReplica().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit}) + channels := model.ChannelList{} + err := s.GetReplicaX().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = ? ORDER BY DisplayName LIMIT ? OFFSET ?", schemeId, limit, offset) if err != nil { return nil, errors.Wrapf(err, "failed to find Channels with schemeId=%s", schemeId) } @@ -3311,16 +3484,16 @@ func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit // causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function // *after* the new Schemes functionality has been used on an installation will have unintended consequences. func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId string) (map[string]string, error) { - var transaction *gorp.Transaction + var transaction *sqlxTxWrapper var err error - if transaction, err = s.GetMaster().Begin(); err != nil { + if transaction, err = s.GetMasterX().Beginx(); err != nil { return nil, errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) - var channelMembers []channelMember - if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:FromChannelId, :FromUserId) ORDER BY ChannelId, UserId LIMIT 100", map[string]interface{}{"FromChannelId": fromChannelId, "FromUserId": fromUserId}); err != nil { + channelMembers := []channelMember{} + if err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (?, ?) ORDER BY ChannelId, UserId LIMIT 100", fromChannelId, fromUserId); err != nil { return nil, errors.Wrap(err, "failed to find ChannelMembers") } @@ -3355,7 +3528,19 @@ func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId } member.Roles = strings.Join(newRoles, " ") - if _, err := transaction.Update(&member); err != nil { + if _, err := transaction.NamedExec(`UPDATE ChannelMembers + SET Roles=:Roles, + LastViewedAt=:LastViewedAt, + MsgCount=:MsgCount, + MentionCount=:MentionCount, + NotifyProps=:NotifyProps, + LastUpdateAt=:LastUpdateAt, + SchemeUser=:SchemeUser, + SchemeAdmin=:SchemeAdmin, + SchemeGuest=:SchemeGuest, + MentionCountRoot=:MentionCountRoot, + MsgCountRoot=:MsgCountRoot + WHERE ChannelId=:ChannelId AND UserId=:UserId`, &member); err != nil { return nil, errors.Wrap(err, "failed to update ChannelMember") } @@ -3372,11 +3557,11 @@ func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId } func (s SqlChannelStore) ResetAllChannelSchemes() error { - transaction, err := s.GetMaster().Begin() + transaction, err := s.GetMasterX().Beginx() if err != nil { return errors.Wrap(err, "begin_transaction") } - defer finalizeTransaction(transaction) + defer finalizeTransactionX(transaction) err = s.resetAllChannelSchemesT(transaction) if err != nil { @@ -3390,7 +3575,7 @@ func (s SqlChannelStore) ResetAllChannelSchemes() error { return nil } -func (s SqlChannelStore) resetAllChannelSchemesT(transaction *gorp.Transaction) error { +func (s SqlChannelStore) resetAllChannelSchemesT(transaction *sqlxTxWrapper) error { if _, err := transaction.Exec("UPDATE Channels SET SchemeId=''"); err != nil { return errors.Wrap(err, "failed to update Channels") } @@ -3404,21 +3589,21 @@ func (s SqlChannelStore) ClearAllCustomRoleAssignments() error { lastChannelId := strings.Repeat("0", 26) for { - var transaction *gorp.Transaction + var transaction *sqlxTxWrapper var err error - if transaction, err = s.GetMaster().Begin(); err != nil { + if transaction, err = s.GetMasterX().Beginx(); err != nil { return errors.Wrap(err, "begin_transaction") } - var channelMembers []*channelMember - if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:ChannelId, :UserId) ORDER BY ChannelId, UserId LIMIT 1000", map[string]interface{}{"ChannelId": lastChannelId, "UserId": lastUserId}); err != nil { - finalizeTransaction(transaction) + channelMembers := []*channelMember{} + if err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (?, ?) ORDER BY ChannelId, UserId LIMIT 1000", lastChannelId, lastUserId); err != nil { + finalizeTransactionX(transaction) return errors.Wrap(err, "failed to find ChannelMembers") } if len(channelMembers) == 0 { - finalizeTransaction(transaction) + finalizeTransactionX(transaction) break } @@ -3439,15 +3624,15 @@ func (s SqlChannelStore) ClearAllCustomRoleAssignments() error { newRolesString := strings.Join(newRoles, " ") if newRolesString != member.Roles { - if _, err := transaction.Exec("UPDATE ChannelMembers SET Roles = :Roles WHERE UserId = :UserId AND ChannelId = :ChannelId", map[string]interface{}{"Roles": newRolesString, "ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil { - finalizeTransaction(transaction) + if _, err := transaction.Exec("UPDATE ChannelMembers SET Roles = ? WHERE UserId = ? AND ChannelId = ?", newRolesString, member.UserId, member.ChannelId); err != nil { + finalizeTransactionX(transaction) return errors.Wrap(err, "failed to update ChannelMembers") } } } if err := transaction.Commit(); err != nil { - finalizeTransaction(transaction) + finalizeTransactionX(transaction) return errors.Wrap(err, "commit_transaction") } } @@ -3456,8 +3641,8 @@ func (s SqlChannelStore) ClearAllCustomRoleAssignments() error { } func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) ([]*model.ChannelForExport, error) { - var channels []*model.ChannelForExport - if _, err := s.GetReplica().Select(&channels, ` + channels := []*model.ChannelForExport{} + if err := s.GetReplicaX().Select(&channels, ` SELECT Channels.*, Teams.Name as TeamName, @@ -3468,12 +3653,12 @@ func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) LEFT JOIN Schemes ON Channels.SchemeId = Schemes.Id WHERE - Channels.Id > :AfterId + Channels.Id > ? AND Channels.Type IN ('O', 'P') ORDER BY Id - LIMIT :Limit`, - map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil { + LIMIT ?`, + afterId, limit); err != nil { return nil, errors.Wrap(err, "failed to find Channels for export") } @@ -3481,8 +3666,8 @@ func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) } func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string) ([]*model.ChannelMemberForExport, error) { - var members []*model.ChannelMemberForExport - _, err := s.GetReplica().Select(&members, ` + members := []*model.ChannelMemberForExport{} + err := s.GetReplicaX().Select(&members, ` SELECT ChannelMembers.ChannelId, ChannelMembers.UserId, @@ -3502,11 +3687,10 @@ func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string INNER JOIN Channels ON ChannelMembers.ChannelId = Channels.Id WHERE - ChannelMembers.UserId = :UserId - AND Channels.TeamId = :TeamId + ChannelMembers.UserId = ? + AND Channels.TeamId = ? AND Channels.DeleteAt = 0`, - map[string]interface{}{"TeamId": teamId, "UserId": userId}) - + userId, teamId) if err != nil { return nil, errors.Wrap(err, "failed to find Channels for export") } @@ -3515,7 +3699,7 @@ func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string } func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId string) ([]*model.DirectChannelForExport, error) { - var directChannelsForExport []*model.DirectChannelForExport + directChannelsForExport := []*model.DirectChannelForExport{} query := s.getQueryBuilder(). Select("Channels.*"). From("Channels"). @@ -3532,8 +3716,8 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s return nil, errors.Wrap(err, "channel_tosql") } - if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil { - return nil, errors.Wrap(err, "failed to find direct Channels for export") + if err2 := s.GetReplicaX().Select(&directChannelsForExport, queryString, args...); err2 != nil { + return nil, errors.Wrap(err2, "failed to find direct Channels for export") } var channelIds []string @@ -3554,9 +3738,9 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s return nil, errors.Wrap(err, "channel_tosql") } - var channelMembers []*model.ChannelMemberForExport - if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil { - return nil, errors.Wrap(err, "failed to find ChannelMembers") + channelMembers := []*model.ChannelMemberForExport{} + if err2 := s.GetReplicaX().Select(&channelMembers, queryString, args...); err2 != nil { + return nil, errors.Wrap(err2, "failed to find ChannelMembers") } // Populate each channel with its members @@ -3580,16 +3764,16 @@ func (s SqlChannelStore) GetChannelsBatchForIndexing(startTime, endTime int64, l FROM Channels WHERE - CreateAt >= :StartTime + CreateAt >= ? AND - CreateAt < :EndTime + CreateAt < ? ORDER BY CreateAt LIMIT - :NumChannels` + ?` - var channels []*model.Channel - _, err := s.GetSearchReplica().Select(&channels, query, map[string]interface{}{"StartTime": startTime, "EndTime": endTime, "NumChannels": limit}) + channels := []*model.Channel{} + err := s.GetSearchReplicaX().Select(&channels, query, startTime, endTime, limit) if err != nil { return nil, errors.Wrap(err, "failed to find Channels") } @@ -3610,13 +3794,15 @@ func (s SqlChannelStore) UserBelongsToChannels(userId string, channelIds []strin if err != nil { return false, errors.Wrap(err, "channel_tosql") } - c, err := s.GetReplica().SelectInt(queryString, args...) + var c int64 + err = s.GetReplicaX().Get(&c, queryString, args...) if err != nil { return false, errors.Wrap(err, "failed to count ChannelMembers") } return c > 0, nil } +// TODO: parameterize userIDs func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) error { sql := fmt.Sprintf(` UPDATE @@ -3628,11 +3814,11 @@ func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) e FALSE END WHERE - ChannelId = :ChannelId + ChannelId = ? AND (SchemeGuest = false OR SchemeGuest IS NULL) `, strings.Join(userIDs, "', '")) - if _, err := s.GetMaster().Exec(sql, map[string]interface{}{"ChannelId": channelID}); err != nil { + if _, err := s.GetMasterX().Exec(sql, channelID); err != nil { return errors.Wrap(err, "failed to update ChannelMembers") } @@ -3640,14 +3826,18 @@ func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) e } func (s SqlChannelStore) GroupSyncedChannelCount() (int64, error) { - query := s.getQueryBuilder().Select("COUNT(*)").From("Channels").Where(sq.Eq{"GroupConstrained": true, "DeleteAt": 0}) + query := s.getQueryBuilder(). + Select("COUNT(*)"). + From("Channels"). + Where(sq.Eq{"GroupConstrained": true, "DeleteAt": 0}) sql, args, err := query.ToSql() if err != nil { return 0, errors.Wrap(err, "channel_tosql") } - count, err := s.GetReplica().SelectInt(sql, args...) + var count int64 + err = s.GetReplicaX().Get(&count, sql, args...) if err != nil { return 0, errors.Wrap(err, "failed to count Channels") } @@ -3666,7 +3856,7 @@ func (s SqlChannelStore) SetShared(channelId string, shared bool) error { return errors.Wrap(err, "channel_set_shared_tosql") } - result, err := s.GetMaster().Exec(squery, args...) + result, err := s.GetMasterX().Exec(squery, args...) if err != nil { return errors.Wrap(err, "failed to update `Shared` for Channels") } @@ -3695,7 +3885,7 @@ func (s SqlChannelStore) GetTeamForChannel(channelID string) (*model.Team, error } team := model.Team{} - err = s.GetReplica().SelectOne(&team, query, args...) + err = s.GetReplicaX().Get(&team, query, args...) if err != nil { if err == sql.ErrNoRows { return nil, store.NewErrNotFound("Team", fmt.Sprintf("channel_id=%s", channelID)) diff --git a/store/storetest/channel_store.go b/store/storetest/channel_store.go index 366755c2a6..86fb705313 100644 --- a/store/storetest/channel_store.go +++ b/store/storetest/channel_store.go @@ -453,6 +453,10 @@ func testChannelStoreGet(t *testing.T, ss store.Store, s SqlStore) { require.NoError(t, err, err) require.Greater(t, len(channelsTeam), 0, "too little") + _, err = ss.Channel().GetTeamChannels("notfound") + var nfErr *store.ErrNotFound + require.True(t, errors.As(err, &nfErr)) + // Manually truncate Channels table until testlib can handle cleanups s.GetMaster().Exec("TRUNCATE Channels") }