github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/store/sqlstore/channel_store.go (about)

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