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