github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/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  	"time"
    14  
    15  	"github.com/mattermost/gorp"
    16  	"github.com/mattermost/mattermost-server/v5/einterfaces"
    17  	"github.com/mattermost/mattermost-server/v5/mlog"
    18  	"github.com/mattermost/mattermost-server/v5/model"
    19  	"github.com/mattermost/mattermost-server/v5/services/cache"
    20  	"github.com/mattermost/mattermost-server/v5/store"
    21  
    22  	sq "github.com/Masterminds/squirrel"
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  const (
    27  	ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE     = model.SESSION_CACHE_SIZE
    28  	ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_DURATION = 15 * time.Minute // 15 mins
    29  
    30  	ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE     = model.SESSION_CACHE_SIZE
    31  	ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_DURATION = 30 * time.Minute // 30 mins
    32  
    33  	CHANNEL_CACHE_DURATION = 15 * time.Minute // 15 mins
    34  )
    35  
    36  type SqlChannelStore struct {
    37  	SqlStore
    38  	metrics einterfaces.MetricsInterface
    39  }
    40  
    41  type channelMember struct {
    42  	ChannelId    string
    43  	UserId       string
    44  	Roles        string
    45  	LastViewedAt int64
    46  	MsgCount     int64
    47  	MentionCount int64
    48  	NotifyProps  model.StringMap
    49  	LastUpdateAt int64
    50  	SchemeUser   sql.NullBool
    51  	SchemeAdmin  sql.NullBool
    52  	SchemeGuest  sql.NullBool
    53  }
    54  
    55  func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember {
    56  	return &channelMember{
    57  		ChannelId:    cm.ChannelId,
    58  		UserId:       cm.UserId,
    59  		Roles:        cm.ExplicitRoles,
    60  		LastViewedAt: cm.LastViewedAt,
    61  		MsgCount:     cm.MsgCount,
    62  		MentionCount: cm.MentionCount,
    63  		NotifyProps:  cm.NotifyProps,
    64  		LastUpdateAt: cm.LastUpdateAt,
    65  		SchemeGuest:  sql.NullBool{Valid: true, Bool: cm.SchemeGuest},
    66  		SchemeUser:   sql.NullBool{Valid: true, Bool: cm.SchemeUser},
    67  		SchemeAdmin:  sql.NullBool{Valid: true, Bool: cm.SchemeAdmin},
    68  	}
    69  }
    70  
    71  type channelMemberWithSchemeRoles struct {
    72  	ChannelId                     string
    73  	UserId                        string
    74  	Roles                         string
    75  	LastViewedAt                  int64
    76  	MsgCount                      int64
    77  	MentionCount                  int64
    78  	NotifyProps                   model.StringMap
    79  	LastUpdateAt                  int64
    80  	SchemeGuest                   sql.NullBool
    81  	SchemeUser                    sql.NullBool
    82  	SchemeAdmin                   sql.NullBool
    83  	TeamSchemeDefaultGuestRole    sql.NullString
    84  	TeamSchemeDefaultUserRole     sql.NullString
    85  	TeamSchemeDefaultAdminRole    sql.NullString
    86  	ChannelSchemeDefaultGuestRole sql.NullString
    87  	ChannelSchemeDefaultUserRole  sql.NullString
    88  	ChannelSchemeDefaultAdminRole sql.NullString
    89  }
    90  
    91  func channelMemberSliceColumns() []string {
    92  	return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MentionCount", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"}
    93  }
    94  
    95  func channelMemberToSlice(member *model.ChannelMember) []interface{} {
    96  	resultSlice := []interface{}{}
    97  	resultSlice = append(resultSlice, member.ChannelId)
    98  	resultSlice = append(resultSlice, member.UserId)
    99  	resultSlice = append(resultSlice, member.ExplicitRoles)
   100  	resultSlice = append(resultSlice, member.LastViewedAt)
   101  	resultSlice = append(resultSlice, member.MsgCount)
   102  	resultSlice = append(resultSlice, member.MentionCount)
   103  	resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps))
   104  	resultSlice = append(resultSlice, member.LastUpdateAt)
   105  	resultSlice = append(resultSlice, member.SchemeUser)
   106  	resultSlice = append(resultSlice, member.SchemeAdmin)
   107  	resultSlice = append(resultSlice, member.SchemeGuest)
   108  	return resultSlice
   109  }
   110  
   111  type channelMemberWithSchemeRolesList []channelMemberWithSchemeRoles
   112  
   113  func getChannelRoles(schemeGuest, schemeUser, schemeAdmin bool, defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole, defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole string, roles []string) rolesInfo {
   114  	result := rolesInfo{
   115  		roles:         []string{},
   116  		explicitRoles: []string{},
   117  		schemeGuest:   schemeGuest,
   118  		schemeUser:    schemeUser,
   119  		schemeAdmin:   schemeAdmin,
   120  	}
   121  
   122  	// Identify any scheme derived roles that are in "Roles" field due to not yet being migrated, and exclude
   123  	// them from ExplicitRoles field.
   124  	for _, role := range roles {
   125  		switch role {
   126  		case model.CHANNEL_GUEST_ROLE_ID:
   127  			result.schemeGuest = true
   128  		case model.CHANNEL_USER_ROLE_ID:
   129  			result.schemeUser = true
   130  		case model.CHANNEL_ADMIN_ROLE_ID:
   131  			result.schemeAdmin = true
   132  		default:
   133  			result.explicitRoles = append(result.explicitRoles, role)
   134  			result.roles = append(result.roles, role)
   135  		}
   136  	}
   137  
   138  	// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
   139  	// them to the Roles field for backwards compatibility reasons.
   140  	var schemeImpliedRoles []string
   141  	if result.schemeGuest {
   142  		if defaultChannelGuestRole != "" {
   143  			schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelGuestRole)
   144  		} else if defaultTeamGuestRole != "" {
   145  			schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamGuestRole)
   146  		} else {
   147  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_GUEST_ROLE_ID)
   148  		}
   149  	}
   150  	if result.schemeUser {
   151  		if defaultChannelUserRole != "" {
   152  			schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelUserRole)
   153  		} else if defaultTeamUserRole != "" {
   154  			schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamUserRole)
   155  		} else {
   156  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
   157  		}
   158  	}
   159  	if result.schemeAdmin {
   160  		if defaultChannelAdminRole != "" {
   161  			schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelAdminRole)
   162  		} else if defaultTeamAdminRole != "" {
   163  			schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamAdminRole)
   164  		} else {
   165  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
   166  		}
   167  	}
   168  	for _, impliedRole := range schemeImpliedRoles {
   169  		alreadyThere := false
   170  		for _, role := range result.roles {
   171  			if role == impliedRole {
   172  				alreadyThere = true
   173  				break
   174  			}
   175  		}
   176  		if !alreadyThere {
   177  			result.roles = append(result.roles, impliedRole)
   178  		}
   179  	}
   180  	return result
   181  }
   182  
   183  func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember {
   184  	// Identify any system-wide scheme derived roles that are in "Roles" field due to not yet being migrated,
   185  	// and exclude them from ExplicitRoles field.
   186  	schemeGuest := db.SchemeGuest.Valid && db.SchemeGuest.Bool
   187  	schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool
   188  	schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool
   189  
   190  	defaultTeamGuestRole := ""
   191  	if db.TeamSchemeDefaultGuestRole.Valid {
   192  		defaultTeamGuestRole = db.TeamSchemeDefaultGuestRole.String
   193  	}
   194  
   195  	defaultTeamUserRole := ""
   196  	if db.TeamSchemeDefaultUserRole.Valid {
   197  		defaultTeamUserRole = db.TeamSchemeDefaultUserRole.String
   198  	}
   199  
   200  	defaultTeamAdminRole := ""
   201  	if db.TeamSchemeDefaultAdminRole.Valid {
   202  		defaultTeamAdminRole = db.TeamSchemeDefaultAdminRole.String
   203  	}
   204  
   205  	defaultChannelGuestRole := ""
   206  	if db.ChannelSchemeDefaultGuestRole.Valid {
   207  		defaultChannelGuestRole = db.ChannelSchemeDefaultGuestRole.String
   208  	}
   209  
   210  	defaultChannelUserRole := ""
   211  	if db.ChannelSchemeDefaultUserRole.Valid {
   212  		defaultChannelUserRole = db.ChannelSchemeDefaultUserRole.String
   213  	}
   214  
   215  	defaultChannelAdminRole := ""
   216  	if db.ChannelSchemeDefaultAdminRole.Valid {
   217  		defaultChannelAdminRole = db.ChannelSchemeDefaultAdminRole.String
   218  	}
   219  
   220  	rolesResult := getChannelRoles(
   221  		schemeGuest, schemeUser, schemeAdmin,
   222  		defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole,
   223  		defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole,
   224  		strings.Fields(db.Roles),
   225  	)
   226  	return &model.ChannelMember{
   227  		ChannelId:     db.ChannelId,
   228  		UserId:        db.UserId,
   229  		Roles:         strings.Join(rolesResult.roles, " "),
   230  		LastViewedAt:  db.LastViewedAt,
   231  		MsgCount:      db.MsgCount,
   232  		MentionCount:  db.MentionCount,
   233  		NotifyProps:   db.NotifyProps,
   234  		LastUpdateAt:  db.LastUpdateAt,
   235  		SchemeAdmin:   rolesResult.schemeAdmin,
   236  		SchemeUser:    rolesResult.schemeUser,
   237  		SchemeGuest:   rolesResult.schemeGuest,
   238  		ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
   239  	}
   240  }
   241  
   242  func (db channelMemberWithSchemeRolesList) ToModel() *model.ChannelMembers {
   243  	cms := model.ChannelMembers{}
   244  
   245  	for _, cm := range db {
   246  		cms = append(cms, *cm.ToModel())
   247  	}
   248  
   249  	return &cms
   250  }
   251  
   252  type allChannelMember struct {
   253  	ChannelId                     string
   254  	Roles                         string
   255  	SchemeGuest                   sql.NullBool
   256  	SchemeUser                    sql.NullBool
   257  	SchemeAdmin                   sql.NullBool
   258  	TeamSchemeDefaultGuestRole    sql.NullString
   259  	TeamSchemeDefaultUserRole     sql.NullString
   260  	TeamSchemeDefaultAdminRole    sql.NullString
   261  	ChannelSchemeDefaultGuestRole sql.NullString
   262  	ChannelSchemeDefaultUserRole  sql.NullString
   263  	ChannelSchemeDefaultAdminRole sql.NullString
   264  }
   265  
   266  type allChannelMembers []allChannelMember
   267  
   268  func (db allChannelMember) Process() (string, string) {
   269  	roles := strings.Fields(db.Roles)
   270  
   271  	// Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add
   272  	// them to the Roles field for backwards compatibility reasons.
   273  	var schemeImpliedRoles []string
   274  	if db.SchemeGuest.Valid && db.SchemeGuest.Bool {
   275  		if db.ChannelSchemeDefaultGuestRole.Valid && db.ChannelSchemeDefaultGuestRole.String != "" {
   276  			schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultGuestRole.String)
   277  		} else if db.TeamSchemeDefaultGuestRole.Valid && db.TeamSchemeDefaultGuestRole.String != "" {
   278  			schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultGuestRole.String)
   279  		} else {
   280  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_GUEST_ROLE_ID)
   281  		}
   282  	}
   283  	if db.SchemeUser.Valid && db.SchemeUser.Bool {
   284  		if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" {
   285  			schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String)
   286  		} else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" {
   287  			schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String)
   288  		} else {
   289  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID)
   290  		}
   291  	}
   292  	if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool {
   293  		if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" {
   294  			schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String)
   295  		} else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" {
   296  			schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String)
   297  		} else {
   298  			schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID)
   299  		}
   300  	}
   301  	for _, impliedRole := range schemeImpliedRoles {
   302  		alreadyThere := false
   303  		for _, role := range roles {
   304  			if role == impliedRole {
   305  				alreadyThere = true
   306  			}
   307  		}
   308  		if !alreadyThere {
   309  			roles = append(roles, impliedRole)
   310  		}
   311  	}
   312  
   313  	return db.ChannelId, strings.Join(roles, " ")
   314  }
   315  
   316  func (db allChannelMembers) ToMapStringString() map[string]string {
   317  	result := make(map[string]string)
   318  
   319  	for _, item := range db {
   320  		key, value := item.Process()
   321  		result[key] = value
   322  	}
   323  
   324  	return result
   325  }
   326  
   327  // publicChannel is a subset of the metadata corresponding to public channels only.
   328  type publicChannel struct {
   329  	Id          string `json:"id"`
   330  	DeleteAt    int64  `json:"delete_at"`
   331  	TeamId      string `json:"team_id"`
   332  	DisplayName string `json:"display_name"`
   333  	Name        string `json:"name"`
   334  	Header      string `json:"header"`
   335  	Purpose     string `json:"purpose"`
   336  }
   337  
   338  var allChannelMembersForUserCache = cache.NewLRU(&cache.LRUOptions{
   339  	Size: ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE,
   340  })
   341  var allChannelMembersNotifyPropsForChannelCache = cache.NewLRU(&cache.LRUOptions{
   342  	Size: ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE,
   343  })
   344  var channelByNameCache = cache.NewLRU(&cache.LRUOptions{
   345  	Size: model.CHANNEL_CACHE_SIZE,
   346  })
   347  
   348  func (s SqlChannelStore) ClearCaches() {
   349  	allChannelMembersForUserCache.Purge()
   350  	allChannelMembersNotifyPropsForChannelCache.Purge()
   351  	channelByNameCache.Purge()
   352  
   353  	if s.metrics != nil {
   354  		s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Purge")
   355  		s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Purge")
   356  		s.metrics.IncrementMemCacheInvalidationCounter("Channel By Name - Purge")
   357  	}
   358  }
   359  
   360  func newSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.ChannelStore {
   361  	s := &SqlChannelStore{
   362  		SqlStore: sqlStore,
   363  		metrics:  metrics,
   364  	}
   365  
   366  	for _, db := range sqlStore.GetAllConns() {
   367  		table := db.AddTableWithName(model.Channel{}, "Channels").SetKeys(false, "Id")
   368  		table.ColMap("Id").SetMaxSize(26)
   369  		table.ColMap("TeamId").SetMaxSize(26)
   370  		table.ColMap("Type").SetMaxSize(1)
   371  		table.ColMap("DisplayName").SetMaxSize(64)
   372  		table.ColMap("Name").SetMaxSize(64)
   373  		table.SetUniqueTogether("Name", "TeamId")
   374  		table.ColMap("Header").SetMaxSize(1024)
   375  		table.ColMap("Purpose").SetMaxSize(250)
   376  		table.ColMap("CreatorId").SetMaxSize(26)
   377  		table.ColMap("SchemeId").SetMaxSize(26)
   378  
   379  		tablem := db.AddTableWithName(channelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId")
   380  		tablem.ColMap("ChannelId").SetMaxSize(26)
   381  		tablem.ColMap("UserId").SetMaxSize(26)
   382  		tablem.ColMap("Roles").SetMaxSize(64)
   383  		tablem.ColMap("NotifyProps").SetMaxSize(2000)
   384  
   385  		tablePublicChannels := db.AddTableWithName(publicChannel{}, "PublicChannels").SetKeys(false, "Id")
   386  		tablePublicChannels.ColMap("Id").SetMaxSize(26)
   387  		tablePublicChannels.ColMap("TeamId").SetMaxSize(26)
   388  		tablePublicChannels.ColMap("DisplayName").SetMaxSize(64)
   389  		tablePublicChannels.ColMap("Name").SetMaxSize(64)
   390  		tablePublicChannels.SetUniqueTogether("Name", "TeamId")
   391  		tablePublicChannels.ColMap("Header").SetMaxSize(1024)
   392  		tablePublicChannels.ColMap("Purpose").SetMaxSize(250)
   393  
   394  		tableSidebarCategories := db.AddTableWithName(model.SidebarCategory{}, "SidebarCategories").SetKeys(false, "Id")
   395  		tableSidebarCategories.ColMap("Id").SetMaxSize(26)
   396  		tableSidebarCategories.ColMap("UserId").SetMaxSize(26)
   397  		tableSidebarCategories.ColMap("TeamId").SetMaxSize(26)
   398  		tableSidebarCategories.ColMap("Sorting").SetMaxSize(64)
   399  		tableSidebarCategories.ColMap("Type").SetMaxSize(64)
   400  		tableSidebarCategories.ColMap("DisplayName").SetMaxSize(64)
   401  
   402  		tableSidebarChannels := db.AddTableWithName(model.SidebarChannel{}, "SidebarChannels").SetKeys(false, "ChannelId", "UserId", "CategoryId")
   403  		tableSidebarChannels.ColMap("ChannelId").SetMaxSize(26)
   404  		tableSidebarChannels.ColMap("UserId").SetMaxSize(26)
   405  		tableSidebarChannels.ColMap("CategoryId").SetMaxSize(26)
   406  	}
   407  
   408  	return s
   409  }
   410  
   411  func (s SqlChannelStore) createIndexesIfNotExists() {
   412  	s.CreateIndexIfNotExists("idx_channels_team_id", "Channels", "TeamId")
   413  	s.CreateIndexIfNotExists("idx_channels_name", "Channels", "Name")
   414  	s.CreateIndexIfNotExists("idx_channels_update_at", "Channels", "UpdateAt")
   415  	s.CreateIndexIfNotExists("idx_channels_create_at", "Channels", "CreateAt")
   416  	s.CreateIndexIfNotExists("idx_channels_delete_at", "Channels", "DeleteAt")
   417  
   418  	if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   419  		s.CreateIndexIfNotExists("idx_channels_name_lower", "Channels", "lower(Name)")
   420  		s.CreateIndexIfNotExists("idx_channels_displayname_lower", "Channels", "lower(DisplayName)")
   421  	}
   422  
   423  	s.CreateIndexIfNotExists("idx_channelmembers_channel_id", "ChannelMembers", "ChannelId")
   424  	s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId")
   425  
   426  	s.CreateFullTextIndexIfNotExists("idx_channel_search_txt", "Channels", "Name, DisplayName, Purpose")
   427  
   428  	s.CreateIndexIfNotExists("idx_publicchannels_team_id", "PublicChannels", "TeamId")
   429  	s.CreateIndexIfNotExists("idx_publicchannels_name", "PublicChannels", "Name")
   430  	s.CreateIndexIfNotExists("idx_publicchannels_delete_at", "PublicChannels", "DeleteAt")
   431  	if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   432  		s.CreateIndexIfNotExists("idx_publicchannels_name_lower", "PublicChannels", "lower(Name)")
   433  		s.CreateIndexIfNotExists("idx_publicchannels_displayname_lower", "PublicChannels", "lower(DisplayName)")
   434  	}
   435  	s.CreateFullTextIndexIfNotExists("idx_publicchannels_search_txt", "PublicChannels", "Name, DisplayName, Purpose")
   436  	s.CreateIndexIfNotExists("idx_channels_scheme_id", "Channels", "SchemeId")
   437  }
   438  
   439  func (s SqlChannelStore) CreateInitialSidebarCategories(userId, teamId string) error {
   440  	transaction, err := s.GetMaster().Begin()
   441  	if err != nil {
   442  		return errors.Wrap(err, "CreateInitialSidebarCategories: begin_transaction")
   443  	}
   444  	defer finalizeTransaction(transaction)
   445  
   446  	if err := s.createInitialSidebarCategoriesT(transaction, userId, teamId); err != nil {
   447  		return errors.Wrap(err, "CreateInitialSidebarCategories: createInitialSidebarCategoriesT")
   448  	}
   449  
   450  	if err := transaction.Commit(); err != nil {
   451  		return errors.Wrap(err, "CreateInitialSidebarCategories: commit_transaction")
   452  	}
   453  
   454  	return nil
   455  }
   456  
   457  func (s SqlChannelStore) createInitialSidebarCategoriesT(transaction *gorp.Transaction, userId, teamId string) error {
   458  	selectQuery, selectParams, _ := s.getQueryBuilder().
   459  		Select("Type").
   460  		From("SidebarCategories").
   461  		Where(sq.Eq{
   462  			"UserId": userId,
   463  			"TeamId": teamId,
   464  			"Type":   []model.SidebarCategoryType{model.SidebarCategoryFavorites, model.SidebarCategoryChannels, model.SidebarCategoryDirectMessages},
   465  		}).ToSql()
   466  
   467  	var existingTypes []model.SidebarCategoryType
   468  	_, err := transaction.Select(&existingTypes, selectQuery, selectParams...)
   469  	if err != nil {
   470  		return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to select existing categories")
   471  	}
   472  
   473  	hasCategoryOfType := make(map[model.SidebarCategoryType]bool, len(existingTypes))
   474  	for _, existingType := range existingTypes {
   475  		hasCategoryOfType[existingType] = true
   476  	}
   477  
   478  	if !hasCategoryOfType[model.SidebarCategoryFavorites] {
   479  		favoritesCategoryId := model.NewId()
   480  
   481  		// Create the SidebarChannels first since there's more opportunity for something to fail here
   482  		if err := s.migrateFavoritesToSidebarT(transaction, userId, teamId, favoritesCategoryId); err != nil {
   483  			return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to migrate favorites to sidebar")
   484  		}
   485  
   486  		if err := transaction.Insert(&model.SidebarCategory{
   487  			DisplayName: "Favorites", // This will be retranslated by the client into the user's locale
   488  			Id:          favoritesCategoryId,
   489  			UserId:      userId,
   490  			TeamId:      teamId,
   491  			Sorting:     model.SidebarCategorySortDefault,
   492  			SortOrder:   model.DefaultSidebarSortOrderFavorites,
   493  			Type:        model.SidebarCategoryFavorites,
   494  		}); err != nil {
   495  			return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert favorites category")
   496  		}
   497  	}
   498  
   499  	if !hasCategoryOfType[model.SidebarCategoryChannels] {
   500  		if err := transaction.Insert(&model.SidebarCategory{
   501  			DisplayName: "Channels", // This will be retranslateed by the client into the user's locale
   502  			Id:          model.NewId(),
   503  			UserId:      userId,
   504  			TeamId:      teamId,
   505  			Sorting:     model.SidebarCategorySortDefault,
   506  			SortOrder:   model.DefaultSidebarSortOrderChannels,
   507  			Type:        model.SidebarCategoryChannels,
   508  		}); err != nil {
   509  			return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert channels category")
   510  		}
   511  	}
   512  
   513  	if !hasCategoryOfType[model.SidebarCategoryDirectMessages] {
   514  		if err := transaction.Insert(&model.SidebarCategory{
   515  			DisplayName: "Direct Messages", // This will be retranslateed by the client into the user's locale
   516  			Id:          model.NewId(),
   517  			UserId:      userId,
   518  			TeamId:      teamId,
   519  			Sorting:     model.SidebarCategorySortRecent,
   520  			SortOrder:   model.DefaultSidebarSortOrderDMs,
   521  			Type:        model.SidebarCategoryDirectMessages,
   522  		}); err != nil {
   523  			return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert direct messages category")
   524  		}
   525  	}
   526  
   527  	return nil
   528  }
   529  
   530  type userMembership struct {
   531  	UserId     string
   532  	ChannelId  string
   533  	CategoryId string
   534  }
   535  
   536  func (s SqlChannelStore) migrateMembershipToSidebar(transaction *gorp.Transaction, runningOrder *int64, sql string, args ...interface{}) ([]userMembership, error) {
   537  	var memberships []userMembership
   538  	if _, err := transaction.Select(&memberships, sql, args...); err != nil {
   539  		return nil, err
   540  	}
   541  
   542  	for _, favorite := range memberships {
   543  		sql, args, _ := s.getQueryBuilder().
   544  			Insert("SidebarChannels").
   545  			Columns("ChannelId", "UserId", "CategoryId", "SortOrder").
   546  			Values(favorite.ChannelId, favorite.UserId, favorite.CategoryId, *runningOrder).ToSql()
   547  
   548  		if _, err := transaction.Exec(sql, args...); err != nil && !IsUniqueConstraintError(err, []string{"UserId", "PRIMARY"}) {
   549  			return nil, err
   550  		}
   551  		*runningOrder = *runningOrder + model.MinimalSidebarSortDistance
   552  	}
   553  
   554  	if err := transaction.Commit(); err != nil {
   555  		return nil, err
   556  	}
   557  	return memberships, nil
   558  }
   559  
   560  func (s SqlChannelStore) migrateFavoritesToSidebarT(transaction *gorp.Transaction, userId, teamId, favoritesCategoryId string) error {
   561  	favoritesQuery, favoritesParams, _ := s.getQueryBuilder().
   562  		Select("Preferences.Name").
   563  		From("Preferences").
   564  		Join("Channels on Preferences.Name = Channels.Id").
   565  		Join("ChannelMembers on Preferences.Name = ChannelMembers.ChannelId and Preferences.UserId = ChannelMembers.UserId").
   566  		Where(sq.Eq{
   567  			"Preferences.UserId":   userId,
   568  			"Preferences.Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
   569  			"Preferences.Value":    "true",
   570  		}).
   571  		Where(sq.Or{
   572  			sq.Eq{"Channels.TeamId": teamId},
   573  			sq.Eq{"Channels.TeamId": ""},
   574  		}).
   575  		OrderBy(
   576  			"Channels.DisplayName",
   577  			"Channels.Name ASC",
   578  		).ToSql()
   579  
   580  	var favoriteChannelIds []string
   581  	if _, err := transaction.Select(&favoriteChannelIds, favoritesQuery, favoritesParams...); err != nil {
   582  		return errors.Wrap(err, "migrateFavoritesToSidebarT: unable to get favorite channel IDs")
   583  	}
   584  
   585  	for i, channelId := range favoriteChannelIds {
   586  		if err := transaction.Insert(&model.SidebarChannel{
   587  			ChannelId:  channelId,
   588  			CategoryId: favoritesCategoryId,
   589  			UserId:     userId,
   590  			SortOrder:  int64(i * model.MinimalSidebarSortDistance),
   591  		}); err != nil {
   592  			return errors.Wrap(err, "migrateFavoritesToSidebarT: unable to insert SidebarChannel")
   593  		}
   594  	}
   595  
   596  	return nil
   597  }
   598  
   599  // MigrateFavoritesToSidebarChannels populates the SidebarChannels table by analyzing existing user preferences for favorites
   600  // **IMPORTANT** This function should only be called from the migration task and shouldn't be used by itself
   601  func (s SqlChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) {
   602  	transaction, err := s.GetMaster().Begin()
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	defer finalizeTransaction(transaction)
   608  
   609  	sb := s.
   610  		getQueryBuilder().
   611  		Select("Preferences.UserId", "Preferences.Name AS ChannelId", "SidebarCategories.Id AS CategoryId").
   612  		From("Preferences").
   613  		Where(sq.And{
   614  			sq.Eq{"Preferences.Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL},
   615  			sq.NotEq{"Preferences.Value": "false"},
   616  			sq.NotEq{"SidebarCategories.Id": nil},
   617  			sq.Gt{"Preferences.UserId": lastUserId},
   618  		}).
   619  		LeftJoin("Channels ON (Channels.Id=Preferences.Name)").
   620  		LeftJoin("SidebarCategories ON (SidebarCategories.UserId=Preferences.UserId AND SidebarCategories.Type='"+string(model.SidebarCategoryFavorites)+"' AND (SidebarCategories.TeamId=Channels.TeamId OR Channels.TeamId=''))").
   621  		OrderBy("Preferences.UserId", "Channels.Name DESC").
   622  		Limit(100)
   623  
   624  	sql, args, err := sb.ToSql()
   625  	if err != nil {
   626  		return nil, err
   627  	}
   628  
   629  	userFavorites, err := s.migrateMembershipToSidebar(transaction, &runningOrder, sql, args...)
   630  	if err != nil {
   631  		return nil, err
   632  	}
   633  	if len(userFavorites) == 0 {
   634  		return nil, nil
   635  	}
   636  
   637  	data := make(map[string]interface{})
   638  	data["UserId"] = userFavorites[len(userFavorites)-1].UserId
   639  	data["SortOrder"] = runningOrder
   640  	return data, nil
   641  }
   642  
   643  // MigratePublicChannels initializes the PublicChannels table with data created before this version
   644  // of the Mattermost server kept it up-to-date.
   645  func (s SqlChannelStore) MigratePublicChannels() error {
   646  	if _, err := s.GetMaster().Exec(`
   647  		INSERT INTO PublicChannels
   648  		    (Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
   649  		SELECT
   650  		    c.Id, c.DeleteAt, c.TeamId, c.DisplayName, c.Name, c.Header, c.Purpose
   651  		FROM
   652  		    Channels c
   653  		LEFT JOIN
   654  		    PublicChannels pc ON (pc.Id = c.Id)
   655  		WHERE
   656  		    c.Type = 'O'
   657  		AND pc.Id IS NULL
   658  	`); err != nil {
   659  		return err
   660  	}
   661  
   662  	return nil
   663  }
   664  
   665  func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, channel *model.Channel) error {
   666  	publicChannel := &publicChannel{
   667  		Id:          channel.Id,
   668  		DeleteAt:    channel.DeleteAt,
   669  		TeamId:      channel.TeamId,
   670  		DisplayName: channel.DisplayName,
   671  		Name:        channel.Name,
   672  		Header:      channel.Header,
   673  		Purpose:     channel.Purpose,
   674  	}
   675  
   676  	if channel.Type != model.CHANNEL_OPEN {
   677  		if _, err := transaction.Delete(publicChannel); err != nil {
   678  			return errors.Wrap(err, "failed to delete public channel")
   679  		}
   680  
   681  		return nil
   682  	}
   683  
   684  	if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
   685  		// Leverage native upsert for MySQL, since RowsAffected returns 0 if the row exists
   686  		// but no changes were made, breaking the update-then-insert paradigm below when
   687  		// the row already exists. (Postgres 9.4 doesn't support native upsert.)
   688  		if _, err := transaction.Exec(`
   689  			INSERT INTO
   690  			    PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
   691  			VALUES
   692  			    (:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose)
   693  			ON DUPLICATE KEY UPDATE
   694  			    DeleteAt = :DeleteAt,
   695  			    TeamId = :TeamId,
   696  			    DisplayName = :DisplayName,
   697  			    Name = :Name,
   698  			    Header = :Header,
   699  			    Purpose = :Purpose;
   700  		`, map[string]interface{}{
   701  			"Id":          publicChannel.Id,
   702  			"DeleteAt":    publicChannel.DeleteAt,
   703  			"TeamId":      publicChannel.TeamId,
   704  			"DisplayName": publicChannel.DisplayName,
   705  			"Name":        publicChannel.Name,
   706  			"Header":      publicChannel.Header,
   707  			"Purpose":     publicChannel.Purpose,
   708  		}); err != nil {
   709  			return errors.Wrap(err, "failed to insert public channel")
   710  		}
   711  	} else {
   712  		count, err := transaction.Update(publicChannel)
   713  		if err != nil {
   714  			return errors.Wrap(err, "failed to update public channel")
   715  		}
   716  		if count > 0 {
   717  			return nil
   718  		}
   719  
   720  		if err := transaction.Insert(publicChannel); err != nil {
   721  			return errors.Wrap(err, "failed to insert public channel")
   722  		}
   723  	}
   724  
   725  	return nil
   726  }
   727  
   728  // Save writes the (non-direct) channel channel to the database.
   729  func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) {
   730  	if channel.DeleteAt != 0 {
   731  		return nil, store.NewErrInvalidInput("Channel", "DeleteAt", channel.DeleteAt)
   732  	}
   733  
   734  	if channel.Type == model.CHANNEL_DIRECT {
   735  		return nil, store.NewErrInvalidInput("Channel", "Type", channel.Type)
   736  	}
   737  
   738  	var newChannel *model.Channel
   739  	err := store.WithDeadlockRetry(func() error {
   740  		transaction, err := s.GetMaster().Begin()
   741  		if err != nil {
   742  			return errors.Wrap(err, "begin_transaction")
   743  		}
   744  		defer finalizeTransaction(transaction)
   745  
   746  		newChannel, err = s.saveChannelT(transaction, channel, maxChannelsPerTeam)
   747  		if err != nil {
   748  			return err
   749  		}
   750  
   751  		// Additionally propagate the write to the PublicChannels table.
   752  		if err := s.upsertPublicChannelT(transaction, newChannel); err != nil {
   753  			return errors.Wrap(err, "upsert_public_channel")
   754  		}
   755  
   756  		if err := transaction.Commit(); err != nil {
   757  			return errors.Wrap(err, "commit_transaction")
   758  		}
   759  		return nil
   760  	})
   761  	// There are cases when in case of conflict, the original channel value is returned.
   762  	// So we return both and let the caller do the checks.
   763  	return newChannel, err
   764  }
   765  
   766  func (s SqlChannelStore) CreateDirectChannel(user *model.User, otherUser *model.User) (*model.Channel, error) {
   767  	channel := new(model.Channel)
   768  
   769  	channel.DisplayName = ""
   770  	channel.Name = model.GetDMNameFromIds(otherUser.Id, user.Id)
   771  
   772  	channel.Header = ""
   773  	channel.Type = model.CHANNEL_DIRECT
   774  
   775  	cm1 := &model.ChannelMember{
   776  		UserId:      user.Id,
   777  		NotifyProps: model.GetDefaultChannelNotifyProps(),
   778  		SchemeGuest: user.IsGuest(),
   779  		SchemeUser:  !user.IsGuest(),
   780  	}
   781  	cm2 := &model.ChannelMember{
   782  		UserId:      otherUser.Id,
   783  		NotifyProps: model.GetDefaultChannelNotifyProps(),
   784  		SchemeGuest: otherUser.IsGuest(),
   785  		SchemeUser:  !otherUser.IsGuest(),
   786  	}
   787  
   788  	return s.SaveDirectChannel(channel, cm1, cm2)
   789  }
   790  
   791  func (s SqlChannelStore) SaveDirectChannel(directchannel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) (*model.Channel, error) {
   792  	if directchannel.DeleteAt != 0 {
   793  		return nil, store.NewErrInvalidInput("Channel", "DeleteAt", directchannel.DeleteAt)
   794  	}
   795  
   796  	if directchannel.Type != model.CHANNEL_DIRECT {
   797  		return nil, store.NewErrInvalidInput("Channel", "Type", directchannel.Type)
   798  	}
   799  
   800  	transaction, err := s.GetMaster().Begin()
   801  	if err != nil {
   802  		return nil, errors.Wrap(err, "begin_transaction")
   803  	}
   804  	defer finalizeTransaction(transaction)
   805  
   806  	directchannel.TeamId = ""
   807  	newChannel, err := s.saveChannelT(transaction, directchannel, 0)
   808  	if err != nil {
   809  		return newChannel, err
   810  	}
   811  
   812  	// Members need new channel ID
   813  	member1.ChannelId = newChannel.Id
   814  	member2.ChannelId = newChannel.Id
   815  
   816  	if member1.UserId != member2.UserId {
   817  		_, err = s.saveMultipleMembersT(transaction, []*model.ChannelMember{member1, member2})
   818  	} else {
   819  		_, err = s.saveMemberT(transaction, member2)
   820  	}
   821  	if err != nil {
   822  		return nil, err
   823  	}
   824  
   825  	if err := transaction.Commit(); err != nil {
   826  		return nil, errors.Wrap(err, "commit_transaction")
   827  	}
   828  
   829  	return newChannel, nil
   830  
   831  }
   832  
   833  func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) {
   834  	if len(channel.Id) > 0 {
   835  		return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id)
   836  	}
   837  
   838  	channel.PreSave()
   839  	if err := channel.IsValid(); err != nil { // TODO: this needs to return plain error in v6.
   840  		return nil, err // we just pass through the error as-is for now.
   841  	}
   842  
   843  	if channel.Type != model.CHANNEL_DIRECT && channel.Type != model.CHANNEL_GROUP && maxChannelsPerTeam >= 0 {
   844  		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 {
   845  			return nil, errors.Wrapf(err, "save_channel_count: teamId=%s", channel.TeamId)
   846  		} else if count >= maxChannelsPerTeam {
   847  			return nil, store.NewErrLimitExceeded("channels_per_team", int(count), "teamId="+channel.TeamId)
   848  		}
   849  	}
   850  
   851  	if err := transaction.Insert(channel); err != nil {
   852  		if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) {
   853  			dupChannel := model.Channel{}
   854  			s.GetMaster().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name = :Name", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name})
   855  			return &dupChannel, store.NewErrConflict("Channel", err, "id="+channel.Id)
   856  		}
   857  		return nil, errors.Wrapf(err, "save_channel: id=%s", channel.Id)
   858  	}
   859  	return channel, nil
   860  }
   861  
   862  // Update writes the updated channel to the database.
   863  func (s SqlChannelStore) Update(channel *model.Channel) (*model.Channel, error) {
   864  	transaction, err := s.GetMaster().Begin()
   865  	if err != nil {
   866  		return nil, errors.Wrap(err, "begin_transaction")
   867  	}
   868  	defer finalizeTransaction(transaction)
   869  
   870  	updatedChannel, appErr := s.updateChannelT(transaction, channel)
   871  	if appErr != nil {
   872  		return nil, appErr
   873  	}
   874  
   875  	// Additionally propagate the write to the PublicChannels table.
   876  	if err := s.upsertPublicChannelT(transaction, updatedChannel); err != nil {
   877  		return nil, errors.Wrap(err, "upsertPublicChannelT: failed to upsert channel")
   878  	}
   879  
   880  	if err := transaction.Commit(); err != nil {
   881  		return nil, errors.Wrap(err, "commit_transaction")
   882  	}
   883  	return updatedChannel, nil
   884  }
   885  
   886  func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel *model.Channel) (*model.Channel, error) {
   887  	channel.PreUpdate()
   888  
   889  	if channel.DeleteAt != 0 {
   890  		return nil, store.NewErrInvalidInput("Channel", "DeleteAt", channel.DeleteAt)
   891  	}
   892  
   893  	if err := channel.IsValid(); err != nil {
   894  		return nil, err
   895  	}
   896  
   897  	count, err := transaction.Update(channel)
   898  	if err != nil {
   899  		if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) {
   900  			dupChannel := model.Channel{}
   901  			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})
   902  			if dupChannel.DeleteAt > 0 {
   903  				return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id)
   904  			}
   905  			return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id)
   906  		}
   907  		return nil, errors.Wrapf(err, "failed to update channel with id=%s", channel.Id)
   908  	}
   909  
   910  	if count != 1 {
   911  		return nil, fmt.Errorf("the expected number of channels to be updated is 1 but was %d", count)
   912  	}
   913  
   914  	return channel, nil
   915  }
   916  
   917  func (s SqlChannelStore) GetChannelUnread(channelId, userId string) (*model.ChannelUnread, *model.AppError) {
   918  	var unreadChannel model.ChannelUnread
   919  	err := s.GetReplica().SelectOne(&unreadChannel,
   920  		`SELECT
   921  				Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, ChannelMembers.MentionCount MentionCount, ChannelMembers.NotifyProps NotifyProps
   922  			FROM
   923  				Channels, ChannelMembers
   924  			WHERE
   925  				Id = ChannelId
   926                  AND Id = :ChannelId
   927                  AND UserId = :UserId
   928                  AND DeleteAt = 0`,
   929  		map[string]interface{}{"ChannelId": channelId, "UserId": userId})
   930  
   931  	if err != nil {
   932  		if err == sql.ErrNoRows {
   933  			return nil, model.NewAppError("SqlChannelStore.GetChannelUnread", "store.sql_channel.get_unread.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusNotFound)
   934  		}
   935  		return nil, model.NewAppError("SqlChannelStore.GetChannelUnread", "store.sql_channel.get_unread.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError)
   936  	}
   937  	return &unreadChannel, nil
   938  }
   939  
   940  func (s SqlChannelStore) InvalidateChannel(id string) {
   941  }
   942  
   943  func (s SqlChannelStore) InvalidateChannelByName(teamId, name string) {
   944  	channelByNameCache.Remove(teamId + name)
   945  	if s.metrics != nil {
   946  		s.metrics.IncrementMemCacheInvalidationCounter("Channel by Name - Remove by TeamId and Name")
   947  	}
   948  }
   949  
   950  func (s SqlChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) {
   951  	return s.get(id, false, allowFromCache)
   952  }
   953  
   954  func (s SqlChannelStore) GetPinnedPosts(channelId string) (*model.PostList, *model.AppError) {
   955  	pl := model.NewPostList()
   956  
   957  	var posts []*model.Post
   958  	if _, err := s.GetReplica().Select(&posts, "SELECT *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount  FROM Posts p WHERE IsPinned = true AND ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt ASC", map[string]interface{}{"ChannelId": channelId}); err != nil {
   959  		return nil, model.NewAppError("SqlPostStore.GetPinnedPosts", "store.sql_channel.pinned_posts.app_error", nil, err.Error(), http.StatusInternalServerError)
   960  	}
   961  	for _, post := range posts {
   962  		pl.AddPost(post)
   963  		pl.AddOrder(post.Id)
   964  	}
   965  	return pl, nil
   966  }
   967  
   968  func (s SqlChannelStore) GetFromMaster(id string) (*model.Channel, error) {
   969  	return s.get(id, true, false)
   970  }
   971  
   972  func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) (*model.Channel, error) {
   973  	var db *gorp.DbMap
   974  
   975  	if master {
   976  		db = s.GetMaster()
   977  	} else {
   978  		db = s.GetReplica()
   979  	}
   980  
   981  	obj, err := db.Get(model.Channel{}, id)
   982  	if err != nil {
   983  		return nil, errors.Wrapf(err, "failed to find channel with id = %s", id)
   984  	}
   985  
   986  	if obj == nil {
   987  		return nil, store.NewErrNotFound("Channel", id)
   988  	}
   989  
   990  	ch := obj.(*model.Channel)
   991  	return ch, nil
   992  }
   993  
   994  // Delete records the given deleted timestamp to the channel in question.
   995  func (s SqlChannelStore) Delete(channelId string, time int64) error {
   996  	return s.SetDeleteAt(channelId, time, time)
   997  }
   998  
   999  // Restore reverts a previous deleted timestamp from the channel in question.
  1000  func (s SqlChannelStore) Restore(channelId string, time int64) error {
  1001  	return s.SetDeleteAt(channelId, 0, time)
  1002  }
  1003  
  1004  // SetDeleteAt records the given deleted and updated timestamp to the channel in question.
  1005  func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) error {
  1006  	defer s.InvalidateChannel(channelId)
  1007  
  1008  	transaction, err := s.GetMaster().Begin()
  1009  	if err != nil {
  1010  		return errors.Wrap(err, "SetDeleteAt: begin_transaction")
  1011  	}
  1012  	defer finalizeTransaction(transaction)
  1013  
  1014  	err = s.setDeleteAtT(transaction, channelId, deleteAt, updateAt)
  1015  	if err != nil {
  1016  		return errors.Wrap(err, "setDeleteAtT")
  1017  	}
  1018  
  1019  	// Additionally propagate the write to the PublicChannels table.
  1020  	if _, err := transaction.Exec(`
  1021  			UPDATE
  1022  			    PublicChannels
  1023  			SET
  1024  			    DeleteAt = :DeleteAt
  1025  			WHERE
  1026  			    Id = :ChannelId
  1027  		`, map[string]interface{}{
  1028  		"DeleteAt":  deleteAt,
  1029  		"ChannelId": channelId,
  1030  	}); err != nil {
  1031  		return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId)
  1032  	}
  1033  
  1034  	if err := transaction.Commit(); err != nil {
  1035  		return errors.Wrapf(err, "SetDeleteAt: commit_transaction")
  1036  	}
  1037  
  1038  	return nil
  1039  }
  1040  
  1041  func (s SqlChannelStore) setDeleteAtT(transaction *gorp.Transaction, channelId string, deleteAt, updateAt int64) error {
  1042  	_, err := transaction.Exec("Update Channels SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :ChannelId", map[string]interface{}{"DeleteAt": deleteAt, "UpdateAt": updateAt, "ChannelId": channelId})
  1043  	if err != nil {
  1044  		return errors.Wrapf(err, "failed to delete channel with id=%s", channelId)
  1045  	}
  1046  
  1047  	return nil
  1048  }
  1049  
  1050  // PermanentDeleteByTeam removes all channels for the given team from the database.
  1051  func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) error {
  1052  	transaction, err := s.GetMaster().Begin()
  1053  	if err != nil {
  1054  		return errors.Wrap(err, "PermanentDeleteByTeam: begin_transaction")
  1055  	}
  1056  	defer finalizeTransaction(transaction)
  1057  
  1058  	if err := s.permanentDeleteByTeamtT(transaction, teamId); err != nil {
  1059  		return errors.Wrap(err, "permanentDeleteByTeamtT")
  1060  	}
  1061  
  1062  	// Additionally propagate the deletions to the PublicChannels table.
  1063  	if _, err := transaction.Exec(`
  1064  			DELETE FROM
  1065  			    PublicChannels
  1066  			WHERE
  1067  			    TeamId = :TeamId
  1068  		`, map[string]interface{}{
  1069  		"TeamId": teamId,
  1070  	}); err != nil {
  1071  		return errors.Wrapf(err, "failed to delete public channels by team with teamId=%s", teamId)
  1072  	}
  1073  
  1074  	if err := transaction.Commit(); err != nil {
  1075  		return errors.Wrap(err, "PermanentDeleteByTeam: commit_transaction")
  1076  	}
  1077  
  1078  	return nil
  1079  }
  1080  
  1081  func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *gorp.Transaction, teamId string) error {
  1082  	if _, err := transaction.Exec("DELETE FROM Channels WHERE TeamId = :TeamId", map[string]interface{}{"TeamId": teamId}); err != nil {
  1083  		return errors.Wrapf(err, "failed to delete channel by team with teamId=%s", teamId)
  1084  	}
  1085  
  1086  	return nil
  1087  }
  1088  
  1089  // PermanentDelete removes the given channel from the database.
  1090  func (s SqlChannelStore) PermanentDelete(channelId string) error {
  1091  	transaction, err := s.GetMaster().Begin()
  1092  	if err != nil {
  1093  		return errors.Wrap(err, "PermanentDelete: begin_transaction")
  1094  	}
  1095  	defer finalizeTransaction(transaction)
  1096  
  1097  	if err := s.permanentDeleteT(transaction, channelId); err != nil {
  1098  		return errors.Wrap(err, "permanentDeleteT")
  1099  	}
  1100  
  1101  	// Additionally propagate the deletion to the PublicChannels table.
  1102  	if _, err := transaction.Exec(`
  1103  			DELETE FROM
  1104  			    PublicChannels
  1105  			WHERE
  1106  			    Id = :ChannelId
  1107  		`, map[string]interface{}{
  1108  		"ChannelId": channelId,
  1109  	}); err != nil {
  1110  		return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId)
  1111  	}
  1112  
  1113  	if err := transaction.Commit(); err != nil {
  1114  		return errors.Wrap(err, "PermanentDelete: commit_transaction")
  1115  	}
  1116  
  1117  	return nil
  1118  }
  1119  
  1120  func (s SqlChannelStore) permanentDeleteT(transaction *gorp.Transaction, channelId string) error {
  1121  	if _, err := transaction.Exec("DELETE FROM Channels WHERE Id = :ChannelId", map[string]interface{}{"ChannelId": channelId}); err != nil {
  1122  		return errors.Wrapf(err, "failed to delete channel with id=%s", channelId)
  1123  	}
  1124  
  1125  	return nil
  1126  }
  1127  
  1128  func (s SqlChannelStore) PermanentDeleteMembersByChannel(channelId string) *model.AppError {
  1129  	_, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId})
  1130  	if err != nil {
  1131  		return model.NewAppError("SqlChannelStore.RemoveAllMembersByChannel", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  1132  	}
  1133  
  1134  	return nil
  1135  }
  1136  
  1137  func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDeleted bool, lastDeleteAt int) (*model.ChannelList, error) {
  1138  	query := s.getQueryBuilder().
  1139  		Select("Channels.*").
  1140  		From("Channels, ChannelMembers").
  1141  		Where(
  1142  			sq.And{
  1143  				sq.Expr("Id = ChannelId"),
  1144  				sq.Eq{"UserId": userId},
  1145  				sq.Or{
  1146  					sq.Eq{"TeamId": teamId},
  1147  					sq.Eq{"TeamId": ""},
  1148  				},
  1149  			},
  1150  		).
  1151  		OrderBy("DisplayName")
  1152  
  1153  	if includeDeleted {
  1154  		if lastDeleteAt != 0 {
  1155  			// We filter by non-archived, and archived >= a timestamp.
  1156  			query = query.Where(sq.Or{
  1157  				sq.Eq{"DeleteAt": 0},
  1158  				sq.GtOrEq{"DeleteAt": lastDeleteAt},
  1159  			})
  1160  		}
  1161  		// If lastDeleteAt is not set, we include everything. That means no filter is needed.
  1162  	} else {
  1163  		// Don't include archived channels.
  1164  		query = query.Where(sq.Eq{"DeleteAt": 0})
  1165  	}
  1166  
  1167  	channels := &model.ChannelList{}
  1168  	sql, args, err := query.ToSql()
  1169  	if err != nil {
  1170  		return nil, errors.Wrapf(err, "getchannels_tosql")
  1171  	}
  1172  
  1173  	_, err = s.GetReplica().Select(channels, sql, args...)
  1174  	if err != nil {
  1175  		return nil, errors.Wrapf(err, "failed to get channels with TeamId=%s and UserId=%s", teamId, userId)
  1176  	}
  1177  
  1178  	if len(*channels) == 0 {
  1179  		return nil, store.NewErrNotFound("Channel", "userId="+userId)
  1180  	}
  1181  
  1182  	return channels, nil
  1183  }
  1184  
  1185  func (s SqlChannelStore) GetAllChannels(offset, limit int, opts store.ChannelSearchOpts) (*model.ChannelListWithTeamData, error) {
  1186  	query := s.getAllChannelsQuery(opts, false)
  1187  
  1188  	query = query.OrderBy("c.DisplayName, Teams.DisplayName").Limit(uint64(limit)).Offset(uint64(offset))
  1189  
  1190  	queryString, args, err := query.ToSql()
  1191  	if err != nil {
  1192  		return nil, errors.Wrap(err, "failed to create query")
  1193  	}
  1194  
  1195  	data := &model.ChannelListWithTeamData{}
  1196  	_, err = s.GetReplica().Select(data, queryString, args...)
  1197  
  1198  	if err != nil {
  1199  		return nil, errors.Wrap(err, "failed to get all channels")
  1200  	}
  1201  
  1202  	return data, nil
  1203  }
  1204  
  1205  func (s SqlChannelStore) GetAllChannelsCount(opts store.ChannelSearchOpts) (int64, error) {
  1206  	query := s.getAllChannelsQuery(opts, true)
  1207  
  1208  	queryString, args, err := query.ToSql()
  1209  	if err != nil {
  1210  		return 0, errors.Wrap(err, "failed to create query")
  1211  	}
  1212  
  1213  	count, err := s.GetReplica().SelectInt(queryString, args...)
  1214  	if err != nil {
  1215  		return 0, errors.Wrap(err, "failed to count all channels")
  1216  	}
  1217  
  1218  	return count, nil
  1219  }
  1220  
  1221  func (s SqlChannelStore) getAllChannelsQuery(opts store.ChannelSearchOpts, forCount bool) sq.SelectBuilder {
  1222  	var selectStr string
  1223  	if forCount {
  1224  		selectStr = "count(c.Id)"
  1225  	} else {
  1226  		selectStr = "c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt AS TeamUpdateAt"
  1227  	}
  1228  
  1229  	query := s.getQueryBuilder().
  1230  		Select(selectStr).
  1231  		From("Channels AS c").
  1232  		Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}})
  1233  
  1234  	if !forCount {
  1235  		query = query.Join("Teams ON Teams.Id = c.TeamId")
  1236  	}
  1237  
  1238  	if !opts.IncludeDeleted {
  1239  		query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
  1240  	}
  1241  
  1242  	if len(opts.NotAssociatedToGroup) > 0 {
  1243  		query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
  1244  	}
  1245  
  1246  	if len(opts.ExcludeChannelNames) > 0 {
  1247  		query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames})
  1248  	}
  1249  
  1250  	return query
  1251  }
  1252  
  1253  func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) (*model.ChannelList, error) {
  1254  	channels := &model.ChannelList{}
  1255  	_, err := s.GetReplica().Select(channels, `
  1256  		SELECT
  1257  			Channels.*
  1258  		FROM
  1259  			Channels
  1260  		JOIN
  1261  			PublicChannels c ON (c.Id = Channels.Id)
  1262  		WHERE
  1263  			c.TeamId = :TeamId
  1264  		AND c.DeleteAt = 0
  1265  		AND c.Id NOT IN (
  1266  			SELECT
  1267  				c.Id
  1268  			FROM
  1269  				PublicChannels c
  1270  			JOIN
  1271  				ChannelMembers cm ON (cm.ChannelId = c.Id)
  1272  			WHERE
  1273  				c.TeamId = :TeamId
  1274  			AND cm.UserId = :UserId
  1275  			AND c.DeleteAt = 0
  1276  		)
  1277  		ORDER BY
  1278  			c.DisplayName
  1279  		LIMIT :Limit
  1280  		OFFSET :Offset
  1281  		`, map[string]interface{}{
  1282  		"TeamId": teamId,
  1283  		"UserId": userId,
  1284  		"Limit":  limit,
  1285  		"Offset": offset,
  1286  	})
  1287  
  1288  	if err != nil {
  1289  		return nil, errors.Wrapf(err, "failed getting channels with teamId=%s and userId=%s", teamId, userId)
  1290  	}
  1291  
  1292  	return channels, nil
  1293  }
  1294  
  1295  func (s SqlChannelStore) GetPrivateChannelsForTeam(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) {
  1296  	channels := &model.ChannelList{}
  1297  
  1298  	query := s.getQueryBuilder().
  1299  		Select("*").
  1300  		From("Channels").
  1301  		Where(sq.Eq{"Type": model.CHANNEL_PRIVATE, "TeamId": teamId, "DeleteAt": 0}).
  1302  		OrderBy("DisplayName").
  1303  		Limit(uint64(limit)).
  1304  		Offset(uint64(offset))
  1305  
  1306  	sql, args, err := query.ToSql()
  1307  	if err != nil {
  1308  		return nil, model.NewAppError("SqlChannelStore.GetPrivateChannelsForTeam", "store.sql_channel.get_private_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
  1309  	}
  1310  
  1311  	_, err = s.GetReplica().Select(channels, sql, args...)
  1312  	if err != nil {
  1313  		return nil, model.NewAppError("SqlChannelStore.GetPrivateChannelsForTeam", "store.sql_channel.get_private_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
  1314  	}
  1315  	return channels, nil
  1316  }
  1317  
  1318  func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) {
  1319  	channels := &model.ChannelList{}
  1320  	_, err := s.GetReplica().Select(channels, `
  1321  		SELECT
  1322  			Channels.*
  1323  		FROM
  1324  			Channels
  1325  		JOIN
  1326  			PublicChannels pc ON (pc.Id = Channels.Id)
  1327  		WHERE
  1328  			pc.TeamId = :TeamId
  1329  		AND pc.DeleteAt = 0
  1330  		ORDER BY pc.DisplayName
  1331  		LIMIT :Limit
  1332  		OFFSET :Offset
  1333  		`, map[string]interface{}{
  1334  		"TeamId": teamId,
  1335  		"Limit":  limit,
  1336  		"Offset": offset,
  1337  	})
  1338  
  1339  	if err != nil {
  1340  		return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
  1341  	}
  1342  
  1343  	return channels, nil
  1344  }
  1345  
  1346  func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) (*model.ChannelList, *model.AppError) {
  1347  	props := make(map[string]interface{})
  1348  	props["teamId"] = teamId
  1349  
  1350  	idQuery := ""
  1351  
  1352  	for index, channelId := range channelIds {
  1353  		if len(idQuery) > 0 {
  1354  			idQuery += ", "
  1355  		}
  1356  
  1357  		props["channelId"+strconv.Itoa(index)] = channelId
  1358  		idQuery += ":channelId" + strconv.Itoa(index)
  1359  	}
  1360  
  1361  	data := &model.ChannelList{}
  1362  	_, err := s.GetReplica().Select(data, `
  1363  		SELECT
  1364  			Channels.*
  1365  		FROM
  1366  			Channels
  1367  		JOIN
  1368  			PublicChannels pc ON (pc.Id = Channels.Id)
  1369  		WHERE
  1370  			pc.TeamId = :teamId
  1371  		AND pc.DeleteAt = 0
  1372  		AND pc.Id IN (`+idQuery+`)
  1373  		ORDER BY pc.DisplayName
  1374  		`, props)
  1375  
  1376  	if err != nil {
  1377  		return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError)
  1378  	}
  1379  
  1380  	if len(*data) == 0 {
  1381  		return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.not_found.app_error", nil, "", http.StatusNotFound)
  1382  	}
  1383  
  1384  	return data, nil
  1385  }
  1386  
  1387  type channelIdWithCountAndUpdateAt struct {
  1388  	Id            string
  1389  	TotalMsgCount int64
  1390  	UpdateAt      int64
  1391  }
  1392  
  1393  func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, *model.AppError) {
  1394  	var data []channelIdWithCountAndUpdateAt
  1395  	_, 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})
  1396  
  1397  	if err != nil {
  1398  		return nil, model.NewAppError("SqlChannelStore.GetChannelCounts", "store.sql_channel.get_channel_counts.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
  1399  	}
  1400  
  1401  	counts := &model.ChannelCounts{Counts: make(map[string]int64), UpdateTimes: make(map[string]int64)}
  1402  	for i := range data {
  1403  		v := data[i]
  1404  		counts.Counts[v.Id] = v.TotalMsgCount
  1405  		counts.UpdateTimes[v.Id] = v.UpdateAt
  1406  	}
  1407  
  1408  	return counts, nil
  1409  }
  1410  
  1411  func (s SqlChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) {
  1412  	data := &model.ChannelList{}
  1413  	_, err := s.GetReplica().Select(data, "SELECT * FROM Channels WHERE TeamId = :TeamId And Type != 'D' ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId})
  1414  
  1415  	if err != nil {
  1416  		// TODO: This error key would go away once this store method is migrated to return plain errors
  1417  		return nil, model.NewAppError("SqlChannelStore.GetTeamChannels", "app.channel.get_channels.get.app_error", nil, "teamId="+teamId+",  err="+err.Error(), http.StatusInternalServerError)
  1418  	}
  1419  
  1420  	if len(*data) == 0 {
  1421  		// TODO: This error key would go away once this store method is migrated to return plain errors
  1422  		return nil, model.NewAppError("SqlChannelStore.GetTeamChannels", "app.channel.get_channels.not_found.app_error", nil, "teamId="+teamId, http.StatusNotFound)
  1423  	}
  1424  
  1425  	return data, nil
  1426  }
  1427  
  1428  func (s SqlChannelStore) GetByName(teamId string, name string, allowFromCache bool) (*model.Channel, error) {
  1429  	return s.getByName(teamId, name, false, allowFromCache)
  1430  }
  1431  
  1432  func (s SqlChannelStore) GetByNames(teamId string, names []string, allowFromCache bool) ([]*model.Channel, error) {
  1433  	var channels []*model.Channel
  1434  
  1435  	if allowFromCache {
  1436  		var misses []string
  1437  		visited := make(map[string]struct{})
  1438  		for _, name := range names {
  1439  			if _, ok := visited[name]; ok {
  1440  				continue
  1441  			}
  1442  			visited[name] = struct{}{}
  1443  			var cacheItem *model.Channel
  1444  			if err := channelByNameCache.Get(teamId+name, &cacheItem); err == nil {
  1445  				channels = append(channels, cacheItem)
  1446  			} else {
  1447  				misses = append(misses, name)
  1448  			}
  1449  		}
  1450  		names = misses
  1451  	}
  1452  
  1453  	if len(names) > 0 {
  1454  		props := map[string]interface{}{}
  1455  		var namePlaceholders []string
  1456  		for _, name := range names {
  1457  			key := fmt.Sprintf("Name%v", len(namePlaceholders))
  1458  			props[key] = name
  1459  			namePlaceholders = append(namePlaceholders, ":"+key)
  1460  		}
  1461  
  1462  		var query string
  1463  		if teamId == "" {
  1464  			query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0`
  1465  		} else {
  1466  			props["TeamId"] = teamId
  1467  			query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND TeamId = :TeamId AND DeleteAt = 0`
  1468  		}
  1469  
  1470  		var dbChannels []*model.Channel
  1471  		if _, err := s.GetReplica().Select(&dbChannels, query, props); err != nil && err != sql.ErrNoRows {
  1472  			msg := fmt.Sprintf("failed to get channels with names=%v", names)
  1473  			if teamId != "" {
  1474  				msg += fmt.Sprintf("teamId=%s", teamId)
  1475  			}
  1476  			return nil, errors.Wrap(err, msg)
  1477  		}
  1478  		for _, channel := range dbChannels {
  1479  			channelByNameCache.SetWithExpiry(teamId+channel.Name, channel, CHANNEL_CACHE_DURATION)
  1480  			channels = append(channels, channel)
  1481  		}
  1482  		// Not all channels are in cache. Increment aggregate miss counter.
  1483  		if s.metrics != nil {
  1484  			s.metrics.IncrementMemCacheMissCounter("Channel By Name - Aggregate")
  1485  		}
  1486  	} else {
  1487  		// All of the channel names are in cache. Increment aggregate hit counter.
  1488  		if s.metrics != nil {
  1489  			s.metrics.IncrementMemCacheHitCounter("Channel By Name - Aggregate")
  1490  		}
  1491  	}
  1492  
  1493  	return channels, nil
  1494  }
  1495  
  1496  func (s SqlChannelStore) GetByNameIncludeDeleted(teamId string, name string, allowFromCache bool) (*model.Channel, error) {
  1497  	return s.getByName(teamId, name, true, allowFromCache)
  1498  }
  1499  
  1500  func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bool, allowFromCache bool) (*model.Channel, error) {
  1501  	var query string
  1502  	if includeDeleted {
  1503  		query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name"
  1504  	} else {
  1505  		query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt = 0"
  1506  	}
  1507  	channel := model.Channel{}
  1508  
  1509  	if allowFromCache {
  1510  		var cacheItem *model.Channel
  1511  		if err := channelByNameCache.Get(teamId+name, &cacheItem); err == nil {
  1512  			if s.metrics != nil {
  1513  				s.metrics.IncrementMemCacheHitCounter("Channel By Name")
  1514  			}
  1515  			return cacheItem, nil
  1516  		}
  1517  		if s.metrics != nil {
  1518  			s.metrics.IncrementMemCacheMissCounter("Channel By Name")
  1519  		}
  1520  	}
  1521  
  1522  	if err := s.GetReplica().SelectOne(&channel, query, map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil {
  1523  		if err == sql.ErrNoRows {
  1524  			return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s&Name=%s", teamId, name))
  1525  		}
  1526  		return nil, errors.Wrapf(err, "failed to find channel with TeamId=%s and Name=%s", teamId, name)
  1527  	}
  1528  
  1529  	channelByNameCache.SetWithExpiry(teamId+name, &channel, CHANNEL_CACHE_DURATION)
  1530  	return &channel, nil
  1531  }
  1532  
  1533  func (s SqlChannelStore) GetDeletedByName(teamId string, name string) (*model.Channel, error) {
  1534  	channel := model.Channel{}
  1535  
  1536  	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 {
  1537  		if err == sql.ErrNoRows {
  1538  			return nil, store.NewErrNotFound("Channel", fmt.Sprintf("name=%s", name))
  1539  		}
  1540  		return nil, errors.Wrapf(err, "failed to get channel by teamId=%s and name=%s", teamId, name)
  1541  	}
  1542  
  1543  	return &channel, nil
  1544  }
  1545  
  1546  func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int, userId string) (*model.ChannelList, error) {
  1547  	channels := &model.ChannelList{}
  1548  
  1549  	query := `
  1550  		SELECT * FROM Channels
  1551  		WHERE (TeamId = :TeamId OR TeamId = '')
  1552  		AND DeleteAt != 0
  1553  		AND Type != 'P'
  1554  		UNION
  1555  			SELECT * FROM Channels
  1556  			WHERE (TeamId = :TeamId OR TeamId = '')
  1557  			AND DeleteAt != 0
  1558  			AND Type = 'P'
  1559  			AND Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId)
  1560  		ORDER BY DisplayName LIMIT :Limit OFFSET :Offset
  1561  	`
  1562  
  1563  	if _, err := s.GetReplica().Select(channels, query, map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset, "UserId": userId}); err != nil {
  1564  		if err == sql.ErrNoRows {
  1565  			return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s,UserId=%s", teamId, userId))
  1566  		}
  1567  		return nil, errors.Wrapf(err, "failed to get deleted channels with TeamId=%s and UserId=%s", teamId, userId)
  1568  	}
  1569  
  1570  	return channels, nil
  1571  }
  1572  
  1573  var CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
  1574  	SELECT
  1575  		ChannelMembers.*,
  1576  		TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole,
  1577  		TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
  1578  		TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
  1579  		ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole,
  1580  		ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
  1581  		ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
  1582  	FROM
  1583  		ChannelMembers
  1584  	INNER JOIN
  1585  		Channels ON ChannelMembers.ChannelId = Channels.Id
  1586  	LEFT JOIN
  1587  		Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
  1588  	LEFT JOIN
  1589  		Teams ON Channels.TeamId = Teams.Id
  1590  	LEFT JOIN
  1591  		Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
  1592  `
  1593  
  1594  func (s SqlChannelStore) SaveMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, *model.AppError) {
  1595  	for _, member := range members {
  1596  		defer s.InvalidateAllChannelMembersForUser(member.UserId)
  1597  	}
  1598  
  1599  	transaction, err := s.GetMaster().Begin()
  1600  	if err != nil {
  1601  		return nil, model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  1602  	}
  1603  	defer finalizeTransaction(transaction)
  1604  
  1605  	newMembers, err := s.saveMultipleMembersT(transaction, members)
  1606  	if err != nil { // TODO: this will go away once SaveMultipleMembers is migrated too.
  1607  		var cErr *store.ErrConflict
  1608  		var appErr *model.AppError
  1609  		switch {
  1610  		case errors.As(err, &cErr):
  1611  			switch cErr.Resource {
  1612  			case "ChannelMembers":
  1613  				return nil, model.NewAppError("CreateChannel", "store.sql_channel.save_member.exists.app_error", nil, cErr.Error(), http.StatusBadRequest)
  1614  			}
  1615  		case errors.As(err, &appErr): // in case we haven't converted to plain error.
  1616  			return nil, appErr
  1617  		default: // last fallback in case it doesn't map to an existing app error.
  1618  			// TODO: This error key would go away once this store method is migrated to return plain errors
  1619  			return nil, model.NewAppError("CreateDirectChannel", "app.channel.create_direct_channel.internal_error", nil, err.Error(), http.StatusInternalServerError)
  1620  		}
  1621  	}
  1622  
  1623  	if err := transaction.Commit(); err != nil {
  1624  		return nil, model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  1625  	}
  1626  
  1627  	return newMembers, nil
  1628  }
  1629  
  1630  func (s SqlChannelStore) SaveMember(member *model.ChannelMember) (*model.ChannelMember, *model.AppError) {
  1631  	newMembers, appErr := s.SaveMultipleMembers([]*model.ChannelMember{member})
  1632  	if appErr != nil {
  1633  		return nil, appErr
  1634  	}
  1635  	return newMembers[0], nil
  1636  }
  1637  
  1638  func (s SqlChannelStore) saveMultipleMembersT(transaction *gorp.Transaction, members []*model.ChannelMember) ([]*model.ChannelMember, error) {
  1639  	newChannelMembers := map[string]int{}
  1640  	users := map[string]bool{}
  1641  	for _, member := range members {
  1642  		if val, ok := newChannelMembers[member.ChannelId]; val < 1 || !ok {
  1643  			newChannelMembers[member.ChannelId] = 1
  1644  		} else {
  1645  			newChannelMembers[member.ChannelId]++
  1646  		}
  1647  		users[member.UserId] = true
  1648  
  1649  		member.PreSave()
  1650  		if err := member.IsValid(); err != nil { // TODO: this needs to return plain error in v6.
  1651  			return nil, err
  1652  		}
  1653  	}
  1654  
  1655  	channels := []string{}
  1656  	for channel := range newChannelMembers {
  1657  		channels = append(channels, channel)
  1658  	}
  1659  
  1660  	defaultChannelRolesByChannel := map[string]struct {
  1661  		Id    string
  1662  		Guest sql.NullString
  1663  		User  sql.NullString
  1664  		Admin sql.NullString
  1665  	}{}
  1666  
  1667  	channelRolesQuery := s.getQueryBuilder().
  1668  		Select(
  1669  			"Channels.Id as Id",
  1670  			"ChannelScheme.DefaultChannelGuestRole as Guest",
  1671  			"ChannelScheme.DefaultChannelUserRole as User",
  1672  			"ChannelScheme.DefaultChannelAdminRole as Admin",
  1673  		).
  1674  		From("Channels").
  1675  		LeftJoin("Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id").
  1676  		Where(sq.Eq{"Channels.Id": channels})
  1677  
  1678  	channelRolesSql, channelRolesArgs, err := channelRolesQuery.ToSql()
  1679  	if err != nil {
  1680  		return nil, errors.Wrap(err, "channel_roles_tosql")
  1681  	}
  1682  
  1683  	var defaultChannelsRoles []struct {
  1684  		Id    string
  1685  		Guest sql.NullString
  1686  		User  sql.NullString
  1687  		Admin sql.NullString
  1688  	}
  1689  	_, err = s.GetMaster().Select(&defaultChannelsRoles, channelRolesSql, channelRolesArgs...)
  1690  	if err != nil {
  1691  		return nil, errors.Wrap(err, "default_channel_roles_select")
  1692  	}
  1693  
  1694  	for _, defaultRoles := range defaultChannelsRoles {
  1695  		defaultChannelRolesByChannel[defaultRoles.Id] = defaultRoles
  1696  	}
  1697  
  1698  	defaultTeamRolesByChannel := map[string]struct {
  1699  		Id    string
  1700  		Guest sql.NullString
  1701  		User  sql.NullString
  1702  		Admin sql.NullString
  1703  	}{}
  1704  
  1705  	teamRolesQuery := s.getQueryBuilder().
  1706  		Select(
  1707  			"Channels.Id as Id",
  1708  			"TeamScheme.DefaultChannelGuestRole as Guest",
  1709  			"TeamScheme.DefaultChannelUserRole as User",
  1710  			"TeamScheme.DefaultChannelAdminRole as Admin",
  1711  		).
  1712  		From("Channels").
  1713  		LeftJoin("Teams ON Teams.Id = Channels.TeamId").
  1714  		LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id").
  1715  		Where(sq.Eq{"Channels.Id": channels})
  1716  
  1717  	teamRolesSql, teamRolesArgs, err := teamRolesQuery.ToSql()
  1718  	if err != nil {
  1719  		return nil, errors.Wrap(err, "team_roles_tosql")
  1720  	}
  1721  
  1722  	var defaultTeamsRoles []struct {
  1723  		Id    string
  1724  		Guest sql.NullString
  1725  		User  sql.NullString
  1726  		Admin sql.NullString
  1727  	}
  1728  	_, err = s.GetMaster().Select(&defaultTeamsRoles, teamRolesSql, teamRolesArgs...)
  1729  	if err != nil {
  1730  		return nil, errors.Wrap(err, "default_team_roles_select")
  1731  	}
  1732  
  1733  	for _, defaultRoles := range defaultTeamsRoles {
  1734  		defaultTeamRolesByChannel[defaultRoles.Id] = defaultRoles
  1735  	}
  1736  
  1737  	query := s.getQueryBuilder().Insert("ChannelMembers").Columns(channelMemberSliceColumns()...)
  1738  	for _, member := range members {
  1739  		query = query.Values(channelMemberToSlice(member)...)
  1740  	}
  1741  
  1742  	sql, args, err := query.ToSql()
  1743  	if err != nil {
  1744  		return nil, errors.Wrap(err, "channel_members_tosql")
  1745  	}
  1746  
  1747  	if _, err := s.GetMaster().Exec(sql, args...); err != nil {
  1748  		if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey", "PRIMARY"}) {
  1749  			return nil, store.NewErrConflict("ChannelMembers", err, "")
  1750  		}
  1751  		return nil, errors.Wrap(err, "channel_members_save")
  1752  	}
  1753  
  1754  	newMembers := []*model.ChannelMember{}
  1755  	for _, member := range members {
  1756  		defaultTeamGuestRole := defaultTeamRolesByChannel[member.ChannelId].Guest.String
  1757  		defaultTeamUserRole := defaultTeamRolesByChannel[member.ChannelId].User.String
  1758  		defaultTeamAdminRole := defaultTeamRolesByChannel[member.ChannelId].Admin.String
  1759  		defaultChannelGuestRole := defaultChannelRolesByChannel[member.ChannelId].Guest.String
  1760  		defaultChannelUserRole := defaultChannelRolesByChannel[member.ChannelId].User.String
  1761  		defaultChannelAdminRole := defaultChannelRolesByChannel[member.ChannelId].Admin.String
  1762  		rolesResult := getChannelRoles(
  1763  			member.SchemeGuest, member.SchemeUser, member.SchemeAdmin,
  1764  			defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole,
  1765  			defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole,
  1766  			strings.Fields(member.ExplicitRoles),
  1767  		)
  1768  		newMember := *member
  1769  		newMember.SchemeGuest = rolesResult.schemeGuest
  1770  		newMember.SchemeUser = rolesResult.schemeUser
  1771  		newMember.SchemeAdmin = rolesResult.schemeAdmin
  1772  		newMember.Roles = strings.Join(rolesResult.roles, " ")
  1773  		newMember.ExplicitRoles = strings.Join(rolesResult.explicitRoles, " ")
  1774  		newMembers = append(newMembers, &newMember)
  1775  	}
  1776  	return newMembers, nil
  1777  }
  1778  
  1779  func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *model.ChannelMember) (*model.ChannelMember, error) {
  1780  	members, err := s.saveMultipleMembersT(transaction, []*model.ChannelMember{member})
  1781  	if err != nil {
  1782  		return nil, err
  1783  	}
  1784  	return members[0], nil
  1785  }
  1786  
  1787  func (s SqlChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, *model.AppError) {
  1788  	for _, member := range members {
  1789  		member.PreUpdate()
  1790  
  1791  		if err := member.IsValid(); err != nil {
  1792  			return nil, err
  1793  		}
  1794  	}
  1795  
  1796  	var transaction *gorp.Transaction
  1797  	var err error
  1798  
  1799  	if transaction, err = s.GetMaster().Begin(); err != nil {
  1800  		return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  1801  	}
  1802  	defer finalizeTransaction(transaction)
  1803  
  1804  	updatedMembers := []*model.ChannelMember{}
  1805  	for _, member := range members {
  1806  		if _, err := transaction.Update(NewChannelMemberFromModel(member)); err != nil {
  1807  			return nil, model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError)
  1808  		}
  1809  
  1810  		// TODO: Get this out of the transaction when is possible
  1811  		var dbMember channelMemberWithSchemeRoles
  1812  		if err := transaction.SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil {
  1813  			if err == sql.ErrNoRows {
  1814  				return nil, model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
  1815  			}
  1816  			return nil, model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError)
  1817  		}
  1818  		updatedMembers = append(updatedMembers, dbMember.ToModel())
  1819  	}
  1820  
  1821  	if err := transaction.Commit(); err != nil {
  1822  		return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  1823  	}
  1824  	return updatedMembers, nil
  1825  }
  1826  
  1827  func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) (*model.ChannelMember, *model.AppError) {
  1828  	updatedMembers, err := s.UpdateMultipleMembers([]*model.ChannelMember{member})
  1829  	if err != nil {
  1830  		return nil, err
  1831  	}
  1832  	return updatedMembers[0], nil
  1833  }
  1834  
  1835  func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) (*model.ChannelMembers, *model.AppError) {
  1836  	var dbMembers channelMemberWithSchemeRolesList
  1837  	_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset})
  1838  	if err != nil {
  1839  		return nil, model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError)
  1840  	}
  1841  
  1842  	return dbMembers.ToModel(), nil
  1843  }
  1844  
  1845  func (s SqlChannelStore) GetChannelMembersTimezones(channelId string) ([]model.StringMap, *model.AppError) {
  1846  	var dbMembersTimezone []model.StringMap
  1847  	_, err := s.GetReplica().Select(&dbMembersTimezone, `
  1848  		SELECT
  1849  			Users.Timezone
  1850  		FROM
  1851  			ChannelMembers
  1852  		LEFT JOIN
  1853  			Users  ON ChannelMembers.UserId = Id
  1854  		WHERE ChannelId = :ChannelId
  1855  	`, map[string]interface{}{"ChannelId": channelId})
  1856  
  1857  	if err != nil {
  1858  		return nil, model.NewAppError("SqlChannelStore.GetChannelMembersTimezones", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError)
  1859  	}
  1860  
  1861  	return dbMembersTimezone, nil
  1862  }
  1863  
  1864  func (s SqlChannelStore) GetMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) {
  1865  	var dbMember channelMemberWithSchemeRoles
  1866  
  1867  	if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
  1868  		if err == sql.ErrNoRows {
  1869  			return nil, model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound)
  1870  		}
  1871  		return nil, model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError)
  1872  	}
  1873  
  1874  	return dbMember.ToModel(), nil
  1875  }
  1876  
  1877  func (s SqlChannelStore) InvalidateAllChannelMembersForUser(userId string) {
  1878  	allChannelMembersForUserCache.Remove(userId)
  1879  	allChannelMembersForUserCache.Remove(userId + "_deleted")
  1880  	if s.metrics != nil {
  1881  		s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Remove by UserId")
  1882  	}
  1883  }
  1884  
  1885  func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string) bool {
  1886  	var ids map[string]string
  1887  	if err := allChannelMembersForUserCache.Get(userId, &ids); err == nil {
  1888  		if s.metrics != nil {
  1889  			s.metrics.IncrementMemCacheHitCounter("All Channel Members for User")
  1890  		}
  1891  		if _, ok := ids[channelId]; ok {
  1892  			return true
  1893  		}
  1894  		return false
  1895  	}
  1896  
  1897  	if s.metrics != nil {
  1898  		s.metrics.IncrementMemCacheMissCounter("All Channel Members for User")
  1899  	}
  1900  
  1901  	ids, err := s.GetAllChannelMembersForUser(userId, true, false)
  1902  	if err != nil {
  1903  		mlog.Error("Error getting all channel members for user", mlog.Err(err))
  1904  		return false
  1905  	}
  1906  
  1907  	if _, ok := ids[channelId]; ok {
  1908  		return true
  1909  	}
  1910  
  1911  	return false
  1912  }
  1913  
  1914  func (s SqlChannelStore) GetMemberForPost(postId string, userId string) (*model.ChannelMember, *model.AppError) {
  1915  	var dbMember channelMemberWithSchemeRoles
  1916  	query := `
  1917  		SELECT
  1918  			ChannelMembers.*,
  1919  			TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole,
  1920  			TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
  1921  			TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
  1922  			ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole,
  1923  			ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
  1924  			ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
  1925  		FROM
  1926  			ChannelMembers
  1927  		INNER JOIN
  1928  			Posts ON ChannelMembers.ChannelId = Posts.ChannelId
  1929  		INNER JOIN
  1930  			Channels ON ChannelMembers.ChannelId = Channels.Id
  1931  		LEFT JOIN
  1932  			Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id
  1933  		LEFT JOIN
  1934  			Teams ON Channels.TeamId = Teams.Id
  1935  		LEFT JOIN
  1936  			Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
  1937  		WHERE
  1938  			ChannelMembers.UserId = :UserId
  1939  		AND
  1940  			Posts.Id = :PostId`
  1941  	if err := s.GetReplica().SelectOne(&dbMember, query, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil {
  1942  		return nil, model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError)
  1943  	}
  1944  	return dbMember.ToModel(), nil
  1945  }
  1946  
  1947  func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) (map[string]string, *model.AppError) {
  1948  	cache_key := userId
  1949  	if includeDeleted {
  1950  		cache_key += "_deleted"
  1951  	}
  1952  	if allowFromCache {
  1953  		var ids map[string]string
  1954  		if err := allChannelMembersForUserCache.Get(cache_key, &ids); err == nil {
  1955  			if s.metrics != nil {
  1956  				s.metrics.IncrementMemCacheHitCounter("All Channel Members for User")
  1957  			}
  1958  			return ids, nil
  1959  		}
  1960  	}
  1961  
  1962  	if s.metrics != nil {
  1963  		s.metrics.IncrementMemCacheMissCounter("All Channel Members for User")
  1964  	}
  1965  
  1966  	failure := func(err error) *model.AppError {
  1967  		// TODO: This error key would go away once this store method is migrated to return plain errors
  1968  		return model.NewAppError(
  1969  			"SqlChannelStore.GetAllChannelMembersForUser",
  1970  			"app.channel.get_channels.get.app_error",
  1971  			nil,
  1972  			"userId="+userId+", err="+err.Error(),
  1973  			http.StatusInternalServerError,
  1974  		)
  1975  	}
  1976  
  1977  	query := s.getQueryBuilder().
  1978  		Select(`
  1979  				ChannelMembers.ChannelId, ChannelMembers.Roles, ChannelMembers.SchemeGuest,
  1980  				ChannelMembers.SchemeUser, ChannelMembers.SchemeAdmin,
  1981  				TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole,
  1982  				TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
  1983  				TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
  1984  				ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole,
  1985  				ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole,
  1986  				ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole
  1987  		`).
  1988  		From("ChannelMembers").
  1989  		Join("Channels ON ChannelMembers.ChannelId = Channels.Id").
  1990  		LeftJoin("Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id").
  1991  		LeftJoin("Teams ON Channels.TeamId = Teams.Id").
  1992  		LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id").
  1993  		Where(sq.Eq{"ChannelMembers.UserId": userId})
  1994  	if !includeDeleted {
  1995  		query = query.Where(sq.Eq{"Channels.DeleteAt": 0})
  1996  	}
  1997  	queryString, args, err := query.ToSql()
  1998  	if err != nil {
  1999  		return nil, failure(err)
  2000  	}
  2001  
  2002  	rows, err := s.GetReplica().Db.Query(queryString, args...)
  2003  	if err != nil {
  2004  		return nil, failure(err)
  2005  	}
  2006  
  2007  	var data allChannelMembers
  2008  	defer rows.Close()
  2009  	for rows.Next() {
  2010  		var cm allChannelMember
  2011  		err = rows.Scan(
  2012  			&cm.ChannelId, &cm.Roles, &cm.SchemeGuest, &cm.SchemeUser,
  2013  			&cm.SchemeAdmin, &cm.TeamSchemeDefaultGuestRole, &cm.TeamSchemeDefaultUserRole,
  2014  			&cm.TeamSchemeDefaultAdminRole, &cm.ChannelSchemeDefaultGuestRole,
  2015  			&cm.ChannelSchemeDefaultUserRole, &cm.ChannelSchemeDefaultAdminRole,
  2016  		)
  2017  		if err != nil {
  2018  			return nil, failure(err)
  2019  		}
  2020  		data = append(data, cm)
  2021  	}
  2022  	if err = rows.Err(); err != nil {
  2023  		return nil, failure(err)
  2024  	}
  2025  	ids := data.ToMapStringString()
  2026  
  2027  	if allowFromCache {
  2028  		allChannelMembersForUserCache.SetWithExpiry(cache_key, ids, ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_DURATION)
  2029  	}
  2030  	return ids, nil
  2031  }
  2032  
  2033  func (s SqlChannelStore) InvalidateCacheForChannelMembersNotifyProps(channelId string) {
  2034  	allChannelMembersNotifyPropsForChannelCache.Remove(channelId)
  2035  	if s.metrics != nil {
  2036  		s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Remove by ChannelId")
  2037  	}
  2038  }
  2039  
  2040  type allChannelMemberNotifyProps struct {
  2041  	UserId      string
  2042  	NotifyProps model.StringMap
  2043  }
  2044  
  2045  func (s SqlChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) (map[string]model.StringMap, *model.AppError) {
  2046  	if allowFromCache {
  2047  		var cacheItem map[string]model.StringMap
  2048  		if err := allChannelMembersNotifyPropsForChannelCache.Get(channelId, &cacheItem); err == nil {
  2049  			if s.metrics != nil {
  2050  				s.metrics.IncrementMemCacheHitCounter("All Channel Members Notify Props for Channel")
  2051  			}
  2052  			return cacheItem, nil
  2053  		}
  2054  	}
  2055  
  2056  	if s.metrics != nil {
  2057  		s.metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel")
  2058  	}
  2059  
  2060  	var data []allChannelMemberNotifyProps
  2061  	_, err := s.GetReplica().Select(&data, `
  2062  		SELECT UserId, NotifyProps
  2063  		FROM ChannelMembers
  2064  		WHERE ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId})
  2065  
  2066  	if err != nil {
  2067  		return nil, model.NewAppError("SqlChannelStore.GetAllChannelMembersPropsForChannel", "store.sql_channel.get_members.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError)
  2068  	}
  2069  
  2070  	props := make(map[string]model.StringMap)
  2071  	for i := range data {
  2072  		props[data[i].UserId] = data[i].NotifyProps
  2073  	}
  2074  
  2075  	allChannelMembersNotifyPropsForChannelCache.SetWithExpiry(channelId, props, ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_DURATION)
  2076  
  2077  	return props, nil
  2078  }
  2079  
  2080  func (s SqlChannelStore) InvalidateMemberCount(channelId string) {
  2081  }
  2082  
  2083  func (s SqlChannelStore) GetMemberCountFromCache(channelId string) int64 {
  2084  	count, _ := s.GetMemberCount(channelId, true)
  2085  	return count
  2086  }
  2087  
  2088  func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) (int64, *model.AppError) {
  2089  	count, err := s.GetReplica().SelectInt(`
  2090  		SELECT
  2091  			count(*)
  2092  		FROM
  2093  			ChannelMembers,
  2094  			Users
  2095  		WHERE
  2096  			ChannelMembers.UserId = Users.Id
  2097  			AND ChannelMembers.ChannelId = :ChannelId
  2098  			AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId})
  2099  	if err != nil {
  2100  		return 0, model.NewAppError("SqlChannelStore.GetMemberCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2101  	}
  2102  
  2103  	return count, nil
  2104  }
  2105  
  2106  // GetMemberCountsByGroup returns a slice of ChannelMemberCountByGroup for a given channel
  2107  // which contains the number of channel members for each group and optionally the number of unique timezones present for each group in the channel
  2108  func (s SqlChannelStore) GetMemberCountsByGroup(channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError) {
  2109  	selectStr := "GroupMembers.GroupId, COUNT(ChannelMembers.UserId) AS ChannelMemberCount"
  2110  
  2111  	if includeTimezones {
  2112  		// Length of default timezone (len {"automaticTimezone":"","manualTimezone":"","useAutomaticTimezone":"true"})
  2113  		defaultTimezoneLength := `74`
  2114  
  2115  		// Beginning and end of the value for the automatic and manual timezones respectively
  2116  		autoTimezone := `LOCATE(':', Users.Timezone) + 2`
  2117  		autoTimezoneEnd := `LOCATE(',', Users.Timezone) - LOCATE(':', Users.Timezone) - 3`
  2118  		manualTimezone := `LOCATE(',', Users.Timezone) + 19`
  2119  		manualTimezoneEnd := `LOCATE('useAutomaticTimezone', Users.Timezone) - 22 - LOCATE(',', Users.Timezone)`
  2120  
  2121  		if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  2122  			autoTimezone = `POSITION(':' IN Users.Timezone) + 2`
  2123  			autoTimezoneEnd = `POSITION(',' IN Users.Timezone) - POSITION(':' IN Users.Timezone) - 3`
  2124  			manualTimezone = `POSITION(',' IN Users.Timezone) + 19`
  2125  			manualTimezoneEnd = `POSITION('useAutomaticTimezone' IN Users.Timezone) - 22 - POSITION(',' IN Users.Timezone)`
  2126  		}
  2127  
  2128  		selectStr = `
  2129  			GroupMembers.GroupId,
  2130  			COUNT(ChannelMembers.UserId) AS ChannelMemberCount,
  2131  			COUNT(DISTINCT
  2132  				(
  2133  					CASE WHEN Timezone like '%"useAutomaticTimezone":"true"}' AND LENGTH(Timezone) > ` + defaultTimezoneLength + `
  2134  					THEN
  2135  					SUBSTRING(
  2136  						Timezone
  2137  						FROM ` + autoTimezone + `
  2138  						FOR ` + autoTimezoneEnd + `
  2139  					)
  2140  					WHEN Timezone like '%"useAutomaticTimezone":"false"}' AND LENGTH(Timezone) > ` + defaultTimezoneLength + `
  2141  					THEN
  2142  						SUBSTRING(
  2143  						Timezone
  2144  						FROM ` + manualTimezone + `
  2145  						FOR ` + manualTimezoneEnd + `
  2146  					)
  2147  					END
  2148  				)
  2149  			) AS ChannelMemberTimezonesCount
  2150  		`
  2151  	}
  2152  
  2153  	query := s.getQueryBuilder().
  2154  		Select(selectStr).
  2155  		From("ChannelMembers").
  2156  		Join("GroupMembers ON GroupMembers.UserId = ChannelMembers.UserId")
  2157  
  2158  	if includeTimezones {
  2159  		query = query.Join("Users ON Users.Id = GroupMembers.UserId")
  2160  	}
  2161  
  2162  	query = query.Where(sq.Eq{"ChannelMembers.ChannelId": channelID}).GroupBy("GroupMembers.GroupId")
  2163  
  2164  	queryString, args, err := query.ToSql()
  2165  	if err != nil {
  2166  		return nil, model.NewAppError("SqlChannelStore.GetMemberCountsByGroup", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
  2167  	}
  2168  	var data []*model.ChannelMemberCountByGroup
  2169  	if _, err = s.GetReplica().Select(&data, queryString, args...); err != nil {
  2170  		return nil, model.NewAppError("SqlChannelStore.GetMemberCountsByGroup", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelID+", "+err.Error(), http.StatusInternalServerError)
  2171  	}
  2172  
  2173  	return data, nil
  2174  }
  2175  
  2176  func (s SqlChannelStore) InvalidatePinnedPostCount(channelId string) {
  2177  }
  2178  
  2179  func (s SqlChannelStore) GetPinnedPostCount(channelId string, allowFromCache bool) (int64, *model.AppError) {
  2180  	count, err := s.GetReplica().SelectInt(`
  2181  		SELECT count(*)
  2182  			FROM Posts
  2183  		WHERE
  2184  			IsPinned = true
  2185  			AND ChannelId = :ChannelId
  2186  			AND DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId})
  2187  
  2188  	if err != nil {
  2189  		return 0, model.NewAppError("SqlChannelStore.GetPinnedPostCount", "store.sql_channel.get_pinnedpost_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2190  	}
  2191  
  2192  	return count, nil
  2193  }
  2194  
  2195  func (s SqlChannelStore) InvalidateGuestCount(channelId string) {
  2196  }
  2197  
  2198  func (s SqlChannelStore) GetGuestCount(channelId string, allowFromCache bool) (int64, *model.AppError) {
  2199  	count, err := s.GetReplica().SelectInt(`
  2200  		SELECT
  2201  			count(*)
  2202  		FROM
  2203  			ChannelMembers,
  2204  			Users
  2205  		WHERE
  2206  			ChannelMembers.UserId = Users.Id
  2207  			AND ChannelMembers.ChannelId = :ChannelId
  2208  			AND ChannelMembers.SchemeGuest = TRUE
  2209  			AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId})
  2210  	if err != nil {
  2211  		return 0, model.NewAppError("SqlChannelStore.GetGuestCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2212  	}
  2213  	return count, nil
  2214  }
  2215  
  2216  func (s SqlChannelStore) RemoveMembers(channelId string, userIds []string) *model.AppError {
  2217  	query := s.getQueryBuilder().
  2218  		Delete("ChannelMembers").
  2219  		Where(sq.Eq{"ChannelId": channelId}).
  2220  		Where(sq.Eq{"UserId": userIds})
  2221  	sql, args, err := query.ToSql()
  2222  	if err != nil {
  2223  		return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2224  	}
  2225  	_, err = s.GetMaster().Exec(sql, args...)
  2226  	if err != nil {
  2227  		return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2228  	}
  2229  
  2230  	// cleanup sidebarchannels table if the user is no longer a member of that channel
  2231  	sql, args, err = s.getQueryBuilder().
  2232  		Delete("SidebarChannels").
  2233  		Where(sq.And{
  2234  			sq.Eq{"ChannelId": channelId},
  2235  			sq.Eq{"UserId": userIds},
  2236  		}).ToSql()
  2237  	if err != nil {
  2238  		return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2239  	}
  2240  	_, err = s.GetMaster().Exec(sql, args...)
  2241  	if err != nil {
  2242  		return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2243  	}
  2244  	return nil
  2245  }
  2246  
  2247  func (s SqlChannelStore) RemoveMember(channelId string, userId string) *model.AppError {
  2248  	return s.RemoveMembers(channelId, []string{userId})
  2249  }
  2250  
  2251  func (s SqlChannelStore) RemoveAllDeactivatedMembers(channelId string) *model.AppError {
  2252  	query := `
  2253  		DELETE
  2254  		FROM
  2255  			ChannelMembers
  2256  		WHERE
  2257  			UserId IN (
  2258  				SELECT
  2259  					Id
  2260  				FROM
  2261  					Users
  2262  				WHERE
  2263  					Users.DeleteAt != 0
  2264  			)
  2265  		AND
  2266  			ChannelMembers.ChannelId = :ChannelId
  2267  	`
  2268  
  2269  	_, err := s.GetMaster().Exec(query, map[string]interface{}{"ChannelId": channelId})
  2270  	if err != nil {
  2271  		return model.NewAppError("SqlChannelStore.RemoveAllDeactivatedMembers", "store.sql_channel.remove_all_deactivated_members.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
  2272  	}
  2273  	return nil
  2274  }
  2275  
  2276  func (s SqlChannelStore) PermanentDeleteMembersByUser(userId string) *model.AppError {
  2277  	if _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
  2278  		return model.NewAppError("SqlChannelStore.ChannelPermanentDeleteMembersByUser", "store.sql_channel.permanent_delete_members_by_user.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
  2279  	}
  2280  	return nil
  2281  }
  2282  
  2283  func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string) (map[string]int64, *model.AppError) {
  2284  	keys, props := MapStringsToQueryParams(channelIds, "Channel")
  2285  	props["UserId"] = userId
  2286  
  2287  	var lastPostAtTimes []struct {
  2288  		Id            string
  2289  		LastPostAt    int64
  2290  		TotalMsgCount int64
  2291  	}
  2292  
  2293  	query := `SELECT Id, LastPostAt, TotalMsgCount FROM Channels WHERE Id IN ` + keys
  2294  	// TODO: use a CTE for mysql too when version 8 becomes the minimum supported version.
  2295  	if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  2296  		query = `WITH c AS ( ` + query + `),
  2297  	updated AS (
  2298  	UPDATE
  2299  		ChannelMembers cm
  2300  	SET
  2301  		MentionCount = 0,
  2302  		MsgCount = greatest(cm.MsgCount, c.TotalMsgCount),
  2303  		LastViewedAt = greatest(cm.LastViewedAt, c.LastPostAt),
  2304  		LastUpdateAt = greatest(cm.LastViewedAt, c.LastPostAt)
  2305  	FROM c
  2306  		WHERE cm.UserId = :UserId
  2307  		AND c.Id=cm.ChannelId
  2308  )
  2309  	SELECT Id, LastPostAt FROM c`
  2310  	}
  2311  
  2312  	_, err := s.GetMaster().Select(&lastPostAtTimes, query, props)
  2313  	if err != nil || len(lastPostAtTimes) == 0 {
  2314  		status := http.StatusInternalServerError
  2315  		var extra string
  2316  		if err == nil {
  2317  			status = http.StatusBadRequest
  2318  			extra = "No channels found"
  2319  		} else {
  2320  			extra = err.Error()
  2321  		}
  2322  
  2323  		return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAt",
  2324  			"store.sql_channel.update_last_viewed_at.app_error",
  2325  			nil,
  2326  			"channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+extra,
  2327  			status)
  2328  	}
  2329  
  2330  	times := map[string]int64{}
  2331  	if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  2332  		for _, t := range lastPostAtTimes {
  2333  			times[t.Id] = t.LastPostAt
  2334  		}
  2335  		return times, nil
  2336  	}
  2337  
  2338  	msgCountQuery := ""
  2339  	lastViewedQuery := ""
  2340  	for index, t := range lastPostAtTimes {
  2341  		times[t.Id] = t.LastPostAt
  2342  
  2343  		props["msgCount"+strconv.Itoa(index)] = t.TotalMsgCount
  2344  		msgCountQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(MsgCount, :msgCount%d) ", index, index)
  2345  
  2346  		props["lastViewed"+strconv.Itoa(index)] = t.LastPostAt
  2347  		lastViewedQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(LastViewedAt, :lastViewed%d) ", index, index)
  2348  
  2349  		props["channelId"+strconv.Itoa(index)] = t.Id
  2350  	}
  2351  
  2352  	updateQuery := `UPDATE
  2353  			ChannelMembers
  2354  		SET
  2355  			MentionCount = 0,
  2356  			MsgCount = CASE ChannelId ` + msgCountQuery + ` END,
  2357  			LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END,
  2358  			LastUpdateAt = LastViewedAt
  2359  		WHERE
  2360  				UserId = :UserId
  2361  				AND ChannelId IN ` + keys
  2362  
  2363  	if _, err := s.GetMaster().Exec(updateQuery, props); err != nil {
  2364  		return nil, 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)
  2365  	}
  2366  
  2367  	return times, nil
  2368  }
  2369  
  2370  // CountPostsAfter returns the number of posts in the given channel created after but not including the given timestamp. If given a non-empty user ID, only counts posts made by that user.
  2371  func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) {
  2372  	joinLeavePostTypes, params := MapStringsToQueryParams([]string{
  2373  		// These types correspond to the ones checked by Post.IsJoinLeaveMessage
  2374  		model.POST_JOIN_LEAVE,
  2375  		model.POST_ADD_REMOVE,
  2376  		model.POST_JOIN_CHANNEL,
  2377  		model.POST_LEAVE_CHANNEL,
  2378  		model.POST_JOIN_TEAM,
  2379  		model.POST_LEAVE_TEAM,
  2380  		model.POST_ADD_TO_CHANNEL,
  2381  		model.POST_REMOVE_FROM_CHANNEL,
  2382  		model.POST_ADD_TO_TEAM,
  2383  		model.POST_REMOVE_FROM_TEAM,
  2384  	}, "PostType")
  2385  
  2386  	query := `
  2387  	SELECT count(*)
  2388  	FROM Posts
  2389  	WHERE
  2390  		ChannelId = :ChannelId
  2391  		AND CreateAt > :CreateAt
  2392  		AND Type NOT IN ` + joinLeavePostTypes + `
  2393  		AND DeleteAt = 0
  2394  	`
  2395  
  2396  	params["ChannelId"] = channelId
  2397  	params["CreateAt"] = timestamp
  2398  
  2399  	if userId != "" {
  2400  		query += " AND UserId = :UserId"
  2401  		params["UserId"] = userId
  2402  	}
  2403  
  2404  	unread, err := s.GetReplica().SelectInt(query, params)
  2405  	if err != nil {
  2406  		return 0, model.NewAppError("SqlChannelStore.CountPostsAfter", "store.sql_channel.count_posts_since.app_error", nil, fmt.Sprintf("channel_id=%s, timestamp=%d, err=%s", channelId, timestamp, err), http.StatusInternalServerError)
  2407  	}
  2408  	return int(unread), nil
  2409  }
  2410  
  2411  // UpdateLastViewedAtPost updates a ChannelMember as if the user last read the channel at the time of the given post.
  2412  // If the provided mentionCount is -1, the given post and all posts after it are considered to be mentions. Returns
  2413  // an updated model.ChannelUnreadAt that can be returned to the client.
  2414  func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int) (*model.ChannelUnreadAt, *model.AppError) {
  2415  	unreadDate := unreadPost.CreateAt - 1
  2416  
  2417  	unread, appErr := s.CountPostsAfter(unreadPost.ChannelId, unreadDate, "")
  2418  	if appErr != nil {
  2419  		return nil, appErr
  2420  	}
  2421  
  2422  	params := map[string]interface{}{
  2423  		"mentions":     mentionCount,
  2424  		"unreadCount":  unread,
  2425  		"lastViewedAt": unreadDate,
  2426  		"userId":       userID,
  2427  		"channelId":    unreadPost.ChannelId,
  2428  		"updatedAt":    model.GetMillis(),
  2429  	}
  2430  
  2431  	// msg count uses the value from channels to prevent counting on older channels where no. of messages can be high.
  2432  	// we only count the unread which will be a lot less in 99% cases
  2433  	setUnreadQuery := `
  2434  	UPDATE
  2435  		ChannelMembers
  2436  	SET
  2437  		MentionCount = :mentions,
  2438  		MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount,
  2439  		LastViewedAt = :lastViewedAt,
  2440  		LastUpdateAt = :updatedAt
  2441  	WHERE
  2442  		UserId = :userId
  2443  		AND ChannelId = :channelId
  2444  	`
  2445  	_, err := s.GetMaster().Exec(setUnreadQuery, params)
  2446  	if err != nil {
  2447  		return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAtPost", "store.sql_channel.update_last_viewed_at_post.app_error", params, "Error setting channel "+unreadPost.ChannelId+" as unread: "+err.Error(), http.StatusInternalServerError)
  2448  	}
  2449  
  2450  	chanUnreadQuery := `
  2451  	SELECT
  2452  		c.TeamId TeamId,
  2453  		cm.UserId UserId,
  2454  		cm.ChannelId ChannelId,
  2455  		cm.MsgCount MsgCount,
  2456  		cm.MentionCount MentionCount,
  2457  		cm.LastViewedAt LastViewedAt,
  2458  		cm.NotifyProps NotifyProps
  2459  	FROM
  2460  		ChannelMembers cm
  2461  	LEFT JOIN Channels c ON c.Id=cm.ChannelId
  2462  	WHERE
  2463  		cm.UserId = :userId
  2464  		AND cm.channelId = :channelId
  2465  		AND c.DeleteAt = 0
  2466  	`
  2467  	result := &model.ChannelUnreadAt{}
  2468  	if err = s.GetMaster().SelectOne(result, chanUnreadQuery, params); err != nil {
  2469  		return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAtPost", "store.sql_channel.update_last_viewed_at_post.app_error", params, "Error retrieving unread status from channel "+unreadPost.ChannelId+", error was: "+err.Error(), http.StatusInternalServerError)
  2470  	}
  2471  	return result, nil
  2472  }
  2473  
  2474  func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) *model.AppError {
  2475  	_, err := s.GetMaster().Exec(
  2476  		`UPDATE
  2477  			ChannelMembers
  2478  		SET
  2479  			MentionCount = MentionCount + 1,
  2480  			LastUpdateAt = :LastUpdateAt
  2481  		WHERE
  2482  			UserId = :UserId
  2483  				AND ChannelId = :ChannelId`,
  2484  		map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": model.GetMillis()})
  2485  	if err != nil {
  2486  		return model.NewAppError("SqlChannelStore.IncrementMentionCount", "store.sql_channel.increment_mention_count.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
  2487  	}
  2488  
  2489  	return nil
  2490  }
  2491  
  2492  func (s SqlChannelStore) GetAll(teamId string) ([]*model.Channel, *model.AppError) {
  2493  	var data []*model.Channel
  2494  	_, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Type != 'D' ORDER BY Name", map[string]interface{}{"TeamId": teamId})
  2495  
  2496  	if err != nil {
  2497  		return nil, model.NewAppError("SqlChannelStore.GetAll", "store.sql_channel.get_all.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
  2498  	}
  2499  
  2500  	return data, nil
  2501  }
  2502  
  2503  func (s SqlChannelStore) GetChannelsByIds(channelIds []string, includeDeleted bool) ([]*model.Channel, *model.AppError) {
  2504  	keys, params := MapStringsToQueryParams(channelIds, "Channel")
  2505  	query := `SELECT * FROM Channels WHERE Id IN ` + keys + ` ORDER BY Name`
  2506  	if !includeDeleted {
  2507  		query = `SELECT * FROM Channels WHERE DeleteAt=0 AND Id IN ` + keys + ` ORDER BY Name`
  2508  	}
  2509  
  2510  	var channels []*model.Channel
  2511  	_, err := s.GetReplica().Select(&channels, query, params)
  2512  
  2513  	if err != nil {
  2514  		mlog.Error("Query error getting channels by ids", mlog.Err(err))
  2515  		return nil, model.NewAppError("SqlChannelStore.GetChannelsByIds", "store.sql_channel.get_channels_by_ids.app_error", nil, "", http.StatusInternalServerError)
  2516  	}
  2517  	return channels, nil
  2518  }
  2519  
  2520  func (s SqlChannelStore) GetForPost(postId string) (*model.Channel, *model.AppError) {
  2521  	channel := &model.Channel{}
  2522  	if err := s.GetReplica().SelectOne(
  2523  		channel,
  2524  		`SELECT
  2525  			Channels.*
  2526  		FROM
  2527  			Channels,
  2528  			Posts
  2529  		WHERE
  2530  			Channels.Id = Posts.ChannelId
  2531  			AND Posts.Id = :PostId`, map[string]interface{}{"PostId": postId}); err != nil {
  2532  		return nil, model.NewAppError("SqlChannelStore.GetForPost", "store.sql_channel.get_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError)
  2533  
  2534  	}
  2535  	return channel, nil
  2536  }
  2537  
  2538  func (s SqlChannelStore) AnalyticsTypeCount(teamId string, channelType string) (int64, *model.AppError) {
  2539  	query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType"
  2540  
  2541  	if len(teamId) > 0 {
  2542  		query += " AND TeamId = :TeamId"
  2543  	}
  2544  
  2545  	value, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType})
  2546  	if err != nil {
  2547  		return int64(0), model.NewAppError("SqlChannelStore.AnalyticsTypeCount", "store.sql_channel.analytics_type_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  2548  	}
  2549  	return value, nil
  2550  }
  2551  
  2552  func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType string) (int64, *model.AppError) {
  2553  	query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType AND DeleteAt > 0"
  2554  
  2555  	if len(teamId) > 0 {
  2556  		query += " AND TeamId = :TeamId"
  2557  	}
  2558  
  2559  	v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType})
  2560  	if err != nil {
  2561  		return 0, model.NewAppError("SqlChannelStore.AnalyticsDeletedTypeCount", "store.sql_channel.analytics_deleted_type_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  2562  	}
  2563  
  2564  	return v, nil
  2565  }
  2566  
  2567  func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) {
  2568  	var dbMembers channelMemberWithSchemeRolesList
  2569  	_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId AND (Teams.Id = :TeamId OR Teams.Id = '' OR Teams.Id IS NULL)", map[string]interface{}{"TeamId": teamId, "UserId": userId})
  2570  	if err != nil {
  2571  		return nil, model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
  2572  	}
  2573  
  2574  	return dbMembers.ToModel(), nil
  2575  }
  2576  
  2577  func (s SqlChannelStore) GetMembersForUserWithPagination(teamId, userId string, page, perPage int) (*model.ChannelMembers, *model.AppError) {
  2578  	var dbMembers channelMemberWithSchemeRolesList
  2579  	offset := page * perPage
  2580  	_, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"TeamId": teamId, "UserId": userId, "Limit": perPage, "Offset": offset})
  2581  
  2582  	if err != nil {
  2583  		return nil, model.NewAppError("SqlChannelStore.GetMembersForUserWithPagination", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
  2584  	}
  2585  
  2586  	return dbMembers.ToModel(), nil
  2587  }
  2588  
  2589  func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) {
  2590  	deleteFilter := "AND c.DeleteAt = 0"
  2591  	if includeDeleted {
  2592  		deleteFilter = ""
  2593  	}
  2594  
  2595  	queryFormat := `
  2596  		SELECT
  2597  			Channels.*
  2598  		FROM
  2599  			Channels
  2600  		JOIN
  2601  			PublicChannels c ON (c.Id = Channels.Id)
  2602  		WHERE
  2603  			c.TeamId = :TeamId
  2604  			` + deleteFilter + `
  2605  			%v
  2606  		LIMIT ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT)
  2607  
  2608  	var channels model.ChannelList
  2609  
  2610  	if likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose"); likeClause == "" {
  2611  		if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil {
  2612  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2613  		}
  2614  	} else {
  2615  		// Using a UNION results in index_merge and fulltext queries and is much faster than the ref
  2616  		// query you would get using an OR of the LIKE and full-text clauses.
  2617  		fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
  2618  		likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause)
  2619  		fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause)
  2620  		query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery)
  2621  
  2622  		if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil {
  2623  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2624  		}
  2625  	}
  2626  
  2627  	sort.Slice(channels, func(a, b int) bool {
  2628  		return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName)
  2629  	})
  2630  	return &channels, nil
  2631  }
  2632  
  2633  func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) {
  2634  	deleteFilter := "AND DeleteAt = 0"
  2635  	if includeDeleted {
  2636  		deleteFilter = ""
  2637  	}
  2638  
  2639  	queryFormat := `
  2640  		SELECT
  2641  			C.*
  2642  		FROM
  2643  			Channels AS C
  2644  		JOIN
  2645  			ChannelMembers AS CM ON CM.ChannelId = C.Id
  2646  		WHERE
  2647  			(C.TeamId = :TeamId OR (C.TeamId = '' AND C.Type = 'G'))
  2648  			AND CM.UserId = :UserId
  2649  			` + deleteFilter + `
  2650  			%v
  2651  		LIMIT 50`
  2652  
  2653  	var channels model.ChannelList
  2654  
  2655  	if likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose"); likeClause == "" {
  2656  		if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId, "UserId": userId}); err != nil {
  2657  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2658  		}
  2659  	} else {
  2660  		// Using a UNION results in index_merge and fulltext queries and is much faster than the ref
  2661  		// query you would get using an OR of the LIKE and full-text clauses.
  2662  		fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose")
  2663  		likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause)
  2664  		fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause)
  2665  		query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery)
  2666  
  2667  		if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "UserId": userId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil {
  2668  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2669  		}
  2670  	}
  2671  
  2672  	directChannels, err := s.autocompleteInTeamForSearchDirectMessages(userId, term)
  2673  	if err != nil {
  2674  		return nil, err
  2675  	}
  2676  
  2677  	channels = append(channels, directChannels...)
  2678  
  2679  	sort.Slice(channels, func(a, b int) bool {
  2680  		return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName)
  2681  	})
  2682  	return &channels, nil
  2683  }
  2684  
  2685  func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string, term string) ([]*model.Channel, *model.AppError) {
  2686  	queryFormat := `
  2687  			SELECT
  2688  				C.*,
  2689  				OtherUsers.Username as DisplayName
  2690  			FROM
  2691  				Channels AS C
  2692  			JOIN
  2693  				ChannelMembers AS CM ON CM.ChannelId = C.Id
  2694  			INNER JOIN (
  2695  				SELECT
  2696  					ICM.ChannelId AS ChannelId, IU.Username AS Username
  2697  				FROM
  2698  					Users as IU
  2699  				JOIN
  2700  					ChannelMembers AS ICM ON ICM.UserId = IU.Id
  2701  				WHERE
  2702  					IU.Id != :UserId
  2703  					%v
  2704  				) AS OtherUsers ON OtherUsers.ChannelId = C.Id
  2705  			WHERE
  2706  			    C.Type = 'D'
  2707  				AND CM.UserId = :UserId
  2708  			LIMIT 50`
  2709  
  2710  	var channels model.ChannelList
  2711  
  2712  	if likeClause, likeTerm := s.buildLIKEClause(term, "IU.Username, IU.Nickname"); likeClause == "" {
  2713  		if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"UserId": userId}); err != nil {
  2714  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2715  		}
  2716  	} else {
  2717  		query := fmt.Sprintf(queryFormat, "AND "+likeClause)
  2718  
  2719  		if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"UserId": userId, "LikeTerm": likeTerm}); err != nil {
  2720  			return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2721  		}
  2722  	}
  2723  
  2724  	return channels, nil
  2725  }
  2726  
  2727  func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) {
  2728  	deleteFilter := "AND c.DeleteAt = 0"
  2729  	if includeDeleted {
  2730  		deleteFilter = ""
  2731  	}
  2732  
  2733  	return s.performSearch(`
  2734  		SELECT
  2735  			Channels.*
  2736  		FROM
  2737  			Channels
  2738  		JOIN
  2739  			PublicChannels c ON (c.Id = Channels.Id)
  2740  		WHERE
  2741  			c.TeamId = :TeamId
  2742  			`+deleteFilter+`
  2743  			SEARCH_CLAUSE
  2744  		ORDER BY c.DisplayName
  2745  		LIMIT 100
  2746  		`, term, map[string]interface{}{
  2747  		"TeamId": teamId,
  2748  	})
  2749  }
  2750  
  2751  func (s SqlChannelStore) SearchArchivedInTeam(teamId string, term string, userId string) (*model.ChannelList, *model.AppError) {
  2752  	publicChannels, publicErr := s.performSearch(`
  2753  		SELECT
  2754  			Channels.*
  2755  		FROM
  2756  			Channels
  2757  		JOIN
  2758  			Channels c ON (c.Id = Channels.Id)
  2759  		WHERE
  2760  			c.TeamId = :TeamId
  2761  			SEARCH_CLAUSE
  2762  			AND c.DeleteAt != 0
  2763  			AND c.Type != 'P'
  2764  		ORDER BY c.DisplayName
  2765  		LIMIT 100
  2766  		`, term, map[string]interface{}{
  2767  		"TeamId": teamId,
  2768  		"UserId": userId,
  2769  	})
  2770  
  2771  	privateChannels, privateErr := s.performSearch(`
  2772  		SELECT
  2773  			Channels.*
  2774  		FROM
  2775  			Channels
  2776  		JOIN
  2777  			Channels c ON (c.Id = Channels.Id)
  2778  		WHERE
  2779  			c.TeamId = :TeamId
  2780  			SEARCH_CLAUSE
  2781  			AND c.DeleteAt != 0
  2782  			AND c.Type = 'P'
  2783  			AND c.Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId)
  2784  		ORDER BY c.DisplayName
  2785  		LIMIT 100
  2786  		`, term, map[string]interface{}{
  2787  		"TeamId": teamId,
  2788  		"UserId": userId,
  2789  	})
  2790  
  2791  	output := *publicChannels
  2792  	output = append(output, *privateChannels...)
  2793  
  2794  	outputErr := publicErr
  2795  	if privateErr != nil {
  2796  		outputErr = privateErr
  2797  	}
  2798  
  2799  	return &output, outputErr
  2800  }
  2801  
  2802  func (s SqlChannelStore) SearchForUserInTeam(userId string, teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) {
  2803  	deleteFilter := "AND c.DeleteAt = 0"
  2804  	if includeDeleted {
  2805  		deleteFilter = ""
  2806  	}
  2807  
  2808  	return s.performSearch(`
  2809  		SELECT
  2810  			Channels.*
  2811  		FROM
  2812  			Channels
  2813  		JOIN
  2814  			PublicChannels c ON (c.Id = Channels.Id)
  2815          JOIN
  2816              ChannelMembers cm ON (c.Id = cm.ChannelId)
  2817  		WHERE
  2818  			c.TeamId = :TeamId
  2819          AND
  2820              cm.UserId = :UserId
  2821  			`+deleteFilter+`
  2822  			SEARCH_CLAUSE
  2823  		ORDER BY c.DisplayName
  2824  		LIMIT 100
  2825  		`, term, map[string]interface{}{
  2826  		"TeamId": teamId,
  2827  		"UserId": userId,
  2828  	})
  2829  }
  2830  
  2831  func (s SqlChannelStore) channelSearchQuery(term string, opts store.ChannelSearchOpts, countQuery bool) sq.SelectBuilder {
  2832  	var limit int
  2833  	if opts.PerPage != nil {
  2834  		limit = *opts.PerPage
  2835  	} else {
  2836  		limit = 100
  2837  	}
  2838  
  2839  	var selectStr string
  2840  	if countQuery {
  2841  		selectStr = "count(*)"
  2842  	} else {
  2843  		selectStr = "c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt"
  2844  	}
  2845  
  2846  	query := s.getQueryBuilder().
  2847  		Select(selectStr).
  2848  		From("Channels AS c").
  2849  		Join("Teams AS t ON t.Id = c.TeamId")
  2850  
  2851  	// don't bother ordering or limiting if we're just getting the count
  2852  	if !countQuery {
  2853  		query = query.
  2854  			OrderBy("c.DisplayName, t.DisplayName").
  2855  			Limit(uint64(limit))
  2856  	}
  2857  	if opts.Deleted {
  2858  		query = query.Where(sq.NotEq{"c.DeleteAt": int(0)})
  2859  	} else if !opts.IncludeDeleted {
  2860  		query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
  2861  	}
  2862  
  2863  	if opts.IsPaginated() && !countQuery {
  2864  		query = query.Offset(uint64(*opts.Page * *opts.PerPage))
  2865  	}
  2866  
  2867  	likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
  2868  	if likeTerm != "" {
  2869  		likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "?")
  2870  		fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
  2871  		fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "?")
  2872  		query = query.Where(sq.Or{
  2873  			sq.Expr(likeClause, likeTerm, likeTerm, likeTerm), // Keep the number of likeTerms same as the number
  2874  			// of columns (c.Name, c.DisplayName, c.Purpose)
  2875  			sq.Expr(fulltextClause, fulltextTerm),
  2876  		})
  2877  	}
  2878  
  2879  	if len(opts.ExcludeChannelNames) > 0 {
  2880  		query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames})
  2881  	}
  2882  
  2883  	if len(opts.NotAssociatedToGroup) > 0 {
  2884  		query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
  2885  	}
  2886  
  2887  	if len(opts.TeamIds) > 0 {
  2888  		query = query.Where(sq.Eq{"c.TeamId": opts.TeamIds})
  2889  	}
  2890  
  2891  	if opts.GroupConstrained {
  2892  		query = query.Where(sq.Eq{"c.GroupConstrained": true})
  2893  	} else if opts.ExcludeGroupConstrained {
  2894  		query = query.Where(sq.Or{
  2895  			sq.NotEq{"c.GroupConstrained": true},
  2896  			sq.Eq{"c.GroupConstrained": nil},
  2897  		})
  2898  	}
  2899  
  2900  	if opts.Public && !opts.Private {
  2901  		query = query.Where(sq.Eq{"c.Type": model.CHANNEL_OPEN})
  2902  	} else if opts.Private && !opts.Public {
  2903  		query = query.Where(sq.Eq{"c.Type": model.CHANNEL_PRIVATE})
  2904  	} else {
  2905  		query = query.Where(sq.Or{
  2906  			sq.Eq{"c.Type": model.CHANNEL_OPEN},
  2907  			sq.Eq{"c.Type": model.CHANNEL_PRIVATE},
  2908  		})
  2909  	}
  2910  
  2911  	return query
  2912  }
  2913  
  2914  func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) (*model.ChannelListWithTeamData, int64, *model.AppError) {
  2915  	queryString, args, err := s.channelSearchQuery(term, opts, false).ToSql()
  2916  	if err != nil {
  2917  		return nil, 0, model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
  2918  	}
  2919  	var channels model.ChannelListWithTeamData
  2920  	if _, err = s.GetReplica().Select(&channels, queryString, args...); err != nil {
  2921  		return nil, 0, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2922  	}
  2923  
  2924  	var totalCount int64
  2925  
  2926  	// only query a 2nd time for the count if the results are being requested paginated.
  2927  	if opts.IsPaginated() {
  2928  		queryString, args, err = s.channelSearchQuery(term, opts, true).ToSql()
  2929  		if err != nil {
  2930  			return nil, 0, model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
  2931  		}
  2932  		if totalCount, err = s.GetReplica().SelectInt(queryString, args...); err != nil {
  2933  			return nil, 0, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  2934  		}
  2935  	} else {
  2936  		totalCount = int64(len(channels))
  2937  	}
  2938  
  2939  	return &channels, totalCount, nil
  2940  }
  2941  
  2942  func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) (*model.ChannelList, *model.AppError) {
  2943  	return s.performSearch(`
  2944  		SELECT
  2945  			Channels.*
  2946  		FROM
  2947  			Channels
  2948  		JOIN
  2949  			PublicChannels c ON (c.Id = Channels.Id)
  2950  		WHERE
  2951  			c.TeamId = :TeamId
  2952  		AND c.DeleteAt = 0
  2953  		AND c.Id NOT IN (
  2954  			SELECT
  2955  				c.Id
  2956  			FROM
  2957  				PublicChannels c
  2958  			JOIN
  2959  				ChannelMembers cm ON (cm.ChannelId = c.Id)
  2960  			WHERE
  2961  				c.TeamId = :TeamId
  2962  			AND cm.UserId = :UserId
  2963  			AND c.DeleteAt = 0
  2964  			)
  2965  		SEARCH_CLAUSE
  2966  		ORDER BY c.DisplayName
  2967  		LIMIT 100
  2968  		`, term, map[string]interface{}{
  2969  		"TeamId": teamId,
  2970  		"UserId": userId,
  2971  	})
  2972  }
  2973  
  2974  func (s SqlChannelStore) buildLIKEClause(term string, searchColumns string) (likeClause, likeTerm string) {
  2975  	likeTerm = sanitizeSearchTerm(term, "*")
  2976  
  2977  	if likeTerm == "" {
  2978  		return
  2979  	}
  2980  
  2981  	// Prepare the LIKE portion of the query.
  2982  	var searchFields []string
  2983  	for _, field := range strings.Split(searchColumns, ", ") {
  2984  		if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  2985  			searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*'", field, ":LikeTerm"))
  2986  		} else {
  2987  			searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*'", field, ":LikeTerm"))
  2988  		}
  2989  	}
  2990  
  2991  	likeClause = fmt.Sprintf("(%s)", strings.Join(searchFields, " OR "))
  2992  	likeTerm = wildcardSearchTerm(likeTerm)
  2993  	return
  2994  }
  2995  
  2996  func (s SqlChannelStore) buildFulltextClause(term string, searchColumns string) (fulltextClause, fulltextTerm string) {
  2997  	// Copy the terms as we will need to prepare them differently for each search type.
  2998  	fulltextTerm = term
  2999  
  3000  	// These chars must be treated as spaces in the fulltext query.
  3001  	for _, c := range spaceFulltextSearchChar {
  3002  		fulltextTerm = strings.Replace(fulltextTerm, c, " ", -1)
  3003  	}
  3004  
  3005  	// Prepare the FULLTEXT portion of the query.
  3006  	if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  3007  		fulltextTerm = strings.Replace(fulltextTerm, "|", "", -1)
  3008  
  3009  		splitTerm := strings.Fields(fulltextTerm)
  3010  		for i, t := range strings.Fields(fulltextTerm) {
  3011  			if i == len(splitTerm)-1 {
  3012  				splitTerm[i] = t + ":*"
  3013  			} else {
  3014  				splitTerm[i] = t + ":* &"
  3015  			}
  3016  		}
  3017  
  3018  		fulltextTerm = strings.Join(splitTerm, " ")
  3019  
  3020  		fulltextClause = fmt.Sprintf("((to_tsvector('english', %s)) @@ to_tsquery('english', :FulltextTerm))", convertMySQLFullTextColumnsToPostgres(searchColumns))
  3021  	} else if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  3022  		splitTerm := strings.Fields(fulltextTerm)
  3023  		for i, t := range strings.Fields(fulltextTerm) {
  3024  			splitTerm[i] = "+" + t + "*"
  3025  		}
  3026  
  3027  		fulltextTerm = strings.Join(splitTerm, " ")
  3028  
  3029  		fulltextClause = fmt.Sprintf("MATCH(%s) AGAINST (:FulltextTerm IN BOOLEAN MODE)", searchColumns)
  3030  	}
  3031  
  3032  	return
  3033  }
  3034  
  3035  func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) (*model.ChannelList, *model.AppError) {
  3036  	likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
  3037  	if likeTerm == "" {
  3038  		// If the likeTerm is empty after preparing, then don't bother searching.
  3039  		searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
  3040  	} else {
  3041  		parameters["LikeTerm"] = likeTerm
  3042  		fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
  3043  		parameters["FulltextTerm"] = fulltextTerm
  3044  		searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
  3045  	}
  3046  
  3047  	var channels model.ChannelList
  3048  
  3049  	if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
  3050  		return nil, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
  3051  	}
  3052  
  3053  	return &channels, nil
  3054  }
  3055  
  3056  func (s SqlChannelStore) getSearchGroupChannelsQuery(userId, term string, isPostgreSQL bool) (string, map[string]interface{}) {
  3057  	var query, baseLikeClause string
  3058  	if isPostgreSQL {
  3059  		baseLikeClause = "ARRAY_TO_STRING(ARRAY_AGG(u.Username), ', ') LIKE %s"
  3060  		query = `
  3061              SELECT
  3062                  *
  3063              FROM
  3064                  Channels
  3065              WHERE
  3066                  Id IN (
  3067                      SELECT
  3068                          cc.Id
  3069                      FROM (
  3070                          SELECT
  3071                              c.Id
  3072                          FROM
  3073                              Channels c
  3074                          JOIN
  3075                              ChannelMembers cm on c.Id = cm.ChannelId
  3076                          JOIN
  3077                              Users u on u.Id = cm.UserId
  3078                          WHERE
  3079                              c.Type = 'G'
  3080                          AND
  3081                              u.Id = :UserId
  3082                          GROUP BY
  3083                              c.Id
  3084                      ) cc
  3085                      JOIN
  3086                          ChannelMembers cm on cc.Id = cm.ChannelId
  3087                      JOIN
  3088                          Users u on u.Id = cm.UserId
  3089                      GROUP BY
  3090                          cc.Id
  3091                      HAVING
  3092                          %s
  3093                      LIMIT
  3094                          ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT) + `
  3095                  )`
  3096  	} else {
  3097  		baseLikeClause = "GROUP_CONCAT(u.Username SEPARATOR ', ') LIKE %s"
  3098  		query = `
  3099              SELECT
  3100                  cc.*
  3101              FROM (
  3102                  SELECT
  3103                      c.*
  3104                  FROM
  3105                      Channels c
  3106                  JOIN
  3107                      ChannelMembers cm on c.Id = cm.ChannelId
  3108                  JOIN
  3109                      Users u on u.Id = cm.UserId
  3110                  WHERE
  3111                      c.Type = 'G'
  3112                  AND
  3113                      u.Id = :UserId
  3114                  GROUP BY
  3115                      c.Id
  3116              ) cc
  3117              JOIN
  3118                  ChannelMembers cm on cc.Id = cm.ChannelId
  3119              JOIN
  3120                  Users u on u.Id = cm.UserId
  3121              GROUP BY
  3122                  cc.Id
  3123              HAVING
  3124                  %s
  3125              LIMIT
  3126                  ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT)
  3127  	}
  3128  
  3129  	var likeClauses []string
  3130  	args := map[string]interface{}{"UserId": userId}
  3131  	terms := strings.Split(strings.ToLower(strings.Trim(term, " ")), " ")
  3132  
  3133  	for idx, term := range terms {
  3134  		argName := fmt.Sprintf("Term%v", idx)
  3135  		term = sanitizeSearchTerm(term, "\\")
  3136  		likeClauses = append(likeClauses, fmt.Sprintf(baseLikeClause, ":"+argName))
  3137  		args[argName] = "%" + term + "%"
  3138  	}
  3139  
  3140  	query = fmt.Sprintf(query, strings.Join(likeClauses, " AND "))
  3141  	return query, args
  3142  }
  3143  
  3144  func (s SqlChannelStore) SearchGroupChannels(userId, term string) (*model.ChannelList, *model.AppError) {
  3145  	isPostgreSQL := s.DriverName() == model.DATABASE_DRIVER_POSTGRES
  3146  	queryString, args := s.getSearchGroupChannelsQuery(userId, term, isPostgreSQL)
  3147  
  3148  	var groupChannels model.ChannelList
  3149  	if _, err := s.GetReplica().Select(&groupChannels, queryString, args); err != nil {
  3150  		return nil, model.NewAppError("SqlChannelStore.SearchGroupChannels", "store.sql_channel.search_group_channels.app_error", nil, "userId="+userId+", term="+term+", err="+err.Error(), http.StatusInternalServerError)
  3151  	}
  3152  	return &groupChannels, nil
  3153  }
  3154  
  3155  func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) {
  3156  	var dbMembers channelMemberWithSchemeRolesList
  3157  	props := make(map[string]interface{})
  3158  	idQuery := ""
  3159  
  3160  	for index, userId := range userIds {
  3161  		if len(idQuery) > 0 {
  3162  			idQuery += ", "
  3163  		}
  3164  
  3165  		props["userId"+strconv.Itoa(index)] = userId
  3166  		idQuery += ":userId" + strconv.Itoa(index)
  3167  	}
  3168  
  3169  	props["ChannelId"] = channelId
  3170  
  3171  	if _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId IN ("+idQuery+")", props); err != nil {
  3172  		return nil, model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError)
  3173  	}
  3174  
  3175  	return dbMembers.ToModel(), nil
  3176  }
  3177  
  3178  func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) (model.ChannelList, *model.AppError) {
  3179  	var channels model.ChannelList
  3180  	_, err := s.GetReplica().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit})
  3181  	if err != nil {
  3182  		return nil, model.NewAppError("SqlChannelStore.GetChannelsByScheme", "store.sql_channel.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError)
  3183  	}
  3184  	return channels, nil
  3185  }
  3186  
  3187  // This function does the Advanced Permissions Phase 2 migration for ChannelMember objects. It performs the migration
  3188  // in batches as a single transaction per batch to ensure consistency but to also minimise execution time to avoid
  3189  // causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function
  3190  // *after* the new Schemes functionality has been used on an installation will have unintended consequences.
  3191  func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId string) (map[string]string, *model.AppError) {
  3192  	var transaction *gorp.Transaction
  3193  	var err error
  3194  
  3195  	if transaction, err = s.GetMaster().Begin(); err != nil {
  3196  		return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3197  	}
  3198  	defer finalizeTransaction(transaction)
  3199  
  3200  	var channelMembers []channelMember
  3201  	if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:FromChannelId, :FromUserId) ORDER BY ChannelId, UserId LIMIT 100", map[string]interface{}{"FromChannelId": fromChannelId, "FromUserId": fromUserId}); err != nil {
  3202  		return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.select.app_error", nil, err.Error(), http.StatusInternalServerError)
  3203  	}
  3204  
  3205  	if len(channelMembers) == 0 {
  3206  		// No more channel members in query result means that the migration has finished.
  3207  		return nil, nil
  3208  	}
  3209  
  3210  	for i := range channelMembers {
  3211  		member := channelMembers[i]
  3212  		roles := strings.Fields(member.Roles)
  3213  		var newRoles []string
  3214  		if !member.SchemeAdmin.Valid {
  3215  			member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true}
  3216  		}
  3217  		if !member.SchemeUser.Valid {
  3218  			member.SchemeUser = sql.NullBool{Bool: false, Valid: true}
  3219  		}
  3220  		if !member.SchemeGuest.Valid {
  3221  			member.SchemeGuest = sql.NullBool{Bool: false, Valid: true}
  3222  		}
  3223  		for _, role := range roles {
  3224  			if role == model.CHANNEL_ADMIN_ROLE_ID {
  3225  				member.SchemeAdmin = sql.NullBool{Bool: true, Valid: true}
  3226  			} else if role == model.CHANNEL_USER_ROLE_ID {
  3227  				member.SchemeUser = sql.NullBool{Bool: true, Valid: true}
  3228  			} else if role == model.CHANNEL_GUEST_ROLE_ID {
  3229  				member.SchemeGuest = sql.NullBool{Bool: true, Valid: true}
  3230  			} else {
  3231  				newRoles = append(newRoles, role)
  3232  			}
  3233  		}
  3234  		member.Roles = strings.Join(newRoles, " ")
  3235  
  3236  		if _, err := transaction.Update(&member); err != nil {
  3237  			return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.update.app_error", nil, err.Error(), http.StatusInternalServerError)
  3238  		}
  3239  
  3240  	}
  3241  
  3242  	if err := transaction.Commit(); err != nil {
  3243  		return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3244  	}
  3245  
  3246  	data := make(map[string]string)
  3247  	data["ChannelId"] = channelMembers[len(channelMembers)-1].ChannelId
  3248  	data["UserId"] = channelMembers[len(channelMembers)-1].UserId
  3249  	return data, nil
  3250  }
  3251  
  3252  func (s SqlChannelStore) ResetAllChannelSchemes() *model.AppError {
  3253  	transaction, err := s.GetMaster().Begin()
  3254  	if err != nil {
  3255  		return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3256  	}
  3257  	defer finalizeTransaction(transaction)
  3258  
  3259  	resetErr := s.resetAllChannelSchemesT(transaction)
  3260  	if resetErr != nil {
  3261  		return resetErr
  3262  	}
  3263  
  3264  	if err := transaction.Commit(); err != nil {
  3265  		return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3266  	}
  3267  
  3268  	return nil
  3269  }
  3270  
  3271  func (s SqlChannelStore) resetAllChannelSchemesT(transaction *gorp.Transaction) *model.AppError {
  3272  	if _, err := transaction.Exec("UPDATE Channels SET SchemeId=''"); err != nil {
  3273  		return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.app_error", nil, err.Error(), http.StatusInternalServerError)
  3274  	}
  3275  
  3276  	return nil
  3277  }
  3278  
  3279  func (s SqlChannelStore) ClearAllCustomRoleAssignments() *model.AppError {
  3280  	builtInRoles := model.MakeDefaultRoles()
  3281  	lastUserId := strings.Repeat("0", 26)
  3282  	lastChannelId := strings.Repeat("0", 26)
  3283  
  3284  	for {
  3285  		var transaction *gorp.Transaction
  3286  		var err error
  3287  
  3288  		if transaction, err = s.GetMaster().Begin(); err != nil {
  3289  			return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3290  		}
  3291  
  3292  		var channelMembers []*channelMember
  3293  		if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:ChannelId, :UserId) ORDER BY ChannelId, UserId LIMIT 1000", map[string]interface{}{"ChannelId": lastChannelId, "UserId": lastUserId}); err != nil {
  3294  			finalizeTransaction(transaction)
  3295  			return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.select.app_error", nil, err.Error(), http.StatusInternalServerError)
  3296  		}
  3297  
  3298  		if len(channelMembers) == 0 {
  3299  			finalizeTransaction(transaction)
  3300  			break
  3301  		}
  3302  
  3303  		for _, member := range channelMembers {
  3304  			lastUserId = member.UserId
  3305  			lastChannelId = member.ChannelId
  3306  
  3307  			var newRoles []string
  3308  
  3309  			for _, role := range strings.Fields(member.Roles) {
  3310  				for name := range builtInRoles {
  3311  					if name == role {
  3312  						newRoles = append(newRoles, role)
  3313  						break
  3314  					}
  3315  				}
  3316  			}
  3317  
  3318  			newRolesString := strings.Join(newRoles, " ")
  3319  			if newRolesString != member.Roles {
  3320  				if _, err := transaction.Exec("UPDATE ChannelMembers SET Roles = :Roles WHERE UserId = :UserId AND ChannelId = :ChannelId", map[string]interface{}{"Roles": newRolesString, "ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil {
  3321  					finalizeTransaction(transaction)
  3322  					return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.update.app_error", nil, err.Error(), http.StatusInternalServerError)
  3323  				}
  3324  			}
  3325  		}
  3326  
  3327  		if err := transaction.Commit(); err != nil {
  3328  			finalizeTransaction(transaction)
  3329  			return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3330  		}
  3331  	}
  3332  
  3333  	return nil
  3334  }
  3335  
  3336  func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) ([]*model.ChannelForExport, *model.AppError) {
  3337  	var channels []*model.ChannelForExport
  3338  	if _, err := s.GetReplica().Select(&channels, `
  3339  		SELECT
  3340  			Channels.*,
  3341  			Teams.Name as TeamName,
  3342  			Schemes.Name as SchemeName
  3343  		FROM Channels
  3344  		INNER JOIN
  3345  			Teams ON Channels.TeamId = Teams.Id
  3346  		LEFT JOIN
  3347  			Schemes ON Channels.SchemeId = Schemes.Id
  3348  		WHERE
  3349  			Channels.Id > :AfterId
  3350  			AND Channels.Type IN ('O', 'P')
  3351  		ORDER BY
  3352  			Id
  3353  		LIMIT :Limit`,
  3354  		map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil {
  3355  		return nil, model.NewAppError("SqlChannelStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
  3356  	}
  3357  
  3358  	return channels, nil
  3359  }
  3360  
  3361  func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string) ([]*model.ChannelMemberForExport, *model.AppError) {
  3362  	var members []*model.ChannelMemberForExport
  3363  	_, err := s.GetReplica().Select(&members, `
  3364  		SELECT
  3365  			ChannelMembers.ChannelId,
  3366  			ChannelMembers.UserId,
  3367  			ChannelMembers.Roles,
  3368  			ChannelMembers.LastViewedAt,
  3369  			ChannelMembers.MsgCount,
  3370  			ChannelMembers.MentionCount,
  3371  			ChannelMembers.NotifyProps,
  3372  			ChannelMembers.LastUpdateAt,
  3373  			ChannelMembers.SchemeUser,
  3374  			ChannelMembers.SchemeAdmin,
  3375  			(ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) as SchemeGuest,
  3376  			Channels.Name as ChannelName
  3377  		FROM
  3378  			ChannelMembers
  3379  		INNER JOIN
  3380  			Channels ON ChannelMembers.ChannelId = Channels.Id
  3381  		WHERE
  3382  			ChannelMembers.UserId = :UserId
  3383  			AND Channels.TeamId = :TeamId
  3384  			AND Channels.DeleteAt = 0`,
  3385  		map[string]interface{}{"TeamId": teamId, "UserId": userId})
  3386  
  3387  	if err != nil {
  3388  		return nil, model.NewAppError("SqlChannelStore.GetChannelMembersForExport", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
  3389  	}
  3390  
  3391  	return members, nil
  3392  }
  3393  
  3394  func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId string) ([]*model.DirectChannelForExport, *model.AppError) {
  3395  	var directChannelsForExport []*model.DirectChannelForExport
  3396  	query := s.getQueryBuilder().
  3397  		Select("Channels.*").
  3398  		From("Channels").
  3399  		Where(sq.And{
  3400  			sq.Gt{"Channels.Id": afterId},
  3401  			sq.Eq{"Channels.DeleteAt": int(0)},
  3402  			sq.Eq{"Channels.Type": []string{"D", "G"}},
  3403  		}).
  3404  		OrderBy("Channels.Id").
  3405  		Limit(uint64(limit))
  3406  
  3407  	queryString, args, err := query.ToSql()
  3408  	if err != nil {
  3409  		return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
  3410  	}
  3411  
  3412  	if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil {
  3413  		return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
  3414  	}
  3415  
  3416  	var channelIds []string
  3417  	for _, channel := range directChannelsForExport {
  3418  		channelIds = append(channelIds, channel.Id)
  3419  	}
  3420  	query = s.getQueryBuilder().
  3421  		Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest").
  3422  		From("ChannelMembers cm").
  3423  		Join("Users u ON ( u.Id = cm.UserId )").
  3424  		Where(sq.And{
  3425  			sq.Eq{"cm.ChannelId": channelIds},
  3426  			sq.Eq{"u.DeleteAt": int(0)},
  3427  		})
  3428  
  3429  	queryString, args, err = query.ToSql()
  3430  	if err != nil {
  3431  		return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
  3432  	}
  3433  
  3434  	var channelMembers []*model.ChannelMemberForExport
  3435  	if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil {
  3436  		return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
  3437  	}
  3438  
  3439  	// Populate each channel with its members
  3440  	dmChannelsMap := make(map[string]*model.DirectChannelForExport)
  3441  	for _, channel := range directChannelsForExport {
  3442  		channel.Members = &[]string{}
  3443  		dmChannelsMap[channel.Id] = channel
  3444  	}
  3445  	for _, member := range channelMembers {
  3446  		members := dmChannelsMap[member.ChannelId].Members
  3447  		*members = append(*members, member.Username)
  3448  	}
  3449  
  3450  	return directChannelsForExport, nil
  3451  }
  3452  
  3453  func (s SqlChannelStore) GetChannelsBatchForIndexing(startTime, endTime int64, limit int) ([]*model.Channel, *model.AppError) {
  3454  	query :=
  3455  		`SELECT
  3456  			 *
  3457  		 FROM
  3458  			 Channels
  3459  		 WHERE
  3460  			 Type = 'O'
  3461  		 AND
  3462  			 CreateAt >= :StartTime
  3463  		 AND
  3464  			 CreateAt < :EndTime
  3465  		 ORDER BY
  3466  			 CreateAt
  3467  		 LIMIT
  3468  			 :NumChannels`
  3469  
  3470  	var channels []*model.Channel
  3471  	_, err := s.GetSearchReplica().Select(&channels, query, map[string]interface{}{"StartTime": startTime, "EndTime": endTime, "NumChannels": limit})
  3472  	if err != nil {
  3473  		return nil, model.NewAppError("SqlChannelStore.GetChannelsBatchForIndexing", "store.sql_channel.get_channels_batch_for_indexing.get.app_error", nil, err.Error(), http.StatusInternalServerError)
  3474  	}
  3475  
  3476  	return channels, nil
  3477  }
  3478  
  3479  func (s SqlChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) {
  3480  	query := s.getQueryBuilder().
  3481  		Select("Count(*)").
  3482  		From("ChannelMembers").
  3483  		Where(sq.And{
  3484  			sq.Eq{"UserId": userId},
  3485  			sq.Eq{"ChannelId": channelIds},
  3486  		})
  3487  
  3488  	queryString, args, err := query.ToSql()
  3489  	if err != nil {
  3490  		return false, model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError)
  3491  	}
  3492  	c, err := s.GetReplica().SelectInt(queryString, args...)
  3493  	if err != nil {
  3494  		return false, model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError)
  3495  	}
  3496  	return c > 0, nil
  3497  }
  3498  
  3499  func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) *model.AppError {
  3500  	sql := fmt.Sprintf(`
  3501  		UPDATE
  3502  			ChannelMembers
  3503  		SET
  3504  			SchemeAdmin = CASE WHEN UserId IN ('%s') THEN
  3505  				TRUE
  3506  			ELSE
  3507  				FALSE
  3508  			END
  3509  		WHERE
  3510  			ChannelId = :ChannelId
  3511  			AND (SchemeGuest = false OR SchemeGuest IS NULL)
  3512  			`, strings.Join(userIDs, "', '"))
  3513  
  3514  	if _, err := s.GetMaster().Exec(sql, map[string]interface{}{"ChannelId": channelID}); err != nil {
  3515  		return model.NewAppError("SqlChannelStore.UpdateMembersRole", "store.update_error", nil, err.Error(), http.StatusInternalServerError)
  3516  	}
  3517  
  3518  	return nil
  3519  }
  3520  
  3521  func (s SqlChannelStore) GroupSyncedChannelCount() (int64, *model.AppError) {
  3522  	query := s.getQueryBuilder().Select("COUNT(*)").From("Channels").Where(sq.Eq{"GroupConstrained": true, "DeleteAt": 0})
  3523  
  3524  	sql, args, err := query.ToSql()
  3525  	if err != nil {
  3526  		return 0, model.NewAppError("SqlChannelStore.GroupSyncedChannelCount", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError)
  3527  	}
  3528  
  3529  	count, err := s.GetReplica().SelectInt(sql, args...)
  3530  	if err != nil {
  3531  		return 0, model.NewAppError("SqlChannelStore.GroupSyncedChannelCount", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
  3532  	}
  3533  
  3534  	return count, nil
  3535  }
  3536  
  3537  type sidebarCategoryForJoin struct {
  3538  	model.SidebarCategory
  3539  	ChannelId *string
  3540  }
  3541  
  3542  func (s SqlChannelStore) CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
  3543  	transaction, err := s.GetMaster().Begin()
  3544  	if err != nil {
  3545  		return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3546  	}
  3547  
  3548  	defer finalizeTransaction(transaction)
  3549  
  3550  	categoriesWithOrder, appErr := s.getSidebarCategoriesT(transaction, userId, teamId)
  3551  	if appErr != nil {
  3552  		return nil, appErr
  3553  	}
  3554  	if len(categoriesWithOrder.Categories) < 1 {
  3555  		return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError)
  3556  	}
  3557  	newOrder := categoriesWithOrder.Order
  3558  	newCategoryId := model.NewId()
  3559  	newCategorySortOrder := 0
  3560  	/*
  3561  		When a new category is created, it should be placed as follows:
  3562  		1. If the Favorites category is first, the new category should be placed after it
  3563  		2. Otherwise, the new category should be placed first.
  3564  	*/
  3565  	if categoriesWithOrder.Categories[0].Type == model.SidebarCategoryFavorites {
  3566  		newOrder = append([]string{newOrder[0], newCategoryId}, newOrder[1:]...)
  3567  		newCategorySortOrder = model.MinimalSidebarSortDistance
  3568  	} else {
  3569  		newOrder = append([]string{newCategoryId}, newOrder...)
  3570  	}
  3571  
  3572  	category := &model.SidebarCategory{
  3573  		DisplayName: newCategory.DisplayName,
  3574  		Id:          newCategoryId,
  3575  		UserId:      userId,
  3576  		TeamId:      teamId,
  3577  		Sorting:     model.SidebarCategorySortDefault,
  3578  		SortOrder:   int64(model.MinimalSidebarSortDistance * len(newOrder)), // first we place it at the end of the list
  3579  		Type:        model.SidebarCategoryCustom,
  3580  	}
  3581  	if err = transaction.Insert(category); err != nil {
  3582  		return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3583  	}
  3584  
  3585  	if len(newCategory.Channels) > 0 {
  3586  		channelIdsKeys, deleteParams := MapStringsToQueryParams(newCategory.Channels, "ChannelId")
  3587  		deleteParams["UserId"] = userId
  3588  		deleteParams["TeamId"] = teamId
  3589  
  3590  		// Remove any channels from their previous categories and add them to the new one
  3591  		var deleteQuery string
  3592  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  3593  			deleteQuery = `
  3594  				DELETE
  3595  					SidebarChannels
  3596  				FROM
  3597  					SidebarChannels
  3598  				JOIN
  3599  					SidebarCategories ON SidebarChannels.CategoryId = SidebarCategories.Id
  3600  				WHERE
  3601  					SidebarChannels.UserId = :UserId
  3602  					AND SidebarChannels.ChannelId IN ` + channelIdsKeys + `
  3603  					AND SidebarCategories.TeamId = :TeamId`
  3604  		} else {
  3605  			deleteQuery = `
  3606  				DELETE FROM
  3607  					SidebarChannels
  3608  				USING
  3609  					SidebarCategories
  3610  				WHERE
  3611  					SidebarChannels.CategoryId = SidebarCategories.Id
  3612  					AND SidebarChannels.UserId = :UserId
  3613  					AND SidebarChannels.ChannelId IN ` + channelIdsKeys + `
  3614  					AND SidebarCategories.TeamId = :TeamId`
  3615  		}
  3616  
  3617  		_, err = transaction.Exec(deleteQuery, deleteParams)
  3618  		if err != nil {
  3619  			return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3620  		}
  3621  
  3622  		var channels []interface{}
  3623  		for i, channelID := range newCategory.Channels {
  3624  			channels = append(channels, &model.SidebarChannel{
  3625  				ChannelId:  channelID,
  3626  				CategoryId: newCategoryId,
  3627  				SortOrder:  int64(i * model.MinimalSidebarSortDistance),
  3628  				UserId:     userId,
  3629  			})
  3630  		}
  3631  		if err = transaction.Insert(channels...); err != nil {
  3632  			return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3633  		}
  3634  	}
  3635  
  3636  	// now we re-order the categories according to the new order
  3637  	if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, newOrder); appErr != nil {
  3638  		return nil, appErr
  3639  	}
  3640  
  3641  	if err = transaction.Commit(); err != nil {
  3642  		return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3643  	}
  3644  
  3645  	// patch category to return proper sort order
  3646  	category.SortOrder = int64(newCategorySortOrder)
  3647  	result := &model.SidebarCategoryWithChannels{
  3648  		SidebarCategory: *category,
  3649  		Channels:        newCategory.Channels,
  3650  	}
  3651  
  3652  	return result, nil
  3653  }
  3654  
  3655  func (s SqlChannelStore) completePopulatingCategoryChannels(category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
  3656  	transaction, err := s.GetMaster().Begin()
  3657  	if err != nil {
  3658  		return nil, model.NewAppError("SqlChannelStore.completePopulatingCategoryChannels", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3659  	}
  3660  	defer finalizeTransaction(transaction)
  3661  
  3662  	result, appErr := s.completePopulatingCategoryChannelsT(transaction, category)
  3663  	if appErr != nil {
  3664  		return nil, appErr
  3665  	}
  3666  
  3667  	if err = transaction.Commit(); err != nil {
  3668  		return nil, model.NewAppError("SqlChannelStore.completePopulatingCategoryChannels", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3669  	}
  3670  
  3671  	return result, nil
  3672  }
  3673  
  3674  func (s SqlChannelStore) completePopulatingCategoryChannelsT(transation *gorp.Transaction, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
  3675  	if category.Type == model.SidebarCategoryCustom || category.Type == model.SidebarCategoryFavorites {
  3676  		return category, nil
  3677  	}
  3678  
  3679  	var channelTypeFilter sq.Sqlizer
  3680  	if category.Type == model.SidebarCategoryDirectMessages {
  3681  		// any DM/GM channels that aren't in any category should be returned as part of the Direct Messages category
  3682  		channelTypeFilter = sq.Eq{"Channels.Type": []string{model.CHANNEL_DIRECT, model.CHANNEL_GROUP}}
  3683  	} else if category.Type == model.SidebarCategoryChannels {
  3684  		// any public/private channels that are on the current team and aren't in any category should be returned as part of the Channels category
  3685  		channelTypeFilter = sq.And{
  3686  			sq.Eq{"Channels.Type": []string{model.CHANNEL_OPEN, model.CHANNEL_PRIVATE}},
  3687  			sq.Eq{"Channels.TeamId": category.TeamId},
  3688  		}
  3689  	}
  3690  
  3691  	// A subquery that is true if the channel does not have a SidebarChannel entry for the current user on the current team
  3692  	doesNotHaveSidebarChannel := sq.Select("1").
  3693  		Prefix("NOT EXISTS (").
  3694  		From("SidebarChannels").
  3695  		Join("SidebarCategories on SidebarChannels.CategoryId=SidebarCategories.Id").
  3696  		Where(sq.And{
  3697  			sq.Expr("SidebarChannels.ChannelId = ChannelMembers.ChannelId"),
  3698  			sq.Eq{"SidebarCategories.UserId": category.UserId},
  3699  			sq.Eq{"SidebarCategories.TeamId": category.TeamId},
  3700  		}).
  3701  		Suffix(")")
  3702  
  3703  	var channels []string
  3704  	sql, args, _ := s.getQueryBuilder().
  3705  		Select("Id").
  3706  		From("ChannelMembers").
  3707  		LeftJoin("Channels ON Channels.Id=ChannelMembers.ChannelId").
  3708  		Where(sq.And{
  3709  			sq.Eq{"ChannelMembers.UserId": category.UserId},
  3710  			channelTypeFilter,
  3711  			sq.Eq{"Channels.DeleteAt": 0},
  3712  			doesNotHaveSidebarChannel,
  3713  		}).
  3714  		OrderBy("DisplayName ASC").ToSql()
  3715  
  3716  	if _, err := transation.Select(&channels, sql, args...); err != nil {
  3717  		return nil, model.NewAppError("SqlPostStore.completePopulatingCategoryChannelsT", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
  3718  	}
  3719  
  3720  	category.Channels = append(channels, category.Channels...)
  3721  	return category, nil
  3722  }
  3723  
  3724  func (s SqlChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
  3725  	var categories []*sidebarCategoryForJoin
  3726  	sql, args, _ := s.getQueryBuilder().
  3727  		Select("SidebarCategories.*", "SidebarChannels.ChannelId").
  3728  		From("SidebarCategories").
  3729  		LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=SidebarCategories.Id").
  3730  		Where(sq.Eq{"SidebarCategories.Id": categoryId}).
  3731  		OrderBy("SidebarChannels.SortOrder ASC").ToSql()
  3732  	if _, err := s.GetReplica().Select(&categories, sql, args...); err != nil {
  3733  		return nil, model.NewAppError("SqlPostStore.GetSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
  3734  	}
  3735  	result := &model.SidebarCategoryWithChannels{
  3736  		SidebarCategory: categories[0].SidebarCategory,
  3737  		Channels:        make([]string, 0),
  3738  	}
  3739  	for _, category := range categories {
  3740  		if category.ChannelId != nil {
  3741  			result.Channels = append(result.Channels, *category.ChannelId)
  3742  		}
  3743  	}
  3744  	return s.completePopulatingCategoryChannels(result)
  3745  }
  3746  
  3747  func (s SqlChannelStore) getSidebarCategoriesT(transaction *gorp.Transaction, userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
  3748  	oc := model.OrderedSidebarCategories{
  3749  		Categories: make(model.SidebarCategoriesWithChannels, 0),
  3750  		Order:      make([]string, 0),
  3751  	}
  3752  
  3753  	var categories []*sidebarCategoryForJoin
  3754  	sql, args, _ := s.getQueryBuilder().
  3755  		Select("SidebarCategories.*", "SidebarChannels.ChannelId").
  3756  		From("SidebarCategories").
  3757  		LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=Id").
  3758  		Where(sq.And{
  3759  			sq.Eq{"SidebarCategories.UserId": userId},
  3760  			sq.Eq{"SidebarCategories.TeamId": teamId},
  3761  		}).
  3762  		OrderBy("SidebarCategories.SortOrder ASC, SidebarChannels.SortOrder ASC").ToSql()
  3763  
  3764  	if _, err := transaction.Select(&categories, sql, args...); err != nil {
  3765  		return nil, model.NewAppError("SqlPostStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
  3766  	}
  3767  	for _, category := range categories {
  3768  		var prevCategory *model.SidebarCategoryWithChannels
  3769  		for _, existing := range oc.Categories {
  3770  			if existing.Id == category.Id {
  3771  				prevCategory = existing
  3772  				break
  3773  			}
  3774  		}
  3775  		if prevCategory == nil {
  3776  			prevCategory = &model.SidebarCategoryWithChannels{
  3777  				SidebarCategory: category.SidebarCategory,
  3778  				Channels:        make([]string, 0),
  3779  			}
  3780  			oc.Categories = append(oc.Categories, prevCategory)
  3781  			oc.Order = append(oc.Order, category.Id)
  3782  		}
  3783  		if category.ChannelId != nil {
  3784  			prevCategory.Channels = append(prevCategory.Channels, *category.ChannelId)
  3785  		}
  3786  	}
  3787  	for _, category := range oc.Categories {
  3788  		if _, err := s.completePopulatingCategoryChannelsT(transaction, category); err != nil {
  3789  			return nil, err
  3790  		}
  3791  	}
  3792  
  3793  	return &oc, nil
  3794  }
  3795  
  3796  func (s SqlChannelStore) GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
  3797  	transaction, err := s.GetMaster().Begin()
  3798  	if err != nil {
  3799  		return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3800  	}
  3801  
  3802  	defer finalizeTransaction(transaction)
  3803  
  3804  	oc, appErr := s.getSidebarCategoriesT(transaction, userId, teamId)
  3805  	if appErr != nil {
  3806  		return nil, appErr
  3807  	}
  3808  
  3809  	if err = transaction.Commit(); err != nil {
  3810  		return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3811  	}
  3812  
  3813  	return oc, nil
  3814  }
  3815  
  3816  func (s SqlChannelStore) GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError) {
  3817  	var ids []string
  3818  
  3819  	sql, args, _ := s.getQueryBuilder().
  3820  		Select("Id").
  3821  		From("SidebarCategories").
  3822  		Where(sq.And{
  3823  			sq.Eq{"UserId": userId},
  3824  			sq.Eq{"TeamId": teamId},
  3825  		}).
  3826  		OrderBy("SidebarCategories.SortOrder ASC").ToSql()
  3827  
  3828  	if _, err := s.GetReplica().Select(&ids, sql, args...); err != nil {
  3829  		return nil, model.NewAppError("SqlPostStore.GetSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
  3830  	}
  3831  	return ids, nil
  3832  }
  3833  
  3834  func (s SqlChannelStore) updateSidebarCategoryOrderT(transaction *gorp.Transaction, userId, teamId string, categoryOrder []string) *model.AppError {
  3835  	var newOrder []interface{}
  3836  	runningOrder := 0
  3837  	for _, categoryId := range categoryOrder {
  3838  		newOrder = append(newOrder, &model.SidebarCategory{
  3839  			Id:        categoryId,
  3840  			SortOrder: int64(runningOrder),
  3841  		})
  3842  		runningOrder += model.MinimalSidebarSortDistance
  3843  	}
  3844  
  3845  	// There's a bug in gorp where UpdateColumns messes up the stored query for any other attempt to use .Update or
  3846  	// .UpdateColumns on this table, so it's okay to use here as long as we don't use those methods for SidebarCategories
  3847  	// anywhere else.
  3848  	if _, err := transaction.UpdateColumns(func(col *gorp.ColumnMap) bool {
  3849  		return col.ColumnName == "SortOrder"
  3850  	}, newOrder...); err != nil {
  3851  		return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3852  	}
  3853  
  3854  	return nil
  3855  }
  3856  
  3857  func (s SqlChannelStore) UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError {
  3858  	transaction, err := s.GetMaster().Begin()
  3859  	if err != nil {
  3860  		return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3861  	}
  3862  
  3863  	defer finalizeTransaction(transaction)
  3864  
  3865  	// Ensure no invalid categories are included and that no categories are left out
  3866  	existingOrder, appErr := s.GetSidebarCategoryOrder(userId, teamId)
  3867  	if appErr != nil {
  3868  		return appErr
  3869  	}
  3870  	if len(existingOrder) != len(categoryOrder) {
  3871  		return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, "Cannot update category order, passed list of categories different size than in DB", http.StatusInternalServerError)
  3872  	}
  3873  	for _, originalCategoryId := range existingOrder {
  3874  		found := false
  3875  		for _, newCategoryId := range categoryOrder {
  3876  			if newCategoryId == originalCategoryId {
  3877  				found = true
  3878  				break
  3879  			}
  3880  		}
  3881  		if !found {
  3882  			return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, "Cannot update category order, passed list of categories contains unrecognized category IDs", http.StatusBadRequest)
  3883  		}
  3884  	}
  3885  
  3886  	if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, categoryOrder); appErr != nil {
  3887  		return appErr
  3888  	}
  3889  
  3890  	if err = transaction.Commit(); err != nil {
  3891  		return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3892  	}
  3893  
  3894  	return nil
  3895  }
  3896  
  3897  func (s SqlChannelStore) UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
  3898  	transaction, err := s.GetMaster().Begin()
  3899  	if err != nil {
  3900  		return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  3901  	}
  3902  	defer finalizeTransaction(transaction)
  3903  
  3904  	updatedCategories := []*model.SidebarCategoryWithChannels{}
  3905  	for _, category := range categories {
  3906  		originalCategory, appErr := s.GetSidebarCategory(category.Id)
  3907  		if appErr != nil {
  3908  			return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, appErr.Error(), http.StatusInternalServerError)
  3909  		}
  3910  
  3911  		// Copy category to avoid modifying an argument
  3912  		updatedCategory := &model.SidebarCategoryWithChannels{
  3913  			SidebarCategory: category.SidebarCategory,
  3914  		}
  3915  
  3916  		// Prevent any changes to read-only fields of SidebarCategories
  3917  		updatedCategory.UserId = originalCategory.UserId
  3918  		updatedCategory.TeamId = originalCategory.TeamId
  3919  		updatedCategory.SortOrder = originalCategory.SortOrder
  3920  		updatedCategory.Type = originalCategory.Type
  3921  
  3922  		if updatedCategory.Type != model.SidebarCategoryCustom {
  3923  			updatedCategory.DisplayName = originalCategory.DisplayName
  3924  		}
  3925  
  3926  		if category.Type != model.SidebarCategoryDirectMessages {
  3927  			updatedCategory.Channels = make([]string, len(category.Channels))
  3928  			copy(updatedCategory.Channels, category.Channels)
  3929  		}
  3930  
  3931  		updateQuery, updateParams, _ := s.getQueryBuilder().
  3932  			Update("SidebarCategories").
  3933  			Set("DisplayName", updatedCategory.DisplayName).
  3934  			Set("Sorting", updatedCategory.Sorting).
  3935  			Where(sq.Eq{"Id": updatedCategory.Id}).ToSql()
  3936  
  3937  		if _, err = transaction.Exec(updateQuery, updateParams...); err != nil {
  3938  			return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3939  		}
  3940  
  3941  		// if we are updating DM category, it's order can't channel order cannot be changed.
  3942  		if category.Type != model.SidebarCategoryDirectMessages {
  3943  			// Remove any SidebarChannels entries that were either:
  3944  			// - previously in this category (and any ones that are still in the category will be recreated below)
  3945  			// - in another category and are being added to this category
  3946  			sql, args, _ := s.getQueryBuilder().
  3947  				Delete("SidebarChannels").
  3948  				Where(
  3949  					sq.And{
  3950  						sq.Or{
  3951  							sq.Eq{"ChannelId": originalCategory.Channels},
  3952  							sq.Eq{"ChannelId": updatedCategory.Channels},
  3953  						},
  3954  						sq.Eq{"CategoryId": category.Id},
  3955  					},
  3956  				).ToSql()
  3957  
  3958  			if _, err = transaction.Exec(sql, args...); err != nil {
  3959  				return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3960  			}
  3961  
  3962  			var channels []interface{}
  3963  			runningOrder := 0
  3964  			for _, channelID := range category.Channels {
  3965  				channels = append(channels, &model.SidebarChannel{
  3966  					ChannelId:  channelID,
  3967  					CategoryId: category.Id,
  3968  					SortOrder:  int64(runningOrder),
  3969  					UserId:     userId,
  3970  				})
  3971  				runningOrder += model.MinimalSidebarSortDistance
  3972  			}
  3973  
  3974  			if err = transaction.Insert(channels...); err != nil {
  3975  				return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3976  			}
  3977  		}
  3978  
  3979  		// Update the favorites preferences based on channels moving into or out of the Favorites category for compatibility
  3980  		if category.Type == model.SidebarCategoryFavorites {
  3981  			// Remove any old favorites
  3982  			sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where(
  3983  				sq.Eq{
  3984  					"UserId":   userId,
  3985  					"Name":     originalCategory.Channels,
  3986  					"Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
  3987  				},
  3988  			).ToSql()
  3989  
  3990  			if _, err = transaction.Exec(sql, args...); err != nil {
  3991  				return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  3992  			}
  3993  
  3994  			// And then add the new ones
  3995  			for _, channelID := range category.Channels {
  3996  				// This breaks the PreferenceStore abstraction, but it should be safe to assume that everything is a SQL
  3997  				// store in this package.
  3998  				if err = s.Preference().(*SqlPreferenceStore).save(transaction, &model.Preference{
  3999  					Name:     channelID,
  4000  					UserId:   userId,
  4001  					Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
  4002  					Value:    "true",
  4003  				}); err != nil {
  4004  					return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4005  				}
  4006  			}
  4007  		} else {
  4008  			// Remove any old favorites that might have been in this category
  4009  			sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where(
  4010  				sq.Eq{
  4011  					"UserId":   userId,
  4012  					"Name":     category.Channels,
  4013  					"Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
  4014  				},
  4015  			).ToSql()
  4016  
  4017  			if _, err = transaction.Exec(sql, args...); err != nil {
  4018  				return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4019  			}
  4020  		}
  4021  
  4022  		updatedCategories = append(updatedCategories, updatedCategory)
  4023  	}
  4024  
  4025  	// Ensure Channels are populated for Channels/Direct Messages category if they change
  4026  	for i, updatedCategory := range updatedCategories {
  4027  		populated, err := s.completePopulatingCategoryChannelsT(transaction, updatedCategory)
  4028  		if err != nil {
  4029  			return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4030  		}
  4031  
  4032  		updatedCategories[i] = populated
  4033  	}
  4034  
  4035  	if err = transaction.Commit(); err != nil {
  4036  		return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  4037  	}
  4038  
  4039  	return updatedCategories, nil
  4040  }
  4041  
  4042  // UpdateSidebarChannelsByPreferences is called when the Preference table is being updated to keep SidebarCategories in sync
  4043  // At the moment, it's only handling Favorites and NOT DMs/GMs (those will be handled client side)
  4044  func (s SqlChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) error {
  4045  	transaction, err := s.GetMaster().Begin()
  4046  	if err != nil {
  4047  		return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: begin_transaction")
  4048  	}
  4049  	defer finalizeTransaction(transaction)
  4050  
  4051  	for _, preference := range *preferences {
  4052  		preference := preference
  4053  
  4054  		if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL {
  4055  			continue
  4056  		}
  4057  
  4058  		// if new preference is false - remove the channel from the appropriate sidebar category
  4059  		if preference.Value == "false" {
  4060  			if err := s.removeSidebarEntriesForPreferenceT(transaction, &preference); err != nil {
  4061  				return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: removeSidebarEntriesForPreferenceT")
  4062  			}
  4063  		} else {
  4064  			if err := s.addChannelToFavoritesCategoryT(transaction, &preference); err != nil {
  4065  				return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: addChannelToFavoritesCategoryT")
  4066  			}
  4067  		}
  4068  	}
  4069  
  4070  	if err := transaction.Commit(); err != nil {
  4071  		return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: commit_transaction")
  4072  	}
  4073  
  4074  	return nil
  4075  }
  4076  
  4077  func (s SqlChannelStore) removeSidebarEntriesForPreferenceT(transaction *gorp.Transaction, preference *model.Preference) error {
  4078  	if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL {
  4079  		return nil
  4080  	}
  4081  
  4082  	// Delete any corresponding SidebarChannels entries in a Favorites category corresponding to this preference. This
  4083  	// can't use the query builder because it uses DB-specific syntax
  4084  	params := map[string]interface{}{
  4085  		"UserId":       preference.UserId,
  4086  		"ChannelId":    preference.Name,
  4087  		"CategoryType": model.SidebarCategoryFavorites,
  4088  	}
  4089  	var query string
  4090  	if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  4091  		query = `
  4092  			DELETE
  4093  				SidebarChannels
  4094  			FROM
  4095  				SidebarChannels
  4096  			JOIN
  4097  				SidebarCategories ON SidebarChannels.CategoryId = SidebarCategories.Id
  4098  			WHERE
  4099  				SidebarChannels.UserId = :UserId
  4100  				AND SidebarChannels.ChannelId = :ChannelId
  4101  				AND SidebarCategories.Type = :CategoryType`
  4102  	} else {
  4103  		query = `
  4104  			DELETE FROM
  4105  				SidebarChannels
  4106  			USING
  4107  				SidebarCategories
  4108  			WHERE
  4109  				SidebarChannels.CategoryId = SidebarCategories.Id
  4110  				AND SidebarChannels.UserId = :UserId
  4111  				AND SidebarChannels.ChannelId = :ChannelId
  4112  				AND SidebarCategories.Type = :CategoryType`
  4113  	}
  4114  
  4115  	if _, err := transaction.Exec(query, params); err != nil {
  4116  		return errors.Wrap(err, "Failed to remove sidebar entries for preference")
  4117  	}
  4118  
  4119  	return nil
  4120  }
  4121  
  4122  func (s SqlChannelStore) addChannelToFavoritesCategoryT(transaction *gorp.Transaction, preference *model.Preference) error {
  4123  	if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL {
  4124  		return nil
  4125  	}
  4126  
  4127  	var channel *model.Channel
  4128  	if obj, err := transaction.Get(&model.Channel{}, preference.Name); err != nil {
  4129  		return errors.Wrapf(err, "Failed to get favorited channel with id=%s", preference.Name)
  4130  	} else {
  4131  		channel = obj.(*model.Channel)
  4132  	}
  4133  
  4134  	// Get the IDs of the Favorites category/categories that the channel needs to be added to
  4135  	builder := s.getQueryBuilder().
  4136  		Select("SidebarCategories.Id").
  4137  		From("SidebarCategories").
  4138  		LeftJoin("SidebarChannels on SidebarCategories.Id = SidebarChannels.CategoryId and SidebarChannels.ChannelId = ?", preference.Name).
  4139  		Where(sq.Eq{
  4140  			"SidebarCategories.UserId": preference.UserId,
  4141  			"Type":                     model.SidebarCategoryFavorites,
  4142  		}).
  4143  		Where("SidebarChannels.ChannelId is null")
  4144  
  4145  	if channel.TeamId != "" {
  4146  		builder = builder.Where(sq.Eq{"TeamId": channel.TeamId})
  4147  	}
  4148  
  4149  	idsQuery, idsParams, _ := builder.ToSql()
  4150  
  4151  	var categoryIds []string
  4152  	if _, err := transaction.Select(&categoryIds, idsQuery, idsParams...); err != nil {
  4153  		return errors.Wrap(err, "Failed to get Favorites sidebar categories")
  4154  	}
  4155  
  4156  	if len(categoryIds) == 0 {
  4157  		// The channel is already in the Favorites category/categories
  4158  		return nil
  4159  	}
  4160  
  4161  	// For each category ID, insert a row into SidebarChannels with the given channel ID and a SortOrder that's less than
  4162  	// all existing SortOrders in the category so that the newly favorited channel comes first
  4163  	insertQuery, insertParams, _ := s.getQueryBuilder().
  4164  		Insert("SidebarChannels").
  4165  		Columns(
  4166  			"ChannelId",
  4167  			"CategoryId",
  4168  			"UserId",
  4169  			"SortOrder",
  4170  		).
  4171  		Select(
  4172  			sq.Select().
  4173  				Column("? as ChannelId", preference.Name).
  4174  				Column("SidebarCategories.Id as CategoryId").
  4175  				Column("? as UserId", preference.UserId).
  4176  				Column("COALESCE(MIN(SidebarChannels.SortOrder) - 10, 0) as SortOrder").
  4177  				From("SidebarCategories").
  4178  				LeftJoin("SidebarChannels on SidebarCategories.Id = SidebarChannels.CategoryId").
  4179  				Where(sq.Eq{
  4180  					"SidebarCategories.Id": categoryIds,
  4181  				}).
  4182  				GroupBy("SidebarCategories.Id")).ToSql()
  4183  
  4184  	if _, err := transaction.Exec(insertQuery, insertParams...); err != nil {
  4185  		return errors.Wrap(err, "Failed to add sidebar entries for favorited channel")
  4186  	}
  4187  
  4188  	return nil
  4189  }
  4190  
  4191  // DeleteSidebarChannelsByPreferences is called when the Preference table is being updated to keep SidebarCategories in sync
  4192  // At the moment, it's only handling Favorites and NOT DMs/GMs (those will be handled client side)
  4193  func (s SqlChannelStore) DeleteSidebarChannelsByPreferences(preferences *model.Preferences) error {
  4194  	transaction, err := s.GetMaster().Begin()
  4195  	if err != nil {
  4196  		return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: begin_transaction")
  4197  	}
  4198  	defer finalizeTransaction(transaction)
  4199  
  4200  	for _, preference := range *preferences {
  4201  		preference := preference
  4202  
  4203  		if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL {
  4204  			continue
  4205  		}
  4206  
  4207  		if err := s.removeSidebarEntriesForPreferenceT(transaction, &preference); err != nil {
  4208  			return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: removeSidebarEntriesForPreferenceT")
  4209  		}
  4210  	}
  4211  
  4212  	if err := transaction.Commit(); err != nil {
  4213  		return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: commit_transaction")
  4214  	}
  4215  
  4216  	return nil
  4217  }
  4218  
  4219  func (s SqlChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError {
  4220  	// if channel is being moved, remove it from the categories, since it's possible that there's no matching category in the new team
  4221  	if _, err := s.GetMaster().Exec("DELETE FROM SidebarChannels WHERE ChannelId=:ChannelId", map[string]interface{}{"ChannelId": channel.Id}); err != nil {
  4222  		return model.NewAppError("SqlChannelStore.UpdateSidebarChannelCategoryOnMove", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4223  	}
  4224  	return nil
  4225  }
  4226  
  4227  func (s SqlChannelStore) ClearSidebarOnTeamLeave(userId, teamId string) *model.AppError {
  4228  	// if user leaves the team, clean his team related entries in sidebar channels and categories
  4229  	params := map[string]interface{}{
  4230  		"UserId": userId,
  4231  		"TeamId": teamId,
  4232  	}
  4233  
  4234  	var deleteQuery string
  4235  	if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  4236  		deleteQuery = "DELETE SidebarChannels FROM SidebarChannels LEFT JOIN SidebarCategories ON SidebarCategories.Id = SidebarChannels.CategoryId WHERE SidebarCategories.TeamId=:TeamId AND SidebarCategories.UserId=:UserId"
  4237  	} else {
  4238  		deleteQuery = "DELETE FROM SidebarChannels USING SidebarChannels AS chan LEFT OUTER JOIN SidebarCategories AS cat ON cat.Id = chan.CategoryId WHERE cat.UserId = :UserId AND   cat.TeamId = :TeamId"
  4239  	}
  4240  	if _, err := s.GetMaster().Exec(deleteQuery, params); err != nil {
  4241  		return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4242  	}
  4243  	if _, err := s.GetMaster().Exec("DELETE FROM SidebarCategories WHERE SidebarCategories.TeamId = :TeamId AND SidebarCategories.UserId = :UserId", params); err != nil {
  4244  		return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4245  	}
  4246  	return nil
  4247  }
  4248  
  4249  // DeleteSidebarCategory removes a custom category and moves any channels into it into the Channels and Direct Messages
  4250  // categories respectively. Assumes that the provided user ID and team ID match the given category ID.
  4251  func (s SqlChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError {
  4252  	transaction, err := s.GetMaster().Begin()
  4253  	if err != nil {
  4254  		return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  4255  	}
  4256  	defer finalizeTransaction(transaction)
  4257  
  4258  	// Ensure that we're deleting a custom category
  4259  	var category *model.SidebarCategory
  4260  	if err = transaction.SelectOne(&category, "SELECT * FROM SidebarCategories WHERE Id = :Id", map[string]interface{}{"Id": categoryId}); err != nil {
  4261  		return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4262  	}
  4263  
  4264  	if category.Type != model.SidebarCategoryCustom {
  4265  		return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.delete_invalid.app_error", nil, "", http.StatusBadRequest)
  4266  	}
  4267  
  4268  	// Delete the channels in the category
  4269  	sql, args, _ := s.getQueryBuilder().
  4270  		Delete("SidebarChannels").
  4271  		Where(sq.Eq{"CategoryId": categoryId}).ToSql()
  4272  
  4273  	if _, err := transaction.Exec(sql, args...); err != nil {
  4274  		return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4275  	}
  4276  
  4277  	// Delete the category itself
  4278  	sql, args, _ = s.getQueryBuilder().
  4279  		Delete("SidebarCategories").
  4280  		Where(sq.Eq{"Id": categoryId}).ToSql()
  4281  
  4282  	if _, err := transaction.Exec(sql, args...); err != nil {
  4283  		return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
  4284  	}
  4285  
  4286  	if err := transaction.Commit(); err != nil {
  4287  		return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
  4288  	}
  4289  
  4290  	return nil
  4291  }