github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+incompatible/store/sqlstore/channel_store.go (about)

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