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