github.com/keys-pub/mattermost-server@v4.10.10+incompatible/store/sqlstore/channel_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 "net/http" 10 "sort" 11 "strconv" 12 "strings" 13 14 "github.com/mattermost/gorp" 15 "github.com/mattermost/mattermost-server/einterfaces" 16 "github.com/mattermost/mattermost-server/mlog" 17 "github.com/mattermost/mattermost-server/model" 18 "github.com/mattermost/mattermost-server/store" 19 "github.com/mattermost/mattermost-server/utils" 20 ) 21 22 const ( 23 ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE = model.SESSION_CACHE_SIZE 24 ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SEC = 900 // 15 mins 25 26 ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE = model.SESSION_CACHE_SIZE 27 ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SEC = 1800 // 30 mins 28 29 CHANNEL_MEMBERS_COUNTS_CACHE_SIZE = model.CHANNEL_CACHE_SIZE 30 CHANNEL_MEMBERS_COUNTS_CACHE_SEC = 1800 // 30 mins 31 32 CHANNEL_CACHE_SEC = 900 // 15 mins 33 ) 34 35 type SqlChannelStore struct { 36 SqlStore 37 metrics einterfaces.MetricsInterface 38 } 39 40 var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE) 41 var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE) 42 var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE) 43 var channelCache = utils.NewLru(model.CHANNEL_CACHE_SIZE) 44 var channelByNameCache = utils.NewLru(model.CHANNEL_CACHE_SIZE) 45 46 func (s SqlChannelStore) ClearCaches() { 47 channelMemberCountsCache.Purge() 48 allChannelMembersForUserCache.Purge() 49 allChannelMembersNotifyPropsForChannelCache.Purge() 50 channelCache.Purge() 51 channelByNameCache.Purge() 52 53 if s.metrics != nil { 54 s.metrics.IncrementMemCacheInvalidationCounter("Channel Member Counts - Purge") 55 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Purge") 56 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Purge") 57 s.metrics.IncrementMemCacheInvalidationCounter("Channel - Purge") 58 s.metrics.IncrementMemCacheInvalidationCounter("Channel By Name - Purge") 59 } 60 } 61 62 func NewSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.ChannelStore { 63 s := &SqlChannelStore{ 64 SqlStore: sqlStore, 65 metrics: metrics, 66 } 67 68 for _, db := range sqlStore.GetAllConns() { 69 table := db.AddTableWithName(model.Channel{}, "Channels").SetKeys(false, "Id") 70 table.ColMap("Id").SetMaxSize(26) 71 table.ColMap("TeamId").SetMaxSize(26) 72 table.ColMap("Type").SetMaxSize(1) 73 table.ColMap("DisplayName").SetMaxSize(64) 74 table.ColMap("Name").SetMaxSize(64) 75 table.SetUniqueTogether("Name", "TeamId") 76 table.ColMap("Header").SetMaxSize(1024) 77 table.ColMap("Purpose").SetMaxSize(250) 78 table.ColMap("CreatorId").SetMaxSize(26) 79 80 tablem := db.AddTableWithName(model.ChannelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId") 81 tablem.ColMap("ChannelId").SetMaxSize(26) 82 tablem.ColMap("UserId").SetMaxSize(26) 83 tablem.ColMap("Roles").SetMaxSize(64) 84 tablem.ColMap("NotifyProps").SetMaxSize(2000) 85 } 86 87 return s 88 } 89 90 func (s SqlChannelStore) CreateIndexesIfNotExists() { 91 s.CreateIndexIfNotExists("idx_channels_team_id", "Channels", "TeamId") 92 s.CreateIndexIfNotExists("idx_channels_name", "Channels", "Name") 93 s.CreateIndexIfNotExists("idx_channels_update_at", "Channels", "UpdateAt") 94 s.CreateIndexIfNotExists("idx_channels_create_at", "Channels", "CreateAt") 95 s.CreateIndexIfNotExists("idx_channels_delete_at", "Channels", "DeleteAt") 96 97 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 98 s.CreateIndexIfNotExists("idx_channels_name_lower", "Channels", "lower(Name)") 99 s.CreateIndexIfNotExists("idx_channels_displayname_lower", "Channels", "lower(DisplayName)") 100 } 101 102 s.CreateIndexIfNotExists("idx_channelmembers_channel_id", "ChannelMembers", "ChannelId") 103 s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId") 104 105 s.CreateFullTextIndexIfNotExists("idx_channels_txt", "Channels", "Name, DisplayName") 106 } 107 108 func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel { 109 return store.Do(func(result *store.StoreResult) { 110 if channel.Type == model.CHANNEL_DIRECT { 111 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.direct_channel.app_error", nil, "", http.StatusBadRequest) 112 } else { 113 if transaction, err := s.GetMaster().Begin(); err != nil { 114 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 115 } else { 116 *result = s.saveChannelT(transaction, channel, maxChannelsPerTeam) 117 if result.Err != nil { 118 transaction.Rollback() 119 } else { 120 if err := transaction.Commit(); err != nil { 121 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 122 } 123 } 124 } 125 } 126 }) 127 } 128 129 func (s SqlChannelStore) CreateDirectChannel(userId string, otherUserId string) store.StoreChannel { 130 channel := new(model.Channel) 131 132 channel.DisplayName = "" 133 channel.Name = model.GetDMNameFromIds(otherUserId, userId) 134 135 channel.Header = "" 136 channel.Type = model.CHANNEL_DIRECT 137 138 cm1 := &model.ChannelMember{ 139 UserId: userId, 140 NotifyProps: model.GetDefaultChannelNotifyProps(), 141 Roles: model.CHANNEL_USER_ROLE_ID, 142 } 143 cm2 := &model.ChannelMember{ 144 UserId: otherUserId, 145 NotifyProps: model.GetDefaultChannelNotifyProps(), 146 Roles: model.CHANNEL_USER_ROLE_ID, 147 } 148 149 return s.SaveDirectChannel(channel, cm1, cm2) 150 } 151 152 func (s SqlChannelStore) SaveDirectChannel(directchannel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) store.StoreChannel { 153 return store.Do(func(result *store.StoreResult) { 154 if directchannel.Type != model.CHANNEL_DIRECT { 155 result.Err = model.NewAppError("SqlChannelStore.SaveDirectChannel", "store.sql_channel.save_direct_channel.not_direct.app_error", nil, "", http.StatusBadRequest) 156 } else { 157 if transaction, err := s.GetMaster().Begin(); err != nil { 158 result.Err = model.NewAppError("SqlChannelStore.SaveDirectChannel", "store.sql_channel.save_direct_channel.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 159 } else { 160 directchannel.TeamId = "" 161 channelResult := s.saveChannelT(transaction, directchannel, 0) 162 163 if channelResult.Err != nil { 164 transaction.Rollback() 165 result.Err = channelResult.Err 166 result.Data = channelResult.Data 167 } else { 168 newChannel := channelResult.Data.(*model.Channel) 169 // Members need new channel ID 170 member1.ChannelId = newChannel.Id 171 member2.ChannelId = newChannel.Id 172 173 member1Result := s.saveMemberT(transaction, member1, newChannel) 174 member2Result := member1Result 175 if member1.UserId != member2.UserId { 176 member2Result = s.saveMemberT(transaction, member2, newChannel) 177 } 178 179 if member1Result.Err != nil || member2Result.Err != nil { 180 transaction.Rollback() 181 details := "" 182 if member1Result.Err != nil { 183 details += "Member1Err: " + member1Result.Err.Message 184 } 185 if member2Result.Err != nil { 186 details += "Member2Err: " + member2Result.Err.Message 187 } 188 result.Err = model.NewAppError("SqlChannelStore.SaveDirectChannel", "store.sql_channel.save_direct_channel.add_members.app_error", nil, details, http.StatusInternalServerError) 189 } else { 190 if err := transaction.Commit(); err != nil { 191 result.Err = model.NewAppError("SqlChannelStore.SaveDirectChannel", "store.sql_channel.save_direct_channel.commit.app_error", nil, err.Error(), http.StatusInternalServerError) 192 } else { 193 *result = channelResult 194 } 195 } 196 } 197 } 198 } 199 }) 200 } 201 202 func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *model.Channel, maxChannelsPerTeam int64) store.StoreResult { 203 result := store.StoreResult{} 204 205 if len(channel.Id) > 0 { 206 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.existing.app_error", nil, "id="+channel.Id, http.StatusBadRequest) 207 return result 208 } 209 210 channel.PreSave() 211 if result.Err = channel.IsValid(); result.Err != nil { 212 return result 213 } 214 215 if channel.Type != model.CHANNEL_DIRECT && channel.Type != model.CHANNEL_GROUP && maxChannelsPerTeam >= 0 { 216 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 { 217 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.current_count.app_error", nil, "teamId="+channel.TeamId+", "+err.Error(), http.StatusInternalServerError) 218 return result 219 } else if count >= maxChannelsPerTeam { 220 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.limit.app_error", nil, "teamId="+channel.TeamId, http.StatusBadRequest) 221 return result 222 } 223 } 224 225 if err := transaction.Insert(channel); err != nil { 226 if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { 227 dupChannel := model.Channel{} 228 s.GetMaster().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name = :Name", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) 229 if dupChannel.DeleteAt > 0 { 230 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.previously.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusBadRequest) 231 } else { 232 result.Err = model.NewAppError("SqlChannelStore.Save", store.CHANNEL_EXISTS_ERROR, nil, "id="+channel.Id+", "+err.Error(), http.StatusBadRequest) 233 result.Data = &dupChannel 234 } 235 } else { 236 result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save_channel.save.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusInternalServerError) 237 } 238 } else { 239 result.Data = channel 240 } 241 242 return result 243 } 244 245 func (s SqlChannelStore) Update(channel *model.Channel) store.StoreChannel { 246 return store.Do(func(result *store.StoreResult) { 247 channel.PreUpdate() 248 249 if result.Err = channel.IsValid(); result.Err != nil { 250 return 251 } 252 253 if count, err := s.GetMaster().Update(channel); err != nil { 254 if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { 255 dupChannel := model.Channel{} 256 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}) 257 if dupChannel.DeleteAt > 0 { 258 result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.previously.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusBadRequest) 259 } else { 260 result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.exists.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusBadRequest) 261 } 262 } else { 263 result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.updating.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusInternalServerError) 264 } 265 } else if count != 1 { 266 result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.app_error", nil, "id="+channel.Id, http.StatusInternalServerError) 267 } else { 268 result.Data = channel 269 } 270 }) 271 } 272 273 func (s SqlChannelStore) extraUpdated(channel *model.Channel) store.StoreChannel { 274 return store.Do(func(result *store.StoreResult) { 275 channel.ExtraUpdated() 276 277 _, err := s.GetMaster().Exec( 278 `UPDATE 279 Channels 280 SET 281 ExtraUpdateAt = :Time 282 WHERE 283 Id = :Id`, 284 map[string]interface{}{"Id": channel.Id, "Time": channel.ExtraUpdateAt}) 285 286 if err != nil { 287 result.Err = model.NewAppError("SqlChannelStore.extraUpdated", "store.sql_channel.extra_updated.app_error", nil, "id="+channel.Id+", "+err.Error(), http.StatusInternalServerError) 288 } 289 }) 290 } 291 292 func (s SqlChannelStore) GetChannelUnread(channelId, userId string) store.StoreChannel { 293 return store.Do(func(result *store.StoreResult) { 294 var unreadChannel model.ChannelUnread 295 err := s.GetReplica().SelectOne(&unreadChannel, 296 `SELECT 297 Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, ChannelMembers.MentionCount MentionCount, ChannelMembers.NotifyProps NotifyProps 298 FROM 299 Channels, ChannelMembers 300 WHERE 301 Id = ChannelId 302 AND Id = :ChannelId 303 AND UserId = :UserId 304 AND DeleteAt = 0`, 305 map[string]interface{}{"ChannelId": channelId, "UserId": userId}) 306 307 if err != nil { 308 result.Err = model.NewAppError("SqlChannelStore.GetChannelUnread", "store.sql_channel.get_unread.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError) 309 if err == sql.ErrNoRows { 310 result.Err.StatusCode = http.StatusNotFound 311 } 312 } else { 313 result.Data = &unreadChannel 314 } 315 }) 316 } 317 318 func (s SqlChannelStore) InvalidateChannel(id string) { 319 channelCache.Remove(id) 320 if s.metrics != nil { 321 s.metrics.IncrementMemCacheInvalidationCounter("Channel - Remove by ChannelId") 322 } 323 } 324 325 func (s SqlChannelStore) InvalidateChannelByName(teamId, name string) { 326 channelByNameCache.Remove(teamId + name) 327 if s.metrics != nil { 328 s.metrics.IncrementMemCacheInvalidationCounter("Channel by Name - Remove by TeamId and Name") 329 } 330 } 331 332 func (s SqlChannelStore) Get(id string, allowFromCache bool) store.StoreChannel { 333 return s.get(id, false, allowFromCache) 334 } 335 336 func (s SqlChannelStore) GetPinnedPosts(channelId string) store.StoreChannel { 337 return store.Do(func(result *store.StoreResult) { 338 pl := model.NewPostList() 339 340 var posts []*model.Post 341 if _, err := s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE IsPinned = true AND ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt ASC", map[string]interface{}{"ChannelId": channelId}); err != nil { 342 result.Err = model.NewAppError("SqlPostStore.GetPinnedPosts", "store.sql_channel.pinned_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 343 } else { 344 for _, post := range posts { 345 pl.AddPost(post) 346 pl.AddOrder(post.Id) 347 } 348 } 349 350 result.Data = pl 351 }) 352 } 353 354 func (s SqlChannelStore) GetFromMaster(id string) store.StoreChannel { 355 return s.get(id, true, false) 356 } 357 358 func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) store.StoreChannel { 359 return store.Do(func(result *store.StoreResult) { 360 var db *gorp.DbMap 361 if master { 362 db = s.GetMaster() 363 } else { 364 db = s.GetReplica() 365 } 366 367 if allowFromCache { 368 if cacheItem, ok := channelCache.Get(id); ok { 369 if s.metrics != nil { 370 s.metrics.IncrementMemCacheHitCounter("Channel") 371 } 372 result.Data = (cacheItem.(*model.Channel)).DeepCopy() 373 return 374 } else { 375 if s.metrics != nil { 376 s.metrics.IncrementMemCacheMissCounter("Channel") 377 } 378 } 379 } else { 380 if s.metrics != nil { 381 s.metrics.IncrementMemCacheMissCounter("Channel") 382 } 383 } 384 385 if obj, err := db.Get(model.Channel{}, id); err != nil { 386 result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.find.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) 387 } else if obj == nil { 388 result.Err = model.NewAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id, http.StatusNotFound) 389 } else { 390 result.Data = obj.(*model.Channel) 391 channelCache.AddWithExpiresInSecs(id, obj.(*model.Channel), CHANNEL_CACHE_SEC) 392 } 393 }) 394 } 395 396 func (s SqlChannelStore) Delete(channelId string, time int64) store.StoreChannel { 397 return s.SetDeleteAt(channelId, time, time) 398 } 399 400 func (s SqlChannelStore) Restore(channelId string, time int64) store.StoreChannel { 401 return s.SetDeleteAt(channelId, 0, time) 402 } 403 404 func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt int64, updateAt int64) store.StoreChannel { 405 return store.Do(func(result *store.StoreResult) { 406 _, err := s.GetMaster().Exec("Update Channels SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :ChannelId", map[string]interface{}{"DeleteAt": deleteAt, "UpdateAt": updateAt, "ChannelId": channelId}) 407 if err != nil { 408 result.Err = model.NewAppError("SqlChannelStore.Delete", "store.sql_channel.delete.channel.app_error", nil, "id="+channelId+", err="+err.Error(), http.StatusInternalServerError) 409 } 410 }) 411 } 412 413 func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) store.StoreChannel { 414 return store.Do(func(result *store.StoreResult) { 415 if _, err := s.GetMaster().Exec("DELETE FROM Channels WHERE TeamId = :TeamId", map[string]interface{}{"TeamId": teamId}); err != nil { 416 result.Err = model.NewAppError("SqlChannelStore.PermanentDeleteByTeam", "store.sql_channel.permanent_delete_by_team.app_error", nil, "teamId="+teamId+", "+err.Error(), http.StatusInternalServerError) 417 } 418 }) 419 } 420 421 func (s SqlChannelStore) PermanentDelete(channelId string) store.StoreChannel { 422 return store.Do(func(result *store.StoreResult) { 423 if _, err := s.GetMaster().Exec("DELETE FROM Channels WHERE Id = :ChannelId", map[string]interface{}{"ChannelId": channelId}); err != nil { 424 result.Err = model.NewAppError("SqlChannelStore.PermanentDelete", "store.sql_channel.permanent_delete.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 425 } 426 }) 427 } 428 429 func (s SqlChannelStore) PermanentDeleteMembersByChannel(channelId string) store.StoreChannel { 430 return store.Do(func(result *store.StoreResult) { 431 _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId}) 432 if err != nil { 433 result.Err = model.NewAppError("SqlChannelStore.RemoveAllMembersByChannel", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 434 } 435 }) 436 } 437 438 func (s SqlChannelStore) GetChannels(teamId string, userId string) store.StoreChannel { 439 return store.Do(func(result *store.StoreResult) { 440 data := &model.ChannelList{} 441 _, err := s.GetReplica().Select(data, "SELECT Channels.* FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND DeleteAt = 0 AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) 442 443 if err != nil { 444 result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 445 } else { 446 if len(*data) == 0 { 447 result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId+", userId="+userId, http.StatusBadRequest) 448 } else { 449 result.Data = data 450 } 451 } 452 }) 453 } 454 455 func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel { 456 return store.Do(func(result *store.StoreResult) { 457 data := &model.ChannelList{} 458 _, err := s.GetReplica().Select(data, 459 `SELECT 460 * 461 FROM 462 Channels 463 WHERE 464 TeamId = :TeamId1 465 AND Type IN ('O') 466 AND DeleteAt = 0 467 AND Id NOT IN (SELECT 468 Channels.Id 469 FROM 470 Channels, 471 ChannelMembers 472 WHERE 473 Id = ChannelId 474 AND TeamId = :TeamId2 475 AND UserId = :UserId 476 AND DeleteAt = 0) 477 ORDER BY DisplayName 478 LIMIT :Limit 479 OFFSET :Offset`, 480 map[string]interface{}{"TeamId1": teamId, "TeamId2": teamId, "UserId": userId, "Limit": limit, "Offset": offset}) 481 482 if err != nil { 483 result.Err = model.NewAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 484 } else { 485 result.Data = data 486 } 487 }) 488 } 489 490 func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) store.StoreChannel { 491 return store.Do(func(result *store.StoreResult) { 492 data := &model.ChannelList{} 493 _, err := s.GetReplica().Select(data, 494 `SELECT 495 * 496 FROM 497 Channels 498 WHERE 499 TeamId = :TeamId 500 AND Type = 'O' 501 AND DeleteAt = 0 502 ORDER BY DisplayName 503 LIMIT :Limit 504 OFFSET :Offset`, 505 map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset}) 506 507 if err != nil { 508 result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 509 } else { 510 result.Data = data 511 } 512 }) 513 } 514 515 func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) store.StoreChannel { 516 return store.Do(func(result *store.StoreResult) { 517 props := make(map[string]interface{}) 518 props["teamId"] = teamId 519 520 idQuery := "" 521 522 for index, channelId := range channelIds { 523 if len(idQuery) > 0 { 524 idQuery += ", " 525 } 526 527 props["channelId"+strconv.Itoa(index)] = channelId 528 idQuery += ":channelId" + strconv.Itoa(index) 529 } 530 531 data := &model.ChannelList{} 532 _, err := s.GetReplica().Select(data, 533 `SELECT 534 * 535 FROM 536 Channels 537 WHERE 538 TeamId = :teamId 539 AND Type = 'O' 540 AND DeleteAt = 0 541 AND Id IN (`+idQuery+`) 542 ORDER BY DisplayName`, 543 props) 544 545 if err != nil { 546 result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError) 547 } 548 549 if len(*data) == 0 { 550 result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.not_found.app_error", nil, "", http.StatusNotFound) 551 } 552 553 result.Data = data 554 }) 555 } 556 557 type channelIdWithCountAndUpdateAt struct { 558 Id string 559 TotalMsgCount int64 560 UpdateAt int64 561 } 562 563 func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) store.StoreChannel { 564 return store.Do(func(result *store.StoreResult) { 565 var data []channelIdWithCountAndUpdateAt 566 _, err := s.GetReplica().Select(&data, "SELECT Id, TotalMsgCount, 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}) 567 568 if err != nil { 569 result.Err = model.NewAppError("SqlChannelStore.GetChannelCounts", "store.sql_channel.get_channel_counts.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 570 } else { 571 counts := &model.ChannelCounts{Counts: make(map[string]int64), UpdateTimes: make(map[string]int64)} 572 for i := range data { 573 v := data[i] 574 counts.Counts[v.Id] = v.TotalMsgCount 575 counts.UpdateTimes[v.Id] = v.UpdateAt 576 } 577 578 result.Data = counts 579 } 580 }) 581 } 582 583 func (s SqlChannelStore) GetTeamChannels(teamId string) store.StoreChannel { 584 return store.Do(func(result *store.StoreResult) { 585 data := &model.ChannelList{} 586 _, err := s.GetReplica().Select(data, "SELECT * FROM Channels WHERE TeamId = :TeamId And Type != 'D' ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId}) 587 588 if err != nil { 589 result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 590 } else { 591 if len(*data) == 0 { 592 result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.not_found.app_error", nil, "teamId="+teamId, http.StatusNotFound) 593 } else { 594 result.Data = data 595 } 596 } 597 }) 598 } 599 600 func (s SqlChannelStore) GetByName(teamId string, name string, allowFromCache bool) store.StoreChannel { 601 return s.getByName(teamId, name, false, allowFromCache) 602 } 603 604 func (s SqlChannelStore) GetByNames(teamId string, names []string, allowFromCache bool) store.StoreChannel { 605 return store.Do(func(result *store.StoreResult) { 606 var channels []*model.Channel 607 608 if allowFromCache { 609 var misses []string 610 visited := make(map[string]struct{}) 611 for _, name := range names { 612 if _, ok := visited[name]; ok { 613 continue 614 } 615 visited[name] = struct{}{} 616 if cacheItem, ok := channelByNameCache.Get(teamId + name); ok { 617 if s.metrics != nil { 618 s.metrics.IncrementMemCacheHitCounter("Channel By Name") 619 } 620 channels = append(channels, cacheItem.(*model.Channel)) 621 } else { 622 if s.metrics != nil { 623 s.metrics.IncrementMemCacheMissCounter("Channel By Name") 624 } 625 misses = append(misses, name) 626 } 627 } 628 names = misses 629 } 630 631 if len(names) > 0 { 632 props := map[string]interface{}{} 633 var namePlaceholders []string 634 for _, name := range names { 635 key := fmt.Sprintf("Name%v", len(namePlaceholders)) 636 props[key] = name 637 namePlaceholders = append(namePlaceholders, ":"+key) 638 } 639 640 var query string 641 if teamId == "" { 642 query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0` 643 } else { 644 props["TeamId"] = teamId 645 query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND TeamId = :TeamId AND DeleteAt = 0` 646 } 647 648 var dbChannels []*model.Channel 649 if _, err := s.GetReplica().Select(&dbChannels, query, props); err != nil && err != sql.ErrNoRows { 650 result.Err = model.NewAppError("SqlChannelStore.GetByName", "store.sql_channel.get_by_name.existing.app_error", nil, "teamId="+teamId+", "+err.Error(), http.StatusInternalServerError) 651 return 652 } 653 for _, channel := range dbChannels { 654 channelByNameCache.AddWithExpiresInSecs(teamId+channel.Name, channel, CHANNEL_CACHE_SEC) 655 channels = append(channels, channel) 656 } 657 } 658 659 result.Data = channels 660 }) 661 } 662 663 func (s SqlChannelStore) GetByNameIncludeDeleted(teamId string, name string, allowFromCache bool) store.StoreChannel { 664 return s.getByName(teamId, name, true, allowFromCache) 665 } 666 667 func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bool, allowFromCache bool) store.StoreChannel { 668 var query string 669 if includeDeleted { 670 query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name" 671 } else { 672 query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt = 0" 673 } 674 return store.Do(func(result *store.StoreResult) { 675 channel := model.Channel{} 676 677 if allowFromCache { 678 if cacheItem, ok := channelByNameCache.Get(teamId + name); ok { 679 if s.metrics != nil { 680 s.metrics.IncrementMemCacheHitCounter("Channel By Name") 681 } 682 result.Data = cacheItem.(*model.Channel) 683 return 684 } else { 685 if s.metrics != nil { 686 s.metrics.IncrementMemCacheMissCounter("Channel By Name") 687 } 688 } 689 } 690 if err := s.GetReplica().SelectOne(&channel, query, map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil { 691 if err == sql.ErrNoRows { 692 result.Err = model.NewAppError("SqlChannelStore.GetByName", store.MISSING_CHANNEL_ERROR, nil, "teamId="+teamId+", "+"name="+name+", "+err.Error(), http.StatusNotFound) 693 } else { 694 result.Err = model.NewAppError("SqlChannelStore.GetByName", "store.sql_channel.get_by_name.existing.app_error", nil, "teamId="+teamId+", "+"name="+name+", "+err.Error(), http.StatusInternalServerError) 695 } 696 } else { 697 result.Data = &channel 698 channelByNameCache.AddWithExpiresInSecs(teamId+name, &channel, CHANNEL_CACHE_SEC) 699 } 700 }) 701 } 702 703 func (s SqlChannelStore) GetDeletedByName(teamId string, name string) store.StoreChannel { 704 return store.Do(func(result *store.StoreResult) { 705 channel := model.Channel{} 706 707 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 { 708 if err == sql.ErrNoRows { 709 result.Err = model.NewAppError("SqlChannelStore.GetDeletedByName", "store.sql_channel.get_deleted_by_name.missing.app_error", nil, "teamId="+teamId+", "+"name="+name+", "+err.Error(), http.StatusNotFound) 710 } else { 711 result.Err = model.NewAppError("SqlChannelStore.GetDeletedByName", "store.sql_channel.get_deleted_by_name.existing.app_error", nil, "teamId="+teamId+", "+"name="+name+", "+err.Error(), http.StatusInternalServerError) 712 } 713 } else { 714 result.Data = &channel 715 } 716 }) 717 } 718 719 func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int) store.StoreChannel { 720 return store.Do(func(result *store.StoreResult) { 721 channels := &model.ChannelList{} 722 723 if _, err := s.GetReplica().Select(channels, "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND DeleteAt != 0 ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset}); err != nil { 724 if err == sql.ErrNoRows { 725 result.Err = model.NewAppError("SqlChannelStore.GetDeleted", "store.sql_channel.get_deleted.missing.app_error", nil, "teamId="+teamId+", "+err.Error(), http.StatusNotFound) 726 } else { 727 result.Err = model.NewAppError("SqlChannelStore.GetDeleted", "store.sql_channel.get_deleted.existing.app_error", nil, "teamId="+teamId+", "+err.Error(), http.StatusInternalServerError) 728 } 729 } else { 730 result.Data = channels 731 } 732 }) 733 } 734 735 func (s SqlChannelStore) SaveMember(member *model.ChannelMember) store.StoreChannel { 736 return store.Do(func(result *store.StoreResult) { 737 // Grab the channel we are saving this member to 738 if cr := <-s.GetFromMaster(member.ChannelId); cr.Err != nil { 739 result.Err = cr.Err 740 } else { 741 channel := cr.Data.(*model.Channel) 742 743 if transaction, err := s.GetMaster().Begin(); err != nil { 744 result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 745 } else { 746 *result = s.saveMemberT(transaction, member, channel) 747 if result.Err != nil { 748 transaction.Rollback() 749 } else { 750 if err := transaction.Commit(); err != nil { 751 result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 752 } 753 // If successfull record members have changed in channel 754 if mu := <-s.extraUpdated(channel); mu.Err != nil { 755 result.Err = mu.Err 756 } 757 } 758 } 759 } 760 761 s.InvalidateAllChannelMembersForUser(member.UserId) 762 }) 763 } 764 765 func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *model.ChannelMember, channel *model.Channel) store.StoreResult { 766 result := store.StoreResult{} 767 768 member.PreSave() 769 if result.Err = member.IsValid(); result.Err != nil { 770 return result 771 } 772 773 if err := transaction.Insert(member); err != nil { 774 if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey"}) { 775 result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.exists.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusBadRequest) 776 } else { 777 result.Err = model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.save.app_error", nil, "channel_id="+member.ChannelId+", user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) 778 } 779 } else { 780 result.Data = member 781 } 782 783 return result 784 } 785 786 func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) store.StoreChannel { 787 return store.Do(func(result *store.StoreResult) { 788 member.PreUpdate() 789 790 if result.Err = member.IsValid(); result.Err != nil { 791 return 792 } 793 794 if _, err := s.GetMaster().Update(member); err != nil { 795 result.Err = model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) 796 } else { 797 result.Data = member 798 } 799 }) 800 } 801 802 func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) store.StoreChannel { 803 return store.Do(func(result *store.StoreResult) { 804 var members model.ChannelMembers 805 _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset}) 806 if err != nil { 807 result.Err = model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+err.Error(), http.StatusInternalServerError) 808 } else { 809 result.Data = &members 810 } 811 }) 812 } 813 814 func (s SqlChannelStore) GetMember(channelId string, userId string) store.StoreChannel { 815 return store.Do(func(result *store.StoreResult) { 816 var member model.ChannelMember 817 818 if err := s.GetReplica().SelectOne(&member, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { 819 if err == sql.ErrNoRows { 820 result.Err = model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound) 821 } else { 822 result.Err = model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError) 823 } 824 } else { 825 result.Data = &member 826 } 827 }) 828 } 829 830 func (s SqlChannelStore) InvalidateAllChannelMembersForUser(userId string) { 831 allChannelMembersForUserCache.Remove(userId) 832 if s.metrics != nil { 833 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Remove by UserId") 834 } 835 } 836 837 func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string) bool { 838 if cacheItem, ok := allChannelMembersForUserCache.Get(userId); ok { 839 if s.metrics != nil { 840 s.metrics.IncrementMemCacheHitCounter("All Channel Members for User") 841 } 842 ids := cacheItem.(map[string]string) 843 if _, ok := ids[channelId]; ok { 844 return true 845 } else { 846 return false 847 } 848 } else { 849 if s.metrics != nil { 850 s.metrics.IncrementMemCacheMissCounter("All Channel Members for User") 851 } 852 } 853 854 if result := <-s.GetAllChannelMembersForUser(userId, true); result.Err != nil { 855 mlog.Error("SqlChannelStore.IsUserInChannelUseCache: " + result.Err.Error()) 856 return false 857 } else { 858 ids := result.Data.(map[string]string) 859 if _, ok := ids[channelId]; ok { 860 return true 861 } else { 862 return false 863 } 864 } 865 } 866 867 func (s SqlChannelStore) GetMemberForPost(postId string, userId string) store.StoreChannel { 868 return store.Do(func(result *store.StoreResult) { 869 member := &model.ChannelMember{} 870 if err := s.GetReplica().SelectOne( 871 member, 872 `SELECT 873 ChannelMembers.* 874 FROM 875 ChannelMembers, 876 Posts 877 WHERE 878 ChannelMembers.ChannelId = Posts.ChannelId 879 AND ChannelMembers.UserId = :UserId 880 AND Posts.Id = :PostId`, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil { 881 result.Err = model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 882 } else { 883 result.Data = member 884 } 885 }) 886 } 887 888 type allChannelMember struct { 889 ChannelId string 890 Roles string 891 } 892 893 func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel { 894 return store.Do(func(result *store.StoreResult) { 895 if allowFromCache { 896 if cacheItem, ok := allChannelMembersForUserCache.Get(userId); ok { 897 if s.metrics != nil { 898 s.metrics.IncrementMemCacheHitCounter("All Channel Members for User") 899 } 900 result.Data = cacheItem.(map[string]string) 901 return 902 } else { 903 if s.metrics != nil { 904 s.metrics.IncrementMemCacheMissCounter("All Channel Members for User") 905 } 906 } 907 } else { 908 if s.metrics != nil { 909 s.metrics.IncrementMemCacheMissCounter("All Channel Members for User") 910 } 911 } 912 913 var data []allChannelMember 914 _, err := s.GetReplica().Select(&data, "SELECT ChannelId, Roles FROM Channels, ChannelMembers WHERE Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.UserId = :UserId AND Channels.DeleteAt = 0", map[string]interface{}{"UserId": userId}) 915 916 if err != nil { 917 result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 918 } else { 919 920 ids := make(map[string]string) 921 for i := range data { 922 ids[data[i].ChannelId] = data[i].Roles 923 } 924 925 result.Data = ids 926 927 if allowFromCache { 928 allChannelMembersForUserCache.AddWithExpiresInSecs(userId, ids, ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SEC) 929 } 930 } 931 }) 932 } 933 934 func (s SqlChannelStore) InvalidateCacheForChannelMembersNotifyProps(channelId string) { 935 allChannelMembersNotifyPropsForChannelCache.Remove(channelId) 936 if s.metrics != nil { 937 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Remove by ChannelId") 938 } 939 } 940 941 type allChannelMemberNotifyProps struct { 942 UserId string 943 NotifyProps model.StringMap 944 } 945 946 func (s SqlChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) store.StoreChannel { 947 return store.Do(func(result *store.StoreResult) { 948 if allowFromCache { 949 if cacheItem, ok := allChannelMembersNotifyPropsForChannelCache.Get(channelId); ok { 950 if s.metrics != nil { 951 s.metrics.IncrementMemCacheHitCounter("All Channel Members Notify Props for Channel") 952 } 953 result.Data = cacheItem.(map[string]model.StringMap) 954 return 955 } else { 956 if s.metrics != nil { 957 s.metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel") 958 } 959 } 960 } else { 961 if s.metrics != nil { 962 s.metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel") 963 } 964 } 965 966 var data []allChannelMemberNotifyProps 967 _, err := s.GetReplica().Select(&data, ` 968 SELECT UserId, NotifyProps 969 FROM ChannelMembers 970 WHERE ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId}) 971 972 if err != nil { 973 result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersPropsForChannel", "store.sql_channel.get_members.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError) 974 } else { 975 976 props := make(map[string]model.StringMap) 977 for i := range data { 978 props[data[i].UserId] = data[i].NotifyProps 979 } 980 981 result.Data = props 982 983 allChannelMembersNotifyPropsForChannelCache.AddWithExpiresInSecs(channelId, props, ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SEC) 984 } 985 }) 986 } 987 988 func (s SqlChannelStore) InvalidateMemberCount(channelId string) { 989 channelMemberCountsCache.Remove(channelId) 990 if s.metrics != nil { 991 s.metrics.IncrementMemCacheInvalidationCounter("Channel Member Counts - Remove by ChannelId") 992 } 993 } 994 995 func (s SqlChannelStore) GetMemberCountFromCache(channelId string) int64 { 996 if cacheItem, ok := channelMemberCountsCache.Get(channelId); ok { 997 if s.metrics != nil { 998 s.metrics.IncrementMemCacheHitCounter("Channel Member Counts") 999 } 1000 return cacheItem.(int64) 1001 } else { 1002 if s.metrics != nil { 1003 s.metrics.IncrementMemCacheMissCounter("Channel Member Counts") 1004 } 1005 } 1006 1007 if result := <-s.GetMemberCount(channelId, true); result.Err != nil { 1008 return 0 1009 } else { 1010 return result.Data.(int64) 1011 } 1012 } 1013 1014 func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) store.StoreChannel { 1015 return store.Do(func(result *store.StoreResult) { 1016 if allowFromCache { 1017 if cacheItem, ok := channelMemberCountsCache.Get(channelId); ok { 1018 if s.metrics != nil { 1019 s.metrics.IncrementMemCacheHitCounter("Channel Member Counts") 1020 } 1021 result.Data = cacheItem.(int64) 1022 return 1023 } else { 1024 if s.metrics != nil { 1025 s.metrics.IncrementMemCacheMissCounter("Channel Member Counts") 1026 } 1027 } 1028 } else { 1029 if s.metrics != nil { 1030 s.metrics.IncrementMemCacheMissCounter("Channel Member Counts") 1031 } 1032 } 1033 1034 count, err := s.GetReplica().SelectInt(` 1035 SELECT 1036 count(*) 1037 FROM 1038 ChannelMembers, 1039 Users 1040 WHERE 1041 ChannelMembers.UserId = Users.Id 1042 AND ChannelMembers.ChannelId = :ChannelId 1043 AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) 1044 if err != nil { 1045 result.Err = model.NewAppError("SqlChannelStore.GetMemberCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 1046 } else { 1047 result.Data = count 1048 1049 if allowFromCache { 1050 channelMemberCountsCache.AddWithExpiresInSecs(channelId, count, CHANNEL_MEMBERS_COUNTS_CACHE_SEC) 1051 } 1052 } 1053 }) 1054 } 1055 1056 func (s SqlChannelStore) RemoveMember(channelId string, userId string) store.StoreChannel { 1057 return store.Do(func(result *store.StoreResult) { 1058 // Grab the channel we are saving this member to 1059 if cr := <-s.Get(channelId, true); cr.Err != nil { 1060 result.Err = cr.Err 1061 } else { 1062 channel := cr.Data.(*model.Channel) 1063 1064 _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}) 1065 if err != nil { 1066 result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1067 } else { 1068 // If successfull record members have changed in channel 1069 if mu := <-s.extraUpdated(channel); mu.Err != nil { 1070 result.Err = mu.Err 1071 } 1072 } 1073 } 1074 }) 1075 } 1076 1077 func (s SqlChannelStore) PermanentDeleteMembersByUser(userId string) store.StoreChannel { 1078 return store.Do(func(result *store.StoreResult) { 1079 if _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { 1080 result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.permanent_delete_members_by_user.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1081 } 1082 }) 1083 } 1084 1085 func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string) store.StoreChannel { 1086 return store.Do(func(result *store.StoreResult) { 1087 props := make(map[string]interface{}) 1088 1089 updateIdQuery := "" 1090 for index, channelId := range channelIds { 1091 if len(updateIdQuery) > 0 { 1092 updateIdQuery += " OR " 1093 } 1094 1095 props["channelId"+strconv.Itoa(index)] = channelId 1096 updateIdQuery += "ChannelId = :channelId" + strconv.Itoa(index) 1097 } 1098 1099 selectIdQuery := strings.Replace(updateIdQuery, "ChannelId", "Id", -1) 1100 1101 var lastPostAtTimes []struct { 1102 Id string 1103 LastPostAt int64 1104 TotalMsgCount int64 1105 } 1106 1107 selectQuery := "SELECT Id, LastPostAt, TotalMsgCount FROM Channels WHERE (" + selectIdQuery + ")" 1108 1109 if _, err := s.GetMaster().Select(&lastPostAtTimes, selectQuery, props); err != nil { 1110 result.Err = model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1111 return 1112 } 1113 1114 times := map[string]int64{} 1115 msgCountQuery := "" 1116 lastViewedQuery := "" 1117 for index, t := range lastPostAtTimes { 1118 times[t.Id] = t.LastPostAt 1119 1120 props["msgCount"+strconv.Itoa(index)] = t.TotalMsgCount 1121 msgCountQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(MsgCount, :msgCount%d) ", index, index) 1122 1123 props["lastViewed"+strconv.Itoa(index)] = t.LastPostAt 1124 lastViewedQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(LastViewedAt, :lastViewed%d) ", index, index) 1125 1126 props["channelId"+strconv.Itoa(index)] = t.Id 1127 } 1128 1129 var updateQuery string 1130 1131 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1132 updateQuery = `UPDATE 1133 ChannelMembers 1134 SET 1135 MentionCount = 0, 1136 MsgCount = CAST(CASE ChannelId ` + msgCountQuery + ` END AS BIGINT), 1137 LastViewedAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT), 1138 LastUpdateAt = CAST(CASE ChannelId ` + lastViewedQuery + ` END AS BIGINT) 1139 WHERE 1140 UserId = :UserId 1141 AND (` + updateIdQuery + `)` 1142 } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 1143 updateQuery = `UPDATE 1144 ChannelMembers 1145 SET 1146 MentionCount = 0, 1147 MsgCount = CASE ChannelId ` + msgCountQuery + ` END, 1148 LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END, 1149 LastUpdateAt = CASE ChannelId ` + lastViewedQuery + ` END 1150 WHERE 1151 UserId = :UserId 1152 AND (` + updateIdQuery + `)` 1153 } 1154 1155 props["UserId"] = userId 1156 1157 if _, err := s.GetMaster().Exec(updateQuery, props); err != nil { 1158 result.Err = model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1159 } else { 1160 result.Data = times 1161 } 1162 }) 1163 } 1164 1165 func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) store.StoreChannel { 1166 return store.Do(func(result *store.StoreResult) { 1167 _, err := s.GetMaster().Exec( 1168 `UPDATE 1169 ChannelMembers 1170 SET 1171 MentionCount = MentionCount + 1, 1172 LastUpdateAt = :LastUpdateAt 1173 WHERE 1174 UserId = :UserId 1175 AND ChannelId = :ChannelId`, 1176 map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": model.GetMillis()}) 1177 if err != nil { 1178 result.Err = model.NewAppError("SqlChannelStore.IncrementMentionCount", "store.sql_channel.increment_mention_count.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1179 } 1180 }) 1181 } 1182 1183 func (s SqlChannelStore) GetAll(teamId string) store.StoreChannel { 1184 return store.Do(func(result *store.StoreResult) { 1185 var data []*model.Channel 1186 _, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Type != 'D' ORDER BY Name", map[string]interface{}{"TeamId": teamId}) 1187 1188 if err != nil { 1189 result.Err = model.NewAppError("SqlChannelStore.GetAll", "store.sql_channel.get_all.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 1190 } else { 1191 result.Data = data 1192 } 1193 }) 1194 } 1195 1196 func (s SqlChannelStore) GetForPost(postId string) store.StoreChannel { 1197 return store.Do(func(result *store.StoreResult) { 1198 channel := &model.Channel{} 1199 if err := s.GetReplica().SelectOne( 1200 channel, 1201 `SELECT 1202 Channels.* 1203 FROM 1204 Channels, 1205 Posts 1206 WHERE 1207 Channels.Id = Posts.ChannelId 1208 AND Posts.Id = :PostId`, map[string]interface{}{"PostId": postId}); err != nil { 1209 result.Err = model.NewAppError("SqlChannelStore.GetForPost", "store.sql_channel.get_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 1210 } else { 1211 result.Data = channel 1212 } 1213 }) 1214 } 1215 1216 func (s SqlChannelStore) AnalyticsTypeCount(teamId string, channelType string) store.StoreChannel { 1217 return store.Do(func(result *store.StoreResult) { 1218 query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType" 1219 1220 if len(teamId) > 0 { 1221 query += " AND TeamId = :TeamId" 1222 } 1223 1224 v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) 1225 if err != nil { 1226 result.Err = model.NewAppError("SqlChannelStore.AnalyticsTypeCount", "store.sql_channel.analytics_type_count.app_error", nil, err.Error(), http.StatusInternalServerError) 1227 } else { 1228 result.Data = v 1229 } 1230 }) 1231 } 1232 1233 func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType string) store.StoreChannel { 1234 return store.Do(func(result *store.StoreResult) { 1235 query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType AND DeleteAt > 0" 1236 1237 if len(teamId) > 0 { 1238 query += " AND TeamId = :TeamId" 1239 } 1240 1241 v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) 1242 if err != nil { 1243 result.Err = model.NewAppError("SqlChannelStore.AnalyticsDeletedTypeCount", "store.sql_channel.analytics_deleted_type_count.app_error", nil, err.Error(), http.StatusInternalServerError) 1244 } else { 1245 result.Data = v 1246 } 1247 }) 1248 } 1249 1250 func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.StoreChannel { 1251 return store.Do(func(result *store.StoreResult) { 1252 members := &model.ChannelMembers{} 1253 _, err := s.GetReplica().Select(members, ` 1254 SELECT cm.* 1255 FROM ChannelMembers cm 1256 INNER JOIN Channels c 1257 ON c.Id = cm.ChannelId 1258 AND (c.TeamId = :TeamId OR c.TeamId = '') 1259 AND c.DeleteAt = 0 1260 WHERE cm.UserId = :UserId 1261 `, map[string]interface{}{"TeamId": teamId, "UserId": userId}) 1262 1263 if err != nil { 1264 result.Err = model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1265 } else { 1266 result.Data = members 1267 } 1268 }) 1269 } 1270 1271 func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string) store.StoreChannel { 1272 return store.Do(func(result *store.StoreResult) { 1273 queryFormat := ` 1274 SELECT 1275 * 1276 FROM 1277 Channels 1278 WHERE 1279 TeamId = :TeamId 1280 AND Type = 'O' 1281 AND DeleteAt = 0 1282 %v 1283 LIMIT 50` 1284 1285 var channels model.ChannelList 1286 1287 if likeClause, likeTerm := s.buildLIKEClause(term); likeClause == "" { 1288 if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil { 1289 result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 1290 } 1291 } else { 1292 // Using a UNION results in index_merge and fulltext queries and is much faster than the ref 1293 // query you would get using an OR of the LIKE and full-text clauses. 1294 fulltextClause, fulltextTerm := s.buildFulltextClause(term) 1295 likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause) 1296 fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause) 1297 query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery) 1298 1299 if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil { 1300 result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 1301 } 1302 } 1303 1304 sort.Slice(channels, func(a, b int) bool { 1305 return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName) 1306 }) 1307 result.Data = &channels 1308 }) 1309 } 1310 1311 func (s SqlChannelStore) SearchInTeam(teamId string, term string) store.StoreChannel { 1312 return store.Do(func(result *store.StoreResult) { 1313 searchQuery := ` 1314 SELECT 1315 * 1316 FROM 1317 Channels 1318 WHERE 1319 TeamId = :TeamId 1320 AND Type = 'O' 1321 AND DeleteAt = 0 1322 SEARCH_CLAUSE 1323 ORDER BY DisplayName 1324 LIMIT 100` 1325 1326 *result = s.performSearch(searchQuery, term, map[string]interface{}{"TeamId": teamId}) 1327 }) 1328 } 1329 1330 func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) store.StoreChannel { 1331 return store.Do(func(result *store.StoreResult) { 1332 searchQuery := ` 1333 SELECT 1334 * 1335 FROM 1336 Channels 1337 WHERE 1338 TeamId = :TeamId 1339 AND Type = 'O' 1340 AND DeleteAt = 0 1341 AND Id NOT IN (SELECT 1342 Channels.Id 1343 FROM 1344 Channels, 1345 ChannelMembers 1346 WHERE 1347 Id = ChannelId 1348 AND TeamId = :TeamId 1349 AND UserId = :UserId 1350 AND DeleteAt = 0) 1351 SEARCH_CLAUSE 1352 ORDER BY DisplayName 1353 LIMIT 100` 1354 1355 *result = s.performSearch(searchQuery, term, map[string]interface{}{"TeamId": teamId, "UserId": userId}) 1356 }) 1357 } 1358 1359 func (s SqlChannelStore) buildLIKEClause(term string) (likeClause, likeTerm string) { 1360 likeTerm = term 1361 searchColumns := "Name, DisplayName" 1362 1363 // These chars must be removed from the like query. 1364 for _, c := range ignoreLikeSearchChar { 1365 likeTerm = strings.Replace(likeTerm, c, "", -1) 1366 } 1367 1368 // These chars must be escaped in the like query. 1369 for _, c := range escapeLikeSearchChar { 1370 likeTerm = strings.Replace(likeTerm, c, "*"+c, -1) 1371 } 1372 1373 if likeTerm == "" { 1374 return 1375 } 1376 1377 // Prepare the LIKE portion of the query. 1378 var searchFields []string 1379 for _, field := range strings.Split(searchColumns, ", ") { 1380 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1381 searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*'", field, ":LikeTerm")) 1382 } else { 1383 searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*'", field, ":LikeTerm")) 1384 } 1385 } 1386 1387 likeClause = fmt.Sprintf("(%s)", strings.Join(searchFields, " OR ")) 1388 likeTerm += "%" 1389 return 1390 } 1391 1392 func (s SqlChannelStore) buildFulltextClause(term string) (fulltextClause, fulltextTerm string) { 1393 // Copy the terms as we will need to prepare them differently for each search type. 1394 fulltextTerm = term 1395 1396 searchColumns := "Name, DisplayName" 1397 1398 // These chars must be treated as spaces in the fulltext query. 1399 for _, c := range spaceFulltextSearchChar { 1400 fulltextTerm = strings.Replace(fulltextTerm, c, " ", -1) 1401 } 1402 1403 // Prepare the FULLTEXT portion of the query. 1404 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1405 splitTerm := strings.Fields(fulltextTerm) 1406 for i, t := range strings.Fields(fulltextTerm) { 1407 if i == len(splitTerm)-1 { 1408 splitTerm[i] = t + ":*" 1409 } else { 1410 splitTerm[i] = t + ":* &" 1411 } 1412 } 1413 1414 fulltextTerm = strings.Join(splitTerm, " ") 1415 1416 fulltextClause = fmt.Sprintf("((%s) @@ to_tsquery(:FulltextTerm))", convertMySQLFullTextColumnsToPostgres(searchColumns)) 1417 } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 1418 splitTerm := strings.Fields(fulltextTerm) 1419 for i, t := range strings.Fields(fulltextTerm) { 1420 splitTerm[i] = "+" + t + "*" 1421 } 1422 1423 fulltextTerm = strings.Join(splitTerm, " ") 1424 1425 fulltextClause = fmt.Sprintf("MATCH(%s) AGAINST (:FulltextTerm IN BOOLEAN MODE)", searchColumns) 1426 } 1427 1428 return 1429 } 1430 1431 func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) store.StoreResult { 1432 result := store.StoreResult{} 1433 1434 likeClause, likeTerm := s.buildLIKEClause(term) 1435 if likeTerm == "" { 1436 // If the likeTerm is empty after preparing, then don't bother searching. 1437 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1) 1438 } else { 1439 parameters["LikeTerm"] = likeTerm 1440 fulltextClause, fulltextTerm := s.buildFulltextClause(term) 1441 parameters["FulltextTerm"] = fulltextTerm 1442 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1) 1443 } 1444 1445 var channels model.ChannelList 1446 1447 if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil { 1448 result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 1449 } else { 1450 result.Data = &channels 1451 } 1452 1453 return result 1454 } 1455 1456 func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) store.StoreChannel { 1457 return store.Do(func(result *store.StoreResult) { 1458 var members model.ChannelMembers 1459 props := make(map[string]interface{}) 1460 idQuery := "" 1461 1462 for index, userId := range userIds { 1463 if len(idQuery) > 0 { 1464 idQuery += ", " 1465 } 1466 1467 props["userId"+strconv.Itoa(index)] = userId 1468 idQuery += ":userId" + strconv.Itoa(index) 1469 } 1470 1471 props["ChannelId"] = channelId 1472 1473 if _, err := s.GetReplica().Select(&members, "SELECT * FROM ChannelMembers WHERE ChannelId = :ChannelId AND UserId IN ("+idQuery+")", props); err != nil { 1474 result.Err = model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError) 1475 } else { 1476 result.Data = &members 1477 1478 } 1479 }) 1480 }