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