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