github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/sqlstore/channel_member_history_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "database/sql" 8 "fmt" 9 10 sq "github.com/Masterminds/squirrel" 11 "github.com/pkg/errors" 12 13 "github.com/mattermost/mattermost-server/v5/mlog" 14 "github.com/mattermost/mattermost-server/v5/model" 15 "github.com/mattermost/mattermost-server/v5/store" 16 ) 17 18 type SqlChannelMemberHistoryStore struct { 19 *SqlStore 20 } 21 22 func newSqlChannelMemberHistoryStore(sqlStore *SqlStore) store.ChannelMemberHistoryStore { 23 s := &SqlChannelMemberHistoryStore{ 24 SqlStore: sqlStore, 25 } 26 27 for _, db := range sqlStore.GetAllConns() { 28 table := db.AddTableWithName(model.ChannelMemberHistory{}, "ChannelMemberHistory").SetKeys(false, "ChannelId", "UserId", "JoinTime") 29 table.ColMap("ChannelId").SetMaxSize(26) 30 table.ColMap("UserId").SetMaxSize(26) 31 table.ColMap("JoinTime").SetNotNull(true) 32 } 33 34 return s 35 } 36 37 func (s SqlChannelMemberHistoryStore) LogJoinEvent(userId string, channelId string, joinTime int64) error { 38 channelMemberHistory := &model.ChannelMemberHistory{ 39 UserId: userId, 40 ChannelId: channelId, 41 JoinTime: joinTime, 42 } 43 44 if err := s.GetMaster().Insert(channelMemberHistory); err != nil { 45 return errors.Wrapf(err, "LogJoinEvent userId=%s channelId=%s joinTime=%d", userId, channelId, joinTime) 46 } 47 return nil 48 } 49 50 func (s SqlChannelMemberHistoryStore) LogLeaveEvent(userId string, channelId string, leaveTime int64) error { 51 query, params, err := s.getQueryBuilder(). 52 Update("ChannelMemberHistory"). 53 Set("LeaveTime", leaveTime). 54 Where(sq.And{ 55 sq.Eq{"UserId": userId}, 56 sq.Eq{"ChannelId": channelId}, 57 sq.Eq{"LeaveTime": nil}, 58 }).ToSql() 59 if err != nil { 60 return errors.Wrap(err, "channel_member_history_to_sql") 61 } 62 sqlResult, err := s.GetMaster().Exec(query, params...) 63 if err != nil { 64 return errors.Wrapf(err, "LogLeaveEvent userId=%s channelId=%s leaveTime=%d", userId, channelId, leaveTime) 65 } 66 67 if rows, err := sqlResult.RowsAffected(); err == nil && rows != 1 { 68 // there was no join event to update - this is best effort, so no need to raise an error 69 mlog.Warn("Channel join event for user and channel not found", mlog.String("user", userId), mlog.String("channel", channelId)) 70 } 71 return nil 72 } 73 74 func (s SqlChannelMemberHistoryStore) GetUsersInChannelDuring(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistoryResult, error) { 75 useChannelMemberHistory, err := s.hasDataAtOrBefore(startTime) 76 if err != nil { 77 return nil, errors.Wrapf(err, "hasDataAtOrBefore startTime=%d endTime=%d channelId=%s", startTime, endTime, channelId) 78 } 79 80 if useChannelMemberHistory { 81 // the export period starts after the ChannelMemberHistory table was first introduced, so we can use the 82 // data from it for our export 83 channelMemberHistories, err2 := s.getFromChannelMemberHistoryTable(startTime, endTime, channelId) 84 if err2 != nil { 85 return nil, errors.Wrapf(err2, "getFromChannelMemberHistoryTable startTime=%d endTime=%d channelId=%s", startTime, endTime, channelId) 86 } 87 return channelMemberHistories, nil 88 } 89 // the export period starts before the ChannelMemberHistory table was introduced, so we need to fake the 90 // data by assuming that anybody who has ever joined the channel in question was present during the export period. 91 // this may not always be true, but it's better than saying that somebody wasn't there when they were 92 channelMemberHistories, err := s.getFromChannelMembersTable(startTime, endTime, channelId) 93 if err != nil { 94 return nil, errors.Wrapf(err, "getFromChannelMembersTable startTime=%d endTime=%d channelId=%s", startTime, endTime, channelId) 95 } 96 return channelMemberHistories, nil 97 } 98 99 func (s SqlChannelMemberHistoryStore) hasDataAtOrBefore(time int64) (bool, error) { 100 type NullableCountResult struct { 101 Min sql.NullInt64 102 } 103 query, _, err := s.getQueryBuilder().Select("MIN(JoinTime) as Min").From("ChannelMemberHistory").ToSql() 104 if err != nil { 105 return false, errors.Wrap(err, "channel_member_history_to_sql") 106 } 107 var result NullableCountResult 108 if err := s.GetReplica().SelectOne(&result, query); err != nil { 109 return false, err 110 } else if result.Min.Valid { 111 return result.Min.Int64 <= time, nil 112 } else { 113 // if the result was null, there are no rows in the table, so there is no data from before 114 return false, nil 115 } 116 } 117 118 func (s SqlChannelMemberHistoryStore) getFromChannelMemberHistoryTable(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistoryResult, error) { 119 query, args, err := s.getQueryBuilder(). 120 Select("cmh.*, u.Email, u.Username, Bots.UserId IS NOT NULL AS IsBot, u.DeleteAt AS UserDeleteAt"). 121 From("ChannelMemberHistory cmh"). 122 Join("Users u ON cmh.UserId = u.Id"). 123 LeftJoin("Bots ON Bots.UserId = u.Id"). 124 Where(sq.And{ 125 sq.Eq{"cmh.ChannelId": channelId}, 126 sq.LtOrEq{"cmh.JoinTime": endTime}, 127 sq.Or{ 128 sq.Eq{"cmh.LeaveTime": nil}, 129 sq.GtOrEq{"cmh.LeaveTime": startTime}, 130 }, 131 }). 132 OrderBy("cmh.JoinTime ASC").ToSql() 133 if err != nil { 134 return nil, errors.Wrap(err, "channel_member_history_to_sql") 135 } 136 var histories []*model.ChannelMemberHistoryResult 137 if _, err := s.GetReplica().Select(&histories, query, args...); err != nil { 138 return nil, err 139 } 140 141 return histories, nil 142 } 143 144 func (s SqlChannelMemberHistoryStore) getFromChannelMembersTable(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistoryResult, error) { 145 query, args, err := s.getQueryBuilder(). 146 Select("ch.ChannelId, ch.UserId, u.Email, u.Username, Bots.UserId IS NOT NULL AS IsBot, u.DeleteAt AS UserDeleteAt"). 147 Distinct(). 148 From("ChannelMembers ch"). 149 Join("Users u ON ch.UserId = u.id"). 150 LeftJoin("Bots ON Bots.UserId = u.id"). 151 Where(sq.Eq{"ch.ChannelId": channelId}).ToSql() 152 if err != nil { 153 return nil, errors.Wrap(err, "channel_member_history_to_sql") 154 } 155 156 var histories []*model.ChannelMemberHistoryResult 157 if _, err := s.GetReplica().Select(&histories, query, args...); err != nil { 158 return nil, err 159 } 160 // we have to fill in the join/leave times, because that data doesn't exist in the channel members table 161 for _, channelMemberHistory := range histories { 162 channelMemberHistory.JoinTime = startTime 163 channelMemberHistory.LeaveTime = model.NewInt64(endTime) 164 } 165 return histories, nil 166 } 167 168 func (s SqlChannelMemberHistoryStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, error) { 169 var ( 170 query string 171 args []interface{} 172 err error 173 ) 174 175 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 176 var innerSelect string 177 innerSelect, args, err = s.getQueryBuilder(). 178 Select("ctid"). 179 From("ChannelMemberHistory"). 180 Where(sq.And{ 181 sq.NotEq{"LeaveTime": nil}, 182 sq.LtOrEq{"LeaveTime": endTime}, 183 }).Limit(uint64(limit)). 184 ToSql() 185 if err != nil { 186 return 0, errors.Wrap(err, "channel_member_history_to_sql") 187 } 188 query, _, err = s.getQueryBuilder(). 189 Delete("ChannelMemberHistory"). 190 Where(fmt.Sprintf( 191 "ctid IN (%s)", innerSelect, 192 )).ToSql() 193 } else { 194 query, args, err = s.getQueryBuilder(). 195 Delete("ChannelMemberHistory"). 196 Where(sq.And{ 197 sq.NotEq{"LeaveTime": nil}, 198 sq.LtOrEq{"LeaveTime": endTime}, 199 }). 200 Limit(uint64(limit)).ToSql() 201 } 202 if err != nil { 203 return 0, errors.Wrap(err, "channel_member_history_to_sql") 204 } 205 sqlResult, err := s.GetMaster().Exec(query, args...) 206 if err != nil { 207 return 0, errors.Wrapf(err, "PermanentDeleteBatch endTime=%d limit=%d", endTime, limit) 208 } 209 210 rowsAffected, err := sqlResult.RowsAffected() 211 if err != nil { 212 return 0, errors.Wrapf(err, "PermanentDeleteBatch endTime=%d limit=%d", endTime, limit) 213 } 214 return rowsAffected, nil 215 }