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