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