github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/sqlstore/group_store.go (about)

     1  package sqlstore
     2  
     3  import (
     4  	"database/sql"
     5  	"fmt"
     6  	"strings"
     7  
     8  	sq "github.com/Masterminds/squirrel"
     9  	"github.com/pkg/errors"
    10  
    11  	"github.com/masterhung0112/hk_server/v5/model"
    12  	"github.com/masterhung0112/hk_server/v5/store"
    13  )
    14  
    15  type selectType int
    16  
    17  const (
    18  	selectGroups selectType = iota
    19  	selectCountGroups
    20  )
    21  
    22  type groupTeam struct {
    23  	model.GroupSyncable
    24  	TeamId string `db:"TeamId"`
    25  }
    26  
    27  type groupChannel struct {
    28  	model.GroupSyncable
    29  	ChannelId string `db:"ChannelId"`
    30  }
    31  
    32  type groupTeamJoin struct {
    33  	groupTeam
    34  	TeamDisplayName string `db:"TeamDisplayName"`
    35  	TeamType        string `db:"TeamType"`
    36  }
    37  
    38  type groupChannelJoin struct {
    39  	groupChannel
    40  	ChannelDisplayName string `db:"ChannelDisplayName"`
    41  	TeamDisplayName    string `db:"TeamDisplayName"`
    42  	TeamType           string `db:"TeamType"`
    43  	ChannelType        string `db:"ChannelType"`
    44  	TeamID             string `db:"TeamId"`
    45  }
    46  
    47  type SqlGroupStore struct {
    48  	*SqlStore
    49  }
    50  
    51  func newSqlGroupStore(sqlStore *SqlStore) store.GroupStore {
    52  	s := &SqlGroupStore{SqlStore: sqlStore}
    53  	for _, db := range sqlStore.GetAllConns() {
    54  		groups := db.AddTableWithName(model.Group{}, "UserGroups").SetKeys(false, "Id")
    55  		groups.ColMap("Id").SetMaxSize(26)
    56  		groups.ColMap("Name").SetMaxSize(model.GroupNameMaxLength).SetUnique(true)
    57  		groups.ColMap("DisplayName").SetMaxSize(model.GroupDisplayNameMaxLength)
    58  		groups.ColMap("Description").SetMaxSize(model.GroupDescriptionMaxLength)
    59  		groups.ColMap("Source").SetMaxSize(model.GroupSourceMaxLength)
    60  		groups.ColMap("RemoteId").SetMaxSize(model.GroupRemoteIDMaxLength)
    61  		groups.SetUniqueTogether("Source", "RemoteId")
    62  
    63  		groupMembers := db.AddTableWithName(model.GroupMember{}, "GroupMembers").SetKeys(false, "GroupId", "UserId")
    64  		groupMembers.ColMap("GroupId").SetMaxSize(26)
    65  		groupMembers.ColMap("UserId").SetMaxSize(26)
    66  
    67  		groupTeams := db.AddTableWithName(groupTeam{}, "GroupTeams").SetKeys(false, "GroupId", "TeamId")
    68  		groupTeams.ColMap("GroupId").SetMaxSize(26)
    69  		groupTeams.ColMap("TeamId").SetMaxSize(26)
    70  
    71  		groupChannels := db.AddTableWithName(groupChannel{}, "GroupChannels").SetKeys(false, "GroupId", "ChannelId")
    72  		groupChannels.ColMap("GroupId").SetMaxSize(26)
    73  		groupChannels.ColMap("ChannelId").SetMaxSize(26)
    74  	}
    75  	return s
    76  }
    77  
    78  func (s *SqlGroupStore) createIndexesIfNotExists() {
    79  	s.CreateIndexIfNotExists("idx_groupmembers_create_at", "GroupMembers", "CreateAt")
    80  	s.CreateIndexIfNotExists("idx_usergroups_remote_id", "UserGroups", "RemoteId")
    81  	s.CreateIndexIfNotExists("idx_usergroups_delete_at", "UserGroups", "DeleteAt")
    82  	s.CreateIndexIfNotExists("idx_groupteams_teamid", "GroupTeams", "TeamId")
    83  	s.CreateIndexIfNotExists("idx_groupchannels_channelid", "GroupChannels", "ChannelId")
    84  	s.CreateColumnIfNotExistsNoDefault("Channels", "GroupConstrained", "tinyint(1)", "boolean")
    85  	s.CreateColumnIfNotExistsNoDefault("Teams", "GroupConstrained", "tinyint(1)", "boolean")
    86  	s.CreateIndexIfNotExists("idx_groupteams_schemeadmin", "GroupTeams", "SchemeAdmin")
    87  	s.CreateIndexIfNotExists("idx_groupchannels_schemeadmin", "GroupChannels", "SchemeAdmin")
    88  }
    89  
    90  func (s *SqlGroupStore) Create(group *model.Group) (*model.Group, error) {
    91  	if group.Id != "" {
    92  		return nil, store.NewErrInvalidInput("Group", "id", group.Id)
    93  	}
    94  
    95  	if err := group.IsValidForCreate(); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	group.Id = model.NewId()
   100  	group.CreateAt = model.GetMillis()
   101  	group.UpdateAt = group.CreateAt
   102  
   103  	if err := s.GetMaster().Insert(group); err != nil {
   104  		if IsUniqueConstraintError(err, []string{"Name", "groups_name_key"}) {
   105  			return nil, errors.Wrapf(err, "Group with name %s already exists", *group.Name)
   106  		}
   107  		return nil, errors.Wrap(err, "failed to save Group")
   108  	}
   109  
   110  	return group, nil
   111  }
   112  
   113  func (s *SqlGroupStore) Get(groupId string) (*model.Group, error) {
   114  	var group *model.Group
   115  	if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupId}); err != nil {
   116  		if err == sql.ErrNoRows {
   117  			return nil, store.NewErrNotFound("Group", groupId)
   118  		}
   119  		return nil, errors.Wrapf(err, "failed to get Group with id=%s", groupId)
   120  	}
   121  
   122  	return group, nil
   123  }
   124  
   125  func (s *SqlGroupStore) GetByName(name string, opts model.GroupSearchOpts) (*model.Group, error) {
   126  	var group *model.Group
   127  	query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Name": name})
   128  	if opts.FilterAllowReference {
   129  		query = query.Where("AllowReference = true")
   130  	}
   131  
   132  	queryString, args, err := query.ToSql()
   133  
   134  	if err != nil {
   135  		return nil, errors.Wrap(err, "get_by_name_tosql")
   136  	}
   137  	if err := s.GetReplica().SelectOne(&group, queryString, args...); err != nil {
   138  		if err == sql.ErrNoRows {
   139  			return nil, store.NewErrNotFound("Group", fmt.Sprintf("name=%s", name))
   140  		}
   141  		return nil, errors.Wrapf(err, "failed to get Group with name=%s", name)
   142  	}
   143  
   144  	return group, nil
   145  }
   146  
   147  func (s *SqlGroupStore) GetByIDs(groupIDs []string) ([]*model.Group, error) {
   148  	var groups []*model.Group
   149  	query := s.getQueryBuilder().Select("*").From("UserGroups").Where(sq.Eq{"Id": groupIDs})
   150  	queryString, args, err := query.ToSql()
   151  	if err != nil {
   152  		return nil, errors.Wrap(err, "get_by_ids_tosql")
   153  	}
   154  	if _, err := s.GetReplica().Select(&groups, queryString, args...); err != nil {
   155  		return nil, errors.Wrap(err, "failed to find Groups by ids")
   156  	}
   157  	return groups, nil
   158  }
   159  
   160  func (s *SqlGroupStore) GetByRemoteID(remoteID string, groupSource model.GroupSource) (*model.Group, error) {
   161  	var group *model.Group
   162  	if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE RemoteId = :RemoteId AND Source = :Source", map[string]interface{}{"RemoteId": remoteID, "Source": groupSource}); err != nil {
   163  		if err == sql.ErrNoRows {
   164  			return nil, store.NewErrNotFound("Group", fmt.Sprintf("remoteId=%s", remoteID))
   165  		}
   166  		return nil, errors.Wrapf(err, "failed to get Group with remoteId=%s", remoteID)
   167  	}
   168  
   169  	return group, nil
   170  }
   171  
   172  func (s *SqlGroupStore) GetAllBySource(groupSource model.GroupSource) ([]*model.Group, error) {
   173  	var groups []*model.Group
   174  
   175  	if _, err := s.GetReplica().Select(&groups, "SELECT * from UserGroups WHERE DeleteAt = 0 AND Source = :Source", map[string]interface{}{"Source": groupSource}); err != nil {
   176  		return nil, errors.Wrapf(err, "failed to find Groups by groupSource=%v", groupSource)
   177  	}
   178  
   179  	return groups, nil
   180  }
   181  
   182  func (s *SqlGroupStore) GetByUser(userId string) ([]*model.Group, error) {
   183  	var groups []*model.Group
   184  
   185  	query := `
   186  		SELECT
   187  			UserGroups.*
   188  		FROM
   189  			GroupMembers
   190  			JOIN UserGroups ON UserGroups.Id = GroupMembers.GroupId
   191  		WHERE
   192  			GroupMembers.DeleteAt = 0
   193  			AND UserId = :UserId`
   194  
   195  	if _, err := s.GetReplica().Select(&groups, query, map[string]interface{}{"UserId": userId}); err != nil {
   196  		return nil, errors.Wrapf(err, "failed to find Groups with userId=%s", userId)
   197  	}
   198  
   199  	return groups, nil
   200  }
   201  
   202  func (s *SqlGroupStore) Update(group *model.Group) (*model.Group, error) {
   203  	var retrievedGroup *model.Group
   204  	if err := s.GetReplica().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": group.Id}); err != nil {
   205  		if err == sql.ErrNoRows {
   206  			return nil, store.NewErrNotFound("Group", group.Id)
   207  		}
   208  		return nil, errors.Wrapf(err, "failed to get Group with id=%s", group.Id)
   209  	}
   210  
   211  	// If updating DeleteAt it can only be to 0
   212  	if group.DeleteAt != retrievedGroup.DeleteAt && group.DeleteAt != 0 {
   213  		return nil, errors.New("DeleteAt should be 0 when updating")
   214  	}
   215  
   216  	// Reset these properties, don't update them based on input
   217  	group.CreateAt = retrievedGroup.CreateAt
   218  	group.UpdateAt = model.GetMillis()
   219  
   220  	if err := group.IsValidForUpdate(); err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	rowsChanged, err := s.GetMaster().Update(group)
   225  	if err != nil {
   226  		if IsUniqueConstraintError(err, []string{"Name", "groups_name_key"}) {
   227  			return nil, errors.Wrapf(err, "Group with name %s already exists", *group.Name)
   228  		}
   229  		return nil, errors.Wrap(err, "failed to update Group")
   230  	}
   231  	if rowsChanged > 1 {
   232  		return nil, errors.Wrapf(err, "multiple Groups were update: %d", rowsChanged)
   233  	}
   234  
   235  	return group, nil
   236  }
   237  
   238  func (s *SqlGroupStore) Delete(groupID string) (*model.Group, error) {
   239  	var group *model.Group
   240  	if err := s.GetReplica().SelectOne(&group, "SELECT * from UserGroups WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": groupID}); err != nil {
   241  		if err == sql.ErrNoRows {
   242  			return nil, store.NewErrNotFound("Group", groupID)
   243  		}
   244  		return nil, errors.Wrapf(err, "failed to get Group with id=%s", groupID)
   245  	}
   246  
   247  	time := model.GetMillis()
   248  	group.DeleteAt = time
   249  	group.UpdateAt = time
   250  
   251  	if _, err := s.GetMaster().Update(group); err != nil {
   252  		return nil, errors.Wrapf(err, "failed to update Group with id=%s", groupID)
   253  	}
   254  
   255  	return group, nil
   256  }
   257  
   258  func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, error) {
   259  	var groupMembers []*model.User
   260  
   261  	query := `
   262  		SELECT
   263  			Users.*
   264  		FROM
   265  			GroupMembers
   266  			JOIN Users ON Users.Id = GroupMembers.UserId
   267  		WHERE
   268  			GroupMembers.DeleteAt = 0
   269  			AND Users.DeleteAt = 0
   270  			AND GroupId = :GroupId`
   271  
   272  	if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID}); err != nil {
   273  		return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID)
   274  	}
   275  
   276  	return groupMembers, nil
   277  }
   278  
   279  func (s *SqlGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, error) {
   280  	var groupMembers []*model.User
   281  
   282  	query := `
   283  		SELECT
   284  			Users.*
   285  		FROM
   286  			GroupMembers
   287  			JOIN Users ON Users.Id = GroupMembers.UserId
   288  		WHERE
   289  			GroupMembers.DeleteAt = 0
   290  			AND Users.DeleteAt = 0
   291  			AND GroupId = :GroupId
   292  		ORDER BY
   293  			GroupMembers.CreateAt DESC
   294  		LIMIT
   295  			:Limit
   296  		OFFSET
   297  			:Offset`
   298  
   299  	if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "Limit": perPage, "Offset": page * perPage}); err != nil {
   300  		return nil, errors.Wrapf(err, "failed to find member Users for Group with id=%s", groupID)
   301  	}
   302  
   303  	return groupMembers, nil
   304  }
   305  
   306  func (s *SqlGroupStore) GetMemberCount(groupID string) (int64, error) {
   307  	query := `
   308  		SELECT
   309  			count(*)
   310  		FROM
   311  			GroupMembers
   312  			JOIN Users ON Users.Id = GroupMembers.UserId
   313  		WHERE
   314  			GroupMembers.GroupId = :GroupId
   315  			AND Users.DeleteAt = 0`
   316  
   317  	count, err := s.GetReplica().SelectInt(query, map[string]interface{}{"GroupId": groupID})
   318  	if err != nil {
   319  		return int64(0), errors.Wrapf(err, "failed to count member Users for Group with id=%s", groupID)
   320  	}
   321  
   322  	return count, nil
   323  }
   324  
   325  func (s *SqlGroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, error) {
   326  	var groupMembers []*model.User
   327  
   328  	query := `
   329  		SELECT
   330  			Users.*
   331  		FROM
   332  			GroupMembers
   333  			JOIN Users ON Users.Id = GroupMembers.UserId
   334  		WHERE
   335  			GroupId = :GroupId
   336  			AND GroupMembers.UserId IN (
   337  				SELECT TeamMembers.UserId
   338  				FROM TeamMembers
   339  				JOIN Teams ON Teams.Id = :TeamId
   340  				WHERE TeamMembers.TeamId = Teams.Id
   341  				AND TeamMembers.DeleteAt = 0
   342  			)
   343  			AND GroupMembers.DeleteAt = 0
   344  			AND Users.DeleteAt = 0
   345  		`
   346  
   347  	if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "TeamId": teamID}); err != nil {
   348  		return nil, errors.Wrapf(err, "failed to member Users for groupId=%s and teamId=%s", groupID, teamID)
   349  	}
   350  
   351  	return groupMembers, nil
   352  }
   353  
   354  func (s *SqlGroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, error) {
   355  	var groupMembers []*model.User
   356  
   357  	query := `
   358  		SELECT
   359  			Users.*
   360  		FROM
   361  			GroupMembers
   362  			JOIN Users ON Users.Id = GroupMembers.UserId
   363  		WHERE
   364  			GroupId = :GroupId
   365  			AND GroupMembers.UserId NOT IN (
   366  				SELECT ChannelMembers.UserId
   367  				FROM ChannelMembers
   368  				WHERE ChannelMembers.ChannelId = :ChannelId
   369  			)
   370  			AND GroupMembers.UserId IN (
   371  				SELECT TeamMembers.UserId
   372  				FROM TeamMembers
   373  				JOIN Channels ON Channels.Id = :ChannelId
   374  				JOIN Teams ON Teams.Id = Channels.TeamId
   375  				WHERE TeamMembers.TeamId = Teams.Id
   376  				AND TeamMembers.DeleteAt = 0
   377  			)
   378  			AND GroupMembers.DeleteAt = 0
   379  			AND Users.DeleteAt = 0
   380  		`
   381  
   382  	if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "ChannelId": channelID}); err != nil {
   383  		return nil, errors.Wrapf(err, "failed to member Users for groupId=%s and channelId!=%s", groupID, channelID)
   384  	}
   385  
   386  	return groupMembers, nil
   387  }
   388  
   389  func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, error) {
   390  	member := &model.GroupMember{
   391  		GroupId:  groupID,
   392  		UserId:   userID,
   393  		CreateAt: model.GetMillis(),
   394  	}
   395  
   396  	if err := member.IsValid(); err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	var retrievedGroup *model.Group
   401  	if err := s.GetReplica().SelectOne(&retrievedGroup, "SELECT * FROM UserGroups WHERE Id = :Id", map[string]interface{}{"Id": groupID}); err != nil {
   402  		return nil, errors.Wrapf(err, "failed to get UserGroup with groupId=%s and userId=%s", groupID, userID)
   403  	}
   404  
   405  	var retrievedMember *model.GroupMember
   406  	if err := s.GetReplica().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId", map[string]interface{}{"GroupId": member.GroupId, "UserId": member.UserId}); err != nil {
   407  		if err != sql.ErrNoRows {
   408  			return nil, errors.Wrapf(err, "failed to get GroupMember with groupId=%s and userId=%s", groupID, userID)
   409  		}
   410  	}
   411  
   412  	if retrievedMember == nil {
   413  		if err := s.GetMaster().Insert(member); err != nil {
   414  			if IsUniqueConstraintError(err, []string{"GroupId", "UserId", "groupmembers_pkey", "PRIMARY"}) {
   415  				return nil, store.NewErrInvalidInput("Member", "<groupId, userId>", fmt.Sprintf("<%s, %s>", groupID, userID))
   416  			}
   417  			return nil, errors.Wrap(err, "failed to save Member")
   418  		}
   419  	} else {
   420  		member.DeleteAt = 0
   421  		var rowsChanged int64
   422  		var err error
   423  		if rowsChanged, err = s.GetMaster().Update(member); err != nil {
   424  			return nil, errors.Wrapf(err, "failed to update GroupMember with groupId=%s and userId=%s", groupID, userID)
   425  		}
   426  		if rowsChanged > 1 {
   427  			return nil, errors.Wrapf(err, "multiple GroupMembers were updated: %d", rowsChanged)
   428  		}
   429  	}
   430  
   431  	return member, nil
   432  }
   433  
   434  func (s *SqlGroupStore) DeleteMember(groupID string, userID string) (*model.GroupMember, error) {
   435  	var retrievedMember *model.GroupMember
   436  	if err := s.GetReplica().SelectOne(&retrievedMember, "SELECT * FROM GroupMembers WHERE GroupId = :GroupId AND UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"GroupId": groupID, "UserId": userID}); err != nil {
   437  		if err == sql.ErrNoRows {
   438  			return nil, store.NewErrNotFound("GroupMember", fmt.Sprintf("groupId=%s, userId=%s", groupID, userID))
   439  		}
   440  		return nil, errors.Wrapf(err, "failed to get GroupMember with groupId=%s and userId=%s", groupID, userID)
   441  	}
   442  
   443  	retrievedMember.DeleteAt = model.GetMillis()
   444  
   445  	if _, err := s.GetMaster().Update(retrievedMember); err != nil {
   446  		return nil, errors.Wrapf(err, "failed to update GroupMember with groupId=%s and userId=%s", groupID, userID)
   447  	}
   448  
   449  	return retrievedMember, nil
   450  }
   451  
   452  func (s *SqlGroupStore) PermanentDeleteMembersByUser(userId string) error {
   453  	if _, err := s.GetMaster().Exec("DELETE FROM GroupMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
   454  		return errors.Wrapf(err, "failed to permanent delete GroupMember with userId=%s", userId)
   455  	}
   456  	return nil
   457  }
   458  
   459  func (s *SqlGroupStore) CreateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, error) {
   460  	if err := groupSyncable.IsValid(); err != nil {
   461  		return nil, err
   462  	}
   463  
   464  	// Reset values that shouldn't be updatable by parameter
   465  	groupSyncable.DeleteAt = 0
   466  	groupSyncable.CreateAt = model.GetMillis()
   467  	groupSyncable.UpdateAt = groupSyncable.CreateAt
   468  
   469  	var insertErr error
   470  
   471  	switch groupSyncable.Type {
   472  	case model.GroupSyncableTypeTeam:
   473  		if _, err := s.Team().Get(groupSyncable.SyncableId); err != nil {
   474  			return nil, err
   475  		}
   476  
   477  		insertErr = s.GetMaster().Insert(groupSyncableToGroupTeam(groupSyncable))
   478  	case model.GroupSyncableTypeChannel:
   479  		var channel *model.Channel
   480  		channel, err := s.Channel().Get(groupSyncable.SyncableId, false)
   481  		if err != nil {
   482  			return nil, err
   483  		}
   484  		insertErr = s.GetMaster().Insert(groupSyncableToGroupChannel(groupSyncable))
   485  		groupSyncable.TeamID = channel.TeamId
   486  	default:
   487  		return nil, fmt.Errorf("invalid GroupSyncableType: %s", groupSyncable.Type)
   488  	}
   489  
   490  	if insertErr != nil {
   491  		return nil, errors.Wrap(insertErr, "unable to insert GroupSyncable")
   492  	}
   493  
   494  	return groupSyncable, nil
   495  }
   496  
   497  func (s *SqlGroupStore) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, error) {
   498  	groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
   499  	if err != nil {
   500  		if err == sql.ErrNoRows {
   501  			return nil, store.NewErrNotFound("GroupSyncable", fmt.Sprintf("groupId=%s, syncableId=%s, syncableType=%s", groupID, syncableID, syncableType))
   502  		}
   503  		return nil, errors.Wrapf(err, "failed to find GroupSyncable with groupId=%s, syncableId=%s, syncableType=%s", groupID, syncableID, syncableType)
   504  	}
   505  
   506  	return groupSyncable, nil
   507  }
   508  
   509  func (s *SqlGroupStore) getGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, error) {
   510  	var err error
   511  	var result interface{}
   512  
   513  	switch syncableType {
   514  	case model.GroupSyncableTypeTeam:
   515  		result, err = s.GetReplica().Get(groupTeam{}, groupID, syncableID)
   516  	case model.GroupSyncableTypeChannel:
   517  		result, err = s.GetReplica().Get(groupChannel{}, groupID, syncableID)
   518  	}
   519  
   520  	if err != nil {
   521  		return nil, err
   522  	}
   523  
   524  	if result == nil {
   525  		return nil, sql.ErrNoRows
   526  	}
   527  
   528  	groupSyncable := model.GroupSyncable{}
   529  	switch syncableType {
   530  	case model.GroupSyncableTypeTeam:
   531  		groupTeam := result.(*groupTeam)
   532  		groupSyncable.SyncableId = groupTeam.TeamId
   533  		groupSyncable.GroupId = groupTeam.GroupId
   534  		groupSyncable.AutoAdd = groupTeam.AutoAdd
   535  		groupSyncable.CreateAt = groupTeam.CreateAt
   536  		groupSyncable.DeleteAt = groupTeam.DeleteAt
   537  		groupSyncable.UpdateAt = groupTeam.UpdateAt
   538  		groupSyncable.Type = syncableType
   539  	case model.GroupSyncableTypeChannel:
   540  		groupChannel := result.(*groupChannel)
   541  		groupSyncable.SyncableId = groupChannel.ChannelId
   542  		groupSyncable.GroupId = groupChannel.GroupId
   543  		groupSyncable.AutoAdd = groupChannel.AutoAdd
   544  		groupSyncable.CreateAt = groupChannel.CreateAt
   545  		groupSyncable.DeleteAt = groupChannel.DeleteAt
   546  		groupSyncable.UpdateAt = groupChannel.UpdateAt
   547  		groupSyncable.Type = syncableType
   548  	default:
   549  		return nil, fmt.Errorf("unable to convert syncableType: %s", syncableType.String())
   550  	}
   551  
   552  	return &groupSyncable, nil
   553  }
   554  
   555  func (s *SqlGroupStore) GetAllGroupSyncablesByGroupId(groupID string, syncableType model.GroupSyncableType) ([]*model.GroupSyncable, error) {
   556  	args := map[string]interface{}{"GroupId": groupID}
   557  
   558  	groupSyncables := []*model.GroupSyncable{}
   559  
   560  	switch syncableType {
   561  	case model.GroupSyncableTypeTeam:
   562  		sqlQuery := `
   563  			SELECT
   564  				GroupTeams.*,
   565  				Teams.DisplayName AS TeamDisplayName,
   566  				Teams.Type AS TeamType
   567  			FROM
   568  				GroupTeams
   569  				JOIN Teams ON Teams.Id = GroupTeams.TeamId
   570  			WHERE
   571  				GroupId = :GroupId AND GroupTeams.DeleteAt = 0`
   572  
   573  		results := []*groupTeamJoin{}
   574  		_, err := s.GetReplica().Select(&results, sqlQuery, args)
   575  		if err != nil {
   576  			return nil, errors.Wrapf(err, "failed to find GroupTeams with groupId=%s", groupID)
   577  		}
   578  		for _, result := range results {
   579  			groupSyncable := &model.GroupSyncable{
   580  				SyncableId:      result.TeamId,
   581  				GroupId:         result.GroupId,
   582  				AutoAdd:         result.AutoAdd,
   583  				CreateAt:        result.CreateAt,
   584  				DeleteAt:        result.DeleteAt,
   585  				UpdateAt:        result.UpdateAt,
   586  				Type:            syncableType,
   587  				TeamDisplayName: result.TeamDisplayName,
   588  				TeamType:        result.TeamType,
   589  				SchemeAdmin:     result.SchemeAdmin,
   590  			}
   591  			groupSyncables = append(groupSyncables, groupSyncable)
   592  		}
   593  	case model.GroupSyncableTypeChannel:
   594  		sqlQuery := `
   595  			SELECT
   596  				GroupChannels.*,
   597  				Channels.DisplayName AS ChannelDisplayName,
   598  				Teams.DisplayName AS TeamDisplayName,
   599  				Channels.Type As ChannelType,
   600  				Teams.Type As TeamType,
   601  				Teams.Id AS TeamId
   602  			FROM
   603  				GroupChannels
   604  				JOIN Channels ON Channels.Id = GroupChannels.ChannelId
   605  				JOIN Teams ON Teams.Id = Channels.TeamId
   606  			WHERE
   607  				GroupId = :GroupId AND GroupChannels.DeleteAt = 0`
   608  
   609  		results := []*groupChannelJoin{}
   610  		_, err := s.GetReplica().Select(&results, sqlQuery, args)
   611  		if err != nil {
   612  			return nil, errors.Wrapf(err, "failed to find GroupChannels with groupId=%s", groupID)
   613  		}
   614  		for _, result := range results {
   615  			groupSyncable := &model.GroupSyncable{
   616  				SyncableId:         result.ChannelId,
   617  				GroupId:            result.GroupId,
   618  				AutoAdd:            result.AutoAdd,
   619  				CreateAt:           result.CreateAt,
   620  				DeleteAt:           result.DeleteAt,
   621  				UpdateAt:           result.UpdateAt,
   622  				Type:               syncableType,
   623  				ChannelDisplayName: result.ChannelDisplayName,
   624  				ChannelType:        result.ChannelType,
   625  				TeamDisplayName:    result.TeamDisplayName,
   626  				TeamType:           result.TeamType,
   627  				TeamID:             result.TeamID,
   628  				SchemeAdmin:        result.SchemeAdmin,
   629  			}
   630  			groupSyncables = append(groupSyncables, groupSyncable)
   631  		}
   632  	}
   633  
   634  	return groupSyncables, nil
   635  }
   636  
   637  func (s *SqlGroupStore) UpdateGroupSyncable(groupSyncable *model.GroupSyncable) (*model.GroupSyncable, error) {
   638  	retrievedGroupSyncable, err := s.getGroupSyncable(groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type)
   639  	if err != nil {
   640  		if err == sql.ErrNoRows {
   641  			return nil, errors.Wrap(store.NewErrNotFound("GroupSyncable", fmt.Sprintf("groupId=%s, syncableId=%s, syncableType=%s", groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type)), "GroupSyncable not found")
   642  		}
   643  		return nil, errors.Wrapf(err, "failed to find GroupSyncable with groupId=%s, syncableId=%s, syncableType=%s", groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type)
   644  	}
   645  
   646  	if err := groupSyncable.IsValid(); err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	// If updating DeleteAt it can only be to 0
   651  	if groupSyncable.DeleteAt != retrievedGroupSyncable.DeleteAt && groupSyncable.DeleteAt != 0 {
   652  		return nil, errors.New("DeleteAt should be 0 when updating")
   653  	}
   654  
   655  	// Reset these properties, don't update them based on input
   656  	groupSyncable.CreateAt = retrievedGroupSyncable.CreateAt
   657  	groupSyncable.UpdateAt = model.GetMillis()
   658  
   659  	switch groupSyncable.Type {
   660  	case model.GroupSyncableTypeTeam:
   661  		_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
   662  	case model.GroupSyncableTypeChannel:
   663  		// We need to get the TeamId so redux can manage channels when teams are unlinked
   664  		var channel *model.Channel
   665  		channel, channelErr := s.Channel().Get(groupSyncable.SyncableId, false)
   666  		if channelErr != nil {
   667  			return nil, channelErr
   668  		}
   669  
   670  		_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
   671  
   672  		groupSyncable.TeamID = channel.TeamId
   673  	default:
   674  		return nil, fmt.Errorf("invalid GroupSyncableType: %s", groupSyncable.Type)
   675  	}
   676  
   677  	if err != nil {
   678  		return nil, errors.Wrap(err, "failed to update GroupSyncable")
   679  	}
   680  
   681  	return groupSyncable, nil
   682  }
   683  
   684  func (s *SqlGroupStore) DeleteGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, error) {
   685  	groupSyncable, err := s.getGroupSyncable(groupID, syncableID, syncableType)
   686  	if err != nil {
   687  		if err == sql.ErrNoRows {
   688  			return nil, store.NewErrNotFound("GroupSyncable", fmt.Sprintf("groupId=%s, syncableId=%s, syncableType=%s", groupID, syncableID, syncableType))
   689  		}
   690  		return nil, errors.Wrapf(err, "failed to find GroupSyncable with groupId=%s, syncableId=%s, syncableType=%s", groupID, syncableID, syncableType)
   691  	}
   692  
   693  	if groupSyncable.DeleteAt != 0 {
   694  		return nil, store.NewErrInvalidInput("GroupSyncable", "<groupId, syncableId, syncableType>", fmt.Sprintf("<%s, %s, %s>", groupSyncable.GroupId, groupSyncable.SyncableId, groupSyncable.Type))
   695  	}
   696  
   697  	time := model.GetMillis()
   698  	groupSyncable.DeleteAt = time
   699  	groupSyncable.UpdateAt = time
   700  
   701  	switch groupSyncable.Type {
   702  	case model.GroupSyncableTypeTeam:
   703  		_, err = s.GetMaster().Update(groupSyncableToGroupTeam(groupSyncable))
   704  	case model.GroupSyncableTypeChannel:
   705  		_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
   706  	default:
   707  		return nil, fmt.Errorf("invalid GroupSyncableType: %s", groupSyncable.Type)
   708  	}
   709  
   710  	if err != nil {
   711  		return nil, errors.Wrap(err, "failed to update GroupSyncable")
   712  	}
   713  
   714  	return groupSyncable, nil
   715  }
   716  
   717  func (s *SqlGroupStore) TeamMembersToAdd(since int64, teamID *string, includeRemovedMembers bool) ([]*model.UserTeamIDPair, error) {
   718  	builder := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupTeams.TeamId").
   719  		From("GroupMembers").
   720  		Join("GroupTeams ON GroupTeams.GroupId = GroupMembers.GroupId").
   721  		Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
   722  		Join("Teams ON Teams.Id = GroupTeams.TeamId").
   723  		Where(sq.Eq{
   724  			"UserGroups.DeleteAt":   0,
   725  			"GroupTeams.DeleteAt":   0,
   726  			"GroupTeams.AutoAdd":    true,
   727  			"GroupMembers.DeleteAt": 0,
   728  			"Teams.DeleteAt":        0,
   729  		})
   730  
   731  	if !includeRemovedMembers {
   732  		builder = builder.
   733  			JoinClause("LEFT OUTER JOIN TeamMembers ON TeamMembers.TeamId = GroupTeams.TeamId AND TeamMembers.UserId = GroupMembers.UserId").
   734  			Where(sq.Eq{"TeamMembers.UserId": nil}).
   735  			Where(sq.Or{
   736  				sq.GtOrEq{"GroupMembers.CreateAt": since},
   737  				sq.GtOrEq{"GroupTeams.UpdateAt": since},
   738  			})
   739  	}
   740  	if teamID != nil {
   741  		builder = builder.Where(sq.Eq{"Teams.Id": *teamID})
   742  	}
   743  
   744  	query, params, err := builder.ToSql()
   745  	if err != nil {
   746  		return nil, errors.Wrap(err, "team_members_to_add_tosql")
   747  	}
   748  
   749  	var teamMembers []*model.UserTeamIDPair
   750  
   751  	_, err = s.GetMaster().Select(&teamMembers, query, params...)
   752  	if err != nil {
   753  		return nil, errors.Wrap(err, "failed to find UserTeamIDPairs")
   754  	}
   755  
   756  	return teamMembers, nil
   757  }
   758  
   759  func (s *SqlGroupStore) ChannelMembersToAdd(since int64, channelID *string, includeRemovedMembers bool) ([]*model.UserChannelIDPair, error) {
   760  	builder := s.getQueryBuilder().Select("GroupMembers.UserId", "GroupChannels.ChannelId").
   761  		From("GroupMembers").
   762  		Join("GroupChannels ON GroupChannels.GroupId = GroupMembers.GroupId").
   763  		Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
   764  		Join("Channels ON Channels.Id = GroupChannels.ChannelId").
   765  		Where(sq.Eq{
   766  			"UserGroups.DeleteAt":    0,
   767  			"GroupChannels.DeleteAt": 0,
   768  			"GroupChannels.AutoAdd":  true,
   769  			"GroupMembers.DeleteAt":  0,
   770  			"Channels.DeleteAt":      0,
   771  		})
   772  
   773  	if !includeRemovedMembers {
   774  		builder = builder.
   775  			JoinClause("LEFT OUTER JOIN ChannelMemberHistory ON ChannelMemberHistory.ChannelId = GroupChannels.ChannelId AND ChannelMemberHistory.UserId = GroupMembers.UserId").
   776  			Where(sq.Eq{
   777  				"ChannelMemberHistory.UserId":    nil,
   778  				"ChannelMemberHistory.LeaveTime": nil,
   779  			}).
   780  			Where(sq.Or{
   781  				sq.GtOrEq{"GroupMembers.CreateAt": since},
   782  				sq.GtOrEq{"GroupChannels.UpdateAt": since},
   783  			})
   784  	}
   785  	if channelID != nil {
   786  		builder = builder.Where(sq.Eq{"Channels.Id": *channelID})
   787  	}
   788  
   789  	query, params, err := builder.ToSql()
   790  	if err != nil {
   791  		return nil, errors.Wrap(err, "channel_members_to_add_tosql")
   792  	}
   793  
   794  	var channelMembers []*model.UserChannelIDPair
   795  
   796  	_, err = s.GetMaster().Select(&channelMembers, query, params...)
   797  	if err != nil {
   798  		return nil, errors.Wrap(err, "failed to find UserChannelIDPairs")
   799  	}
   800  
   801  	return channelMembers, nil
   802  }
   803  
   804  func groupSyncableToGroupTeam(groupSyncable *model.GroupSyncable) *groupTeam {
   805  	return &groupTeam{
   806  		GroupSyncable: *groupSyncable,
   807  		TeamId:        groupSyncable.SyncableId,
   808  	}
   809  }
   810  
   811  func groupSyncableToGroupChannel(groupSyncable *model.GroupSyncable) *groupChannel {
   812  	return &groupChannel{
   813  		GroupSyncable: *groupSyncable,
   814  		ChannelId:     groupSyncable.SyncableId,
   815  	}
   816  }
   817  
   818  func (s *SqlGroupStore) TeamMembersToRemove(teamID *string) ([]*model.TeamMember, error) {
   819  	whereStmt := `
   820  		(TeamMembers.TeamId,
   821  			TeamMembers.UserId)
   822  		NOT IN (
   823  			SELECT
   824  				Teams.Id AS TeamId,
   825  				GroupMembers.UserId
   826  			FROM
   827  				Teams
   828  				JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id
   829  				JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId
   830  				JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
   831  			WHERE
   832  				Teams.GroupConstrained = TRUE
   833  				AND GroupTeams.DeleteAt = 0
   834  				AND UserGroups.DeleteAt = 0
   835  				AND Teams.DeleteAt = 0
   836  				AND GroupMembers.DeleteAt = 0
   837  			GROUP BY
   838  				Teams.Id,
   839  				GroupMembers.UserId)`
   840  
   841  	builder := s.getQueryBuilder().Select(
   842  		"TeamMembers.TeamId",
   843  		"TeamMembers.UserId",
   844  		"TeamMembers.Roles",
   845  		"TeamMembers.DeleteAt",
   846  		"TeamMembers.SchemeUser",
   847  		"TeamMembers.SchemeAdmin",
   848  		"(TeamMembers.SchemeGuest IS NOT NULL AND TeamMembers.SchemeGuest) AS SchemeGuest",
   849  	).
   850  		From("TeamMembers").
   851  		Join("Teams ON Teams.Id = TeamMembers.TeamId").
   852  		LeftJoin("Bots ON Bots.UserId = TeamMembers.UserId").
   853  		Where(sq.Eq{"TeamMembers.DeleteAt": 0, "Teams.DeleteAt": 0, "Teams.GroupConstrained": true, "Bots.UserId": nil}).
   854  		Where(whereStmt)
   855  
   856  	if teamID != nil {
   857  		builder = builder.Where(sq.Eq{"TeamMembers.TeamId": *teamID})
   858  	}
   859  
   860  	query, params, err := builder.ToSql()
   861  	if err != nil {
   862  		return nil, errors.Wrap(err, "team_members_to_remove_tosql")
   863  	}
   864  
   865  	var teamMembers []*model.TeamMember
   866  
   867  	_, err = s.GetReplica().Select(&teamMembers, query, params...)
   868  	if err != nil {
   869  		return nil, errors.Wrap(err, "failed to find TeamMembers")
   870  	}
   871  
   872  	return teamMembers, nil
   873  }
   874  
   875  func (s *SqlGroupStore) CountGroupsByChannel(channelId string, opts model.GroupSearchOpts) (int64, error) {
   876  	countQuery := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeChannel, selectCountGroups, channelId, opts)
   877  
   878  	countQueryString, args, err := countQuery.ToSql()
   879  	if err != nil {
   880  		return int64(0), errors.Wrap(err, "count_groups_by_channel_tosql")
   881  	}
   882  
   883  	count, err := s.GetReplica().SelectInt(countQueryString, args...)
   884  	if err != nil {
   885  		return int64(0), errors.Wrapf(err, "failed to count Groups by channel with channelId=%s", channelId)
   886  	}
   887  
   888  	return count, nil
   889  }
   890  
   891  func (s *SqlGroupStore) GetGroupsByChannel(channelId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, error) {
   892  	query := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeChannel, selectGroups, channelId, opts)
   893  
   894  	if opts.PageOpts != nil {
   895  		offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
   896  		query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
   897  	}
   898  
   899  	queryString, args, err := query.ToSql()
   900  	if err != nil {
   901  		return nil, errors.Wrap(err, "get_groups_by_channel_tosql")
   902  	}
   903  
   904  	var groups []*model.GroupWithSchemeAdmin
   905  
   906  	_, err = s.GetReplica().Select(&groups, queryString, args...)
   907  	if err != nil {
   908  		return nil, errors.Wrapf(err, "failed to find Groups with channelId=%s", channelId)
   909  	}
   910  
   911  	return groups, nil
   912  }
   913  
   914  func (s *SqlGroupStore) ChannelMembersToRemove(channelID *string) ([]*model.ChannelMember, error) {
   915  	whereStmt := `
   916  		(ChannelMembers.ChannelId,
   917  			ChannelMembers.UserId)
   918  		NOT IN (
   919  			SELECT
   920  				Channels.Id AS ChannelId,
   921  				GroupMembers.UserId
   922  			FROM
   923  				Channels
   924  				JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id
   925  				JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId
   926  				JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
   927  			WHERE
   928  				Channels.GroupConstrained = TRUE
   929  				AND GroupChannels.DeleteAt = 0
   930  				AND UserGroups.DeleteAt = 0
   931  				AND Channels.DeleteAt = 0
   932  				AND GroupMembers.DeleteAt = 0
   933  			GROUP BY
   934  				Channels.Id,
   935  				GroupMembers.UserId)`
   936  
   937  	builder := s.getQueryBuilder().Select(
   938  		"ChannelMembers.ChannelId",
   939  		"ChannelMembers.UserId",
   940  		"ChannelMembers.LastViewedAt",
   941  		"ChannelMembers.MsgCount",
   942  		"ChannelMembers.MsgCountRoot",
   943  		"ChannelMembers.MentionCount",
   944  		"ChannelMembers.MentionCountRoot",
   945  		"ChannelMembers.NotifyProps",
   946  		"ChannelMembers.LastUpdateAt",
   947  		"ChannelMembers.LastUpdateAt",
   948  		"ChannelMembers.SchemeUser",
   949  		"ChannelMembers.SchemeAdmin",
   950  		"(ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) AS SchemeGuest",
   951  	).
   952  		From("ChannelMembers").
   953  		Join("Channels ON Channels.Id = ChannelMembers.ChannelId").
   954  		LeftJoin("Bots ON Bots.UserId = ChannelMembers.UserId").
   955  		Where(sq.Eq{"Channels.DeleteAt": 0, "Channels.GroupConstrained": true, "Bots.UserId": nil}).
   956  		Where(whereStmt)
   957  
   958  	if channelID != nil {
   959  		builder = builder.Where(sq.Eq{"ChannelMembers.ChannelId": *channelID})
   960  	}
   961  
   962  	query, params, err := builder.ToSql()
   963  	if err != nil {
   964  		return nil, errors.Wrap(err, "channel_members_to_remove_tosql")
   965  	}
   966  
   967  	var channelMembers []*model.ChannelMember
   968  
   969  	_, err = s.GetReplica().Select(&channelMembers, query, params...)
   970  	if err != nil {
   971  		return nil, errors.Wrap(err, "failed to find ChannelMembers")
   972  	}
   973  
   974  	return channelMembers, nil
   975  }
   976  
   977  func (s *SqlGroupStore) groupsBySyncableBaseQuery(st model.GroupSyncableType, t selectType, syncableID string, opts model.GroupSearchOpts) sq.SelectBuilder {
   978  	selectStrs := map[selectType]string{
   979  		selectGroups:      "ug.*, gs.SchemeAdmin AS SyncableSchemeAdmin",
   980  		selectCountGroups: "COUNT(*)",
   981  	}
   982  
   983  	var table string
   984  	var idCol string
   985  	if st == model.GroupSyncableTypeTeam {
   986  		table = "GroupTeams"
   987  		idCol = "TeamId"
   988  	} else {
   989  		table = "GroupChannels"
   990  		idCol = "ChannelId"
   991  	}
   992  
   993  	query := s.getQueryBuilder().
   994  		Select(selectStrs[t]).
   995  		From(fmt.Sprintf("%s gs", table)).
   996  		LeftJoin("UserGroups ug ON gs.GroupId = ug.Id").
   997  		Where(fmt.Sprintf("ug.DeleteAt = 0 AND gs.%s = ? AND gs.DeleteAt = 0", idCol), syncableID)
   998  
   999  	if opts.IncludeMemberCount && t == selectGroups {
  1000  		query = s.getQueryBuilder().
  1001  			Select(fmt.Sprintf("ug.*, coalesce(Members.MemberCount, 0) AS MemberCount, Group%ss.SchemeAdmin AS SyncableSchemeAdmin", st)).
  1002  			From("UserGroups ug").
  1003  			LeftJoin("(SELECT GroupMembers.GroupId, COUNT(*) AS MemberCount FROM GroupMembers LEFT JOIN Users ON Users.Id = GroupMembers.UserId WHERE GroupMembers.DeleteAt = 0 AND Users.DeleteAt = 0 GROUP BY GroupId) AS Members ON Members.GroupId = ug.Id").
  1004  			LeftJoin(fmt.Sprintf("%[1]s ON %[1]s.GroupId = ug.Id", table)).
  1005  			Where(fmt.Sprintf("ug.DeleteAt = 0 AND %[1]s.DeleteAt = 0 AND %[1]s.%[2]s = ?", table, idCol), syncableID).
  1006  			OrderBy("ug.DisplayName")
  1007  	}
  1008  
  1009  	if opts.FilterAllowReference && t == selectGroups {
  1010  		query = query.Where("ug.AllowReference = true")
  1011  	}
  1012  
  1013  	if opts.Q != "" {
  1014  		pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
  1015  		operatorKeyword := "ILIKE"
  1016  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  1017  			operatorKeyword = "LIKE"
  1018  		}
  1019  		query = query.Where(fmt.Sprintf("(ug.Name %[1]s ? OR ug.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
  1020  	}
  1021  
  1022  	return query
  1023  }
  1024  
  1025  func (s *SqlGroupStore) getGroupsAssociatedToChannelsByTeam(teamID string, opts model.GroupSearchOpts) sq.SelectBuilder {
  1026  	query := s.getQueryBuilder().
  1027  		Select("gc.ChannelId, ug.*, gc.SchemeAdmin AS SyncableSchemeAdmin").
  1028  		From("UserGroups ug").
  1029  		LeftJoin(`
  1030  			(SELECT
  1031  				GroupChannels.GroupId, GroupChannels.ChannelId, GroupChannels.DeleteAt, GroupChannels.SchemeAdmin
  1032  			FROM
  1033  				GroupChannels
  1034  			LEFT JOIN
  1035  				Channels ON (Channels.Id = GroupChannels.ChannelId)
  1036  			WHERE
  1037  				GroupChannels.DeleteAt = 0
  1038  				AND Channels.DeleteAt = 0
  1039  				AND Channels.TeamId = ?) AS gc ON gc.GroupId = ug.Id`, teamID).
  1040  		Where("ug.DeleteAt = 0 AND gc.DeleteAt = 0").
  1041  		OrderBy("ug.DisplayName")
  1042  
  1043  	if opts.IncludeMemberCount {
  1044  		query = s.getQueryBuilder().
  1045  			Select("gc.ChannelId, ug.*, coalesce(Members.MemberCount, 0) AS MemberCount, gc.SchemeAdmin AS SyncableSchemeAdmin").
  1046  			From("UserGroups ug").
  1047  			LeftJoin(`
  1048  				(SELECT
  1049  					GroupChannels.ChannelId, GroupChannels.DeleteAt, GroupChannels.GroupId, GroupChannels.SchemeAdmin
  1050  				FROM
  1051  					GroupChannels
  1052  				LEFT JOIN
  1053  					Channels ON (Channels.Id = GroupChannels.ChannelId)
  1054  				WHERE
  1055  					GroupChannels.DeleteAt = 0
  1056  					AND Channels.DeleteAt = 0
  1057  					AND Channels.TeamId = ?) AS gc ON gc.GroupId = ug.Id`, teamID).
  1058  			LeftJoin(`(
  1059  				SELECT
  1060  					GroupMembers.GroupId, COUNT(*) AS MemberCount
  1061  				FROM
  1062  					GroupMembers
  1063  				LEFT JOIN
  1064  					Users ON Users.Id = GroupMembers.UserId
  1065  				WHERE
  1066  					GroupMembers.DeleteAt = 0
  1067  					AND Users.DeleteAt = 0
  1068  				GROUP BY GroupId) AS Members
  1069  			ON Members.GroupId = ug.Id`).
  1070  			Where("ug.DeleteAt = 0 AND gc.DeleteAt = 0").
  1071  			OrderBy("ug.DisplayName")
  1072  	}
  1073  
  1074  	if opts.FilterAllowReference {
  1075  		query = query.Where("ug.AllowReference = true")
  1076  	}
  1077  
  1078  	if opts.Q != "" {
  1079  		pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
  1080  		operatorKeyword := "ILIKE"
  1081  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  1082  			operatorKeyword = "LIKE"
  1083  		}
  1084  		query = query.Where(fmt.Sprintf("(ug.Name %[1]s ? OR ug.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
  1085  	}
  1086  
  1087  	return query
  1088  }
  1089  
  1090  func (s *SqlGroupStore) CountGroupsByTeam(teamId string, opts model.GroupSearchOpts) (int64, error) {
  1091  	countQuery := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeTeam, selectCountGroups, teamId, opts)
  1092  
  1093  	countQueryString, args, err := countQuery.ToSql()
  1094  	if err != nil {
  1095  		return int64(0), errors.Wrap(err, "count_groups_by_team_tosql")
  1096  	}
  1097  
  1098  	count, err := s.GetReplica().SelectInt(countQueryString, args...)
  1099  	if err != nil {
  1100  		return int64(0), errors.Wrapf(err, "failed to count Groups with teamId=%s", teamId)
  1101  	}
  1102  
  1103  	return count, nil
  1104  }
  1105  
  1106  func (s *SqlGroupStore) GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, error) {
  1107  	query := s.groupsBySyncableBaseQuery(model.GroupSyncableTypeTeam, selectGroups, teamId, opts)
  1108  
  1109  	if opts.PageOpts != nil {
  1110  		offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
  1111  		query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
  1112  	}
  1113  
  1114  	queryString, args, err := query.ToSql()
  1115  	if err != nil {
  1116  		return nil, errors.Wrap(err, "get_groups_by_team_tosql")
  1117  	}
  1118  
  1119  	var groups []*model.GroupWithSchemeAdmin
  1120  
  1121  	_, err = s.GetReplica().Select(&groups, queryString, args...)
  1122  	if err != nil {
  1123  		return nil, errors.Wrapf(err, "failed to find Groups with teamId=%s", teamId)
  1124  	}
  1125  
  1126  	return groups, nil
  1127  }
  1128  
  1129  func (s *SqlGroupStore) GetGroupsAssociatedToChannelsByTeam(teamId string, opts model.GroupSearchOpts) (map[string][]*model.GroupWithSchemeAdmin, error) {
  1130  	query := s.getGroupsAssociatedToChannelsByTeam(teamId, opts)
  1131  
  1132  	if opts.PageOpts != nil {
  1133  		offset := uint64(opts.PageOpts.Page * opts.PageOpts.PerPage)
  1134  		query = query.OrderBy("ug.DisplayName").Limit(uint64(opts.PageOpts.PerPage)).Offset(offset)
  1135  	}
  1136  
  1137  	queryString, args, err := query.ToSql()
  1138  	if err != nil {
  1139  		return nil, errors.Wrap(err, "get_groups_associated_to_channel_by_team_tosql")
  1140  	}
  1141  
  1142  	var tgroups []*model.GroupsAssociatedToChannelWithSchemeAdmin
  1143  
  1144  	_, err = s.GetReplica().Select(&tgroups, queryString, args...)
  1145  	if err != nil {
  1146  		return nil, errors.Wrapf(err, "failed to find Groups with teamId=%s", teamId)
  1147  	}
  1148  
  1149  	groups := map[string][]*model.GroupWithSchemeAdmin{}
  1150  	for _, tgroup := range tgroups {
  1151  		var group = model.GroupWithSchemeAdmin{}
  1152  		group.Group = tgroup.Group
  1153  		group.SchemeAdmin = tgroup.SchemeAdmin
  1154  
  1155  		if val, ok := groups[tgroup.ChannelId]; ok {
  1156  			groups[tgroup.ChannelId] = append(val, &group)
  1157  		} else {
  1158  			groups[tgroup.ChannelId] = []*model.GroupWithSchemeAdmin{&group}
  1159  		}
  1160  	}
  1161  
  1162  	return groups, nil
  1163  }
  1164  
  1165  func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts) ([]*model.Group, error) {
  1166  	var groups []*model.Group
  1167  
  1168  	groupsQuery := s.getQueryBuilder().Select("g.*")
  1169  
  1170  	if opts.IncludeMemberCount {
  1171  		groupsQuery = s.getQueryBuilder().
  1172  			Select("g.*, coalesce(Members.MemberCount, 0) AS MemberCount").
  1173  			LeftJoin("(SELECT GroupMembers.GroupId, COUNT(*) AS MemberCount FROM GroupMembers LEFT JOIN Users ON Users.Id = GroupMembers.UserId WHERE GroupMembers.DeleteAt = 0 AND Users.DeleteAt = 0 GROUP BY GroupId) AS Members ON Members.GroupId = g.Id")
  1174  	}
  1175  
  1176  	groupsQuery = groupsQuery.
  1177  		From("UserGroups g").
  1178  		OrderBy("g.DisplayName")
  1179  
  1180  	if opts.Since > 0 {
  1181  		groupsQuery = groupsQuery.Where(sq.Gt{
  1182  			"g.UpdateAt": opts.Since,
  1183  		})
  1184  	} else {
  1185  		groupsQuery = groupsQuery.Where("g.DeleteAt = 0")
  1186  	}
  1187  
  1188  	if perPage != 0 {
  1189  		groupsQuery = groupsQuery.
  1190  			Limit(uint64(perPage)).
  1191  			Offset(uint64(page * perPage))
  1192  	}
  1193  
  1194  	if opts.FilterAllowReference {
  1195  		groupsQuery = groupsQuery.Where("g.AllowReference = true")
  1196  	}
  1197  
  1198  	if opts.Q != "" {
  1199  		pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
  1200  		operatorKeyword := "ILIKE"
  1201  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  1202  			operatorKeyword = "LIKE"
  1203  		}
  1204  		groupsQuery = groupsQuery.Where(fmt.Sprintf("(g.Name %[1]s ? OR g.DisplayName %[1]s ?)", operatorKeyword), pattern, pattern)
  1205  	}
  1206  
  1207  	if len(opts.NotAssociatedToTeam) == 26 {
  1208  		groupsQuery = groupsQuery.Where(`
  1209  			g.Id NOT IN (
  1210  				SELECT
  1211  					Id
  1212  				FROM
  1213  					UserGroups
  1214  					JOIN GroupTeams ON GroupTeams.GroupId = UserGroups.Id
  1215  				WHERE
  1216  					GroupTeams.DeleteAt = 0
  1217  					AND UserGroups.DeleteAt = 0
  1218  					AND GroupTeams.TeamId = ?
  1219  			)
  1220  		`, opts.NotAssociatedToTeam)
  1221  	}
  1222  
  1223  	if len(opts.NotAssociatedToChannel) == 26 {
  1224  		groupsQuery = groupsQuery.Where(`
  1225  			g.Id NOT IN (
  1226  				SELECT
  1227  					Id
  1228  				FROM
  1229  					UserGroups
  1230  					JOIN GroupChannels ON GroupChannels.GroupId = UserGroups.Id
  1231  				WHERE
  1232  					GroupChannels.DeleteAt = 0
  1233  					AND UserGroups.DeleteAt = 0
  1234  					AND GroupChannels.ChannelId = ?
  1235  			)
  1236  		`, opts.NotAssociatedToChannel)
  1237  	}
  1238  
  1239  	if opts.FilterParentTeamPermitted && len(opts.NotAssociatedToChannel) == 26 {
  1240  		groupsQuery = groupsQuery.Where(`
  1241  			CASE
  1242  			WHEN (
  1243  				SELECT
  1244  					Teams.GroupConstrained
  1245  				FROM
  1246  					Teams
  1247  					JOIN Channels ON Channels.TeamId = Teams.Id
  1248  				WHERE
  1249  					Channels.Id = ?
  1250  			) THEN g.Id IN (
  1251  				SELECT
  1252  					GroupId
  1253  				FROM
  1254  					GroupTeams
  1255  				WHERE
  1256  					GroupTeams.DeleteAt = 0
  1257  					AND GroupTeams.TeamId = (
  1258  						SELECT
  1259  							TeamId
  1260  						FROM
  1261  							Channels
  1262  						WHERE
  1263  							Id = ?
  1264  					)
  1265  			)
  1266  			ELSE TRUE
  1267  		END
  1268  		`, opts.NotAssociatedToChannel, opts.NotAssociatedToChannel)
  1269  	}
  1270  
  1271  	queryString, args, err := groupsQuery.ToSql()
  1272  	if err != nil {
  1273  		return nil, errors.Wrap(err, "get_groups_tosql")
  1274  	}
  1275  
  1276  	if _, err = s.GetReplica().Select(&groups, queryString, args...); err != nil {
  1277  		return nil, errors.Wrap(err, "failed to find Groups")
  1278  	}
  1279  
  1280  	return groups, nil
  1281  }
  1282  
  1283  func (s *SqlGroupStore) teamMembersMinusGroupMembersQuery(teamID string, groupIDs []string, isCount bool) sq.SelectBuilder {
  1284  	var selectStr string
  1285  
  1286  	if isCount {
  1287  		selectStr = "count(DISTINCT Users.Id)"
  1288  	} else {
  1289  		tmpl := "Users.*, coalesce(TeamMembers.SchemeGuest, false), TeamMembers.SchemeAdmin, TeamMembers.SchemeUser, %s AS GroupIDs"
  1290  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  1291  			selectStr = fmt.Sprintf(tmpl, "group_concat(UserGroups.Id)")
  1292  		} else {
  1293  			selectStr = fmt.Sprintf(tmpl, "string_agg(UserGroups.Id, ',')")
  1294  		}
  1295  	}
  1296  
  1297  	subQuery := s.getQueryBuilder().Select("GroupMembers.UserId").
  1298  		From("GroupMembers").
  1299  		Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
  1300  		Where("GroupMembers.DeleteAt = 0").
  1301  		Where(fmt.Sprintf("GroupMembers.GroupId IN ('%s')", strings.Join(groupIDs, "', '")))
  1302  
  1303  	query, _ := subQuery.MustSql()
  1304  
  1305  	builder := s.getQueryBuilder().Select(selectStr).
  1306  		From("TeamMembers").
  1307  		Join("Teams ON Teams.Id = TeamMembers.TeamId").
  1308  		Join("Users ON Users.Id = TeamMembers.UserId").
  1309  		LeftJoin("Bots ON Bots.UserId = TeamMembers.UserId").
  1310  		LeftJoin("GroupMembers ON GroupMembers.UserId = Users.Id").
  1311  		LeftJoin("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
  1312  		Where("TeamMembers.DeleteAt = 0").
  1313  		Where("Teams.DeleteAt = 0").
  1314  		Where("Users.DeleteAt = 0").
  1315  		Where("Bots.UserId IS NULL").
  1316  		Where("Teams.Id = ?", teamID).
  1317  		Where(fmt.Sprintf("Users.Id NOT IN (%s)", query))
  1318  
  1319  	if !isCount {
  1320  		builder = builder.GroupBy("Users.Id, TeamMembers.SchemeGuest, TeamMembers.SchemeAdmin, TeamMembers.SchemeUser")
  1321  	}
  1322  
  1323  	return builder
  1324  }
  1325  
  1326  // TeamMembersMinusGroupMembers returns the set of users on the given team minus the set of users in the given
  1327  // groups.
  1328  func (s *SqlGroupStore) TeamMembersMinusGroupMembers(teamID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, error) {
  1329  	query := s.teamMembersMinusGroupMembersQuery(teamID, groupIDs, false)
  1330  	query = query.OrderBy("Users.Username ASC").Limit(uint64(perPage)).Offset(uint64(page * perPage))
  1331  
  1332  	queryString, args, err := query.ToSql()
  1333  	if err != nil {
  1334  		return nil, errors.Wrap(err, "team_members_minus_group_members")
  1335  	}
  1336  
  1337  	var users []*model.UserWithGroups
  1338  	if _, err = s.GetReplica().Select(&users, queryString, args...); err != nil {
  1339  		return nil, errors.Wrap(err, "failed to find UserWithGroups")
  1340  	}
  1341  
  1342  	return users, nil
  1343  }
  1344  
  1345  // CountTeamMembersMinusGroupMembers returns the count of the set of users on the given team minus the set of users
  1346  // in the given groups.
  1347  func (s *SqlGroupStore) CountTeamMembersMinusGroupMembers(teamID string, groupIDs []string) (int64, error) {
  1348  	queryString, args, err := s.teamMembersMinusGroupMembersQuery(teamID, groupIDs, true).ToSql()
  1349  	if err != nil {
  1350  		return 0, errors.Wrap(err, "count_team_members_minus_group_members_tosql")
  1351  	}
  1352  
  1353  	var count int64
  1354  	if count, err = s.GetReplica().SelectInt(queryString, args...); err != nil {
  1355  		return 0, errors.Wrap(err, "failed to count TeamMembers minus GroupMembers")
  1356  	}
  1357  
  1358  	return count, nil
  1359  }
  1360  
  1361  func (s *SqlGroupStore) channelMembersMinusGroupMembersQuery(channelID string, groupIDs []string, isCount bool) sq.SelectBuilder {
  1362  	var selectStr string
  1363  
  1364  	if isCount {
  1365  		selectStr = "count(DISTINCT Users.Id)"
  1366  	} else {
  1367  		tmpl := "Users.*, coalesce(ChannelMembers.SchemeGuest, false), ChannelMembers.SchemeAdmin, ChannelMembers.SchemeUser, %s AS GroupIDs"
  1368  		if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
  1369  			selectStr = fmt.Sprintf(tmpl, "group_concat(UserGroups.Id)")
  1370  		} else {
  1371  			selectStr = fmt.Sprintf(tmpl, "string_agg(UserGroups.Id, ',')")
  1372  		}
  1373  	}
  1374  
  1375  	subQuery := s.getQueryBuilder().Select("GroupMembers.UserId").
  1376  		From("GroupMembers").
  1377  		Join("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
  1378  		Where("GroupMembers.DeleteAt = 0").
  1379  		Where(fmt.Sprintf("GroupMembers.GroupId IN ('%s')", strings.Join(groupIDs, "', '")))
  1380  
  1381  	query, _ := subQuery.MustSql()
  1382  
  1383  	builder := s.getQueryBuilder().Select(selectStr).
  1384  		From("ChannelMembers").
  1385  		Join("Channels ON Channels.Id = ChannelMembers.ChannelId").
  1386  		Join("Users ON Users.Id = ChannelMembers.UserId").
  1387  		LeftJoin("Bots ON Bots.UserId = ChannelMembers.UserId").
  1388  		LeftJoin("GroupMembers ON GroupMembers.UserId = Users.Id").
  1389  		LeftJoin("UserGroups ON UserGroups.Id = GroupMembers.GroupId").
  1390  		Where("Channels.DeleteAt = 0").
  1391  		Where("Users.DeleteAt = 0").
  1392  		Where("Bots.UserId IS NULL").
  1393  		Where("Channels.Id = ?", channelID).
  1394  		Where(fmt.Sprintf("Users.Id NOT IN (%s)", query))
  1395  
  1396  	if !isCount {
  1397  		builder = builder.GroupBy("Users.Id, ChannelMembers.SchemeGuest, ChannelMembers.SchemeAdmin, ChannelMembers.SchemeUser")
  1398  	}
  1399  
  1400  	return builder
  1401  }
  1402  
  1403  // ChannelMembersMinusGroupMembers returns the set of users in the given channel minus the set of users in the given
  1404  // groups.
  1405  func (s *SqlGroupStore) ChannelMembersMinusGroupMembers(channelID string, groupIDs []string, page, perPage int) ([]*model.UserWithGroups, error) {
  1406  	query := s.channelMembersMinusGroupMembersQuery(channelID, groupIDs, false)
  1407  	query = query.OrderBy("Users.Username ASC").Limit(uint64(perPage)).Offset(uint64(page * perPage))
  1408  
  1409  	queryString, args, err := query.ToSql()
  1410  	if err != nil {
  1411  		return nil, errors.Wrap(err, "channel_members_minus_group_members_tosql")
  1412  	}
  1413  
  1414  	var users []*model.UserWithGroups
  1415  	if _, err = s.GetReplica().Select(&users, queryString, args...); err != nil {
  1416  		return nil, errors.Wrap(err, "failed to find UserWithGroups")
  1417  	}
  1418  
  1419  	return users, nil
  1420  }
  1421  
  1422  // CountChannelMembersMinusGroupMembers returns the count of the set of users in the given channel minus the set of users
  1423  // in the given groups.
  1424  func (s *SqlGroupStore) CountChannelMembersMinusGroupMembers(channelID string, groupIDs []string) (int64, error) {
  1425  	queryString, args, err := s.channelMembersMinusGroupMembersQuery(channelID, groupIDs, true).ToSql()
  1426  	if err != nil {
  1427  		return 0, errors.Wrap(err, "count_channel_members_minus_group_members_tosql")
  1428  	}
  1429  
  1430  	var count int64
  1431  	if count, err = s.GetReplica().SelectInt(queryString, args...); err != nil {
  1432  		return 0, errors.Wrap(err, "failed to count ChannelMembers")
  1433  	}
  1434  
  1435  	return count, nil
  1436  }
  1437  
  1438  func (s *SqlGroupStore) AdminRoleGroupsForSyncableMember(userID, syncableID string, syncableType model.GroupSyncableType) ([]string, error) {
  1439  	var groupIds []string
  1440  
  1441  	query := fmt.Sprintf(`
  1442  		SELECT
  1443  			GroupMembers.GroupId
  1444  		FROM
  1445  			GroupMembers
  1446  		INNER JOIN
  1447  			Group%[1]ss ON Group%[1]ss.GroupId = GroupMembers.GroupId
  1448  		WHERE
  1449  			GroupMembers.UserId = :UserId
  1450  			AND GroupMembers.DeleteAt = 0
  1451  			AND %[1]sId = :%[1]sId
  1452  			AND Group%[1]ss.DeleteAt = 0
  1453  			AND Group%[1]ss.SchemeAdmin = TRUE`, syncableType)
  1454  
  1455  	_, err := s.GetReplica().Select(&groupIds, query, map[string]interface{}{"UserId": userID, fmt.Sprintf("%sId", syncableType): syncableID})
  1456  	if err != nil {
  1457  		return nil, errors.Wrap(err, "failed to find Group ids")
  1458  	}
  1459  
  1460  	return groupIds, nil
  1461  }
  1462  
  1463  func (s *SqlGroupStore) PermittedSyncableAdmins(syncableID string, syncableType model.GroupSyncableType) ([]string, error) {
  1464  	builder := s.getQueryBuilder().Select("UserId").
  1465  		From(fmt.Sprintf("Group%ss", syncableType)).
  1466  		Join(fmt.Sprintf("GroupMembers ON GroupMembers.GroupId = Group%ss.GroupId AND Group%[1]ss.SchemeAdmin = TRUE AND GroupMembers.DeleteAt = 0", syncableType.String())).Where(fmt.Sprintf("Group%[1]ss.%[1]sId = ?", syncableType.String()), syncableID)
  1467  
  1468  	query, args, err := builder.ToSql()
  1469  	if err != nil {
  1470  		return nil, errors.Wrap(err, "permitted_syncable_admins_tosql")
  1471  	}
  1472  
  1473  	var userIDs []string
  1474  	if _, err = s.GetMaster().Select(&userIDs, query, args...); err != nil {
  1475  		return nil, errors.Wrapf(err, "failed to find User ids")
  1476  	}
  1477  
  1478  	return userIDs, nil
  1479  }
  1480  
  1481  func (s *SqlGroupStore) GroupCount() (int64, error) {
  1482  	return s.countTable("UserGroups")
  1483  }
  1484  
  1485  func (s *SqlGroupStore) GroupTeamCount() (int64, error) {
  1486  	return s.countTable("GroupTeams")
  1487  }
  1488  
  1489  func (s *SqlGroupStore) GroupChannelCount() (int64, error) {
  1490  	return s.countTable("GroupChannels")
  1491  }
  1492  
  1493  func (s *SqlGroupStore) GroupMemberCount() (int64, error) {
  1494  	return s.countTable("GroupMembers")
  1495  }
  1496  
  1497  func (s *SqlGroupStore) DistinctGroupMemberCount() (int64, error) {
  1498  	return s.countTableWithSelectAndWhere("COUNT(DISTINCT UserId)", "GroupMembers", nil)
  1499  }
  1500  
  1501  func (s *SqlGroupStore) GroupCountWithAllowReference() (int64, error) {
  1502  	return s.countTableWithSelectAndWhere("COUNT(*)", "UserGroups", sq.Eq{"AllowReference": true, "DeleteAt": 0})
  1503  }
  1504  
  1505  func (s *SqlGroupStore) countTable(tableName string) (int64, error) {
  1506  	return s.countTableWithSelectAndWhere("COUNT(*)", tableName, nil)
  1507  }
  1508  
  1509  func (s *SqlGroupStore) countTableWithSelectAndWhere(selectStr, tableName string, whereStmt map[string]interface{}) (int64, error) {
  1510  	if whereStmt == nil {
  1511  		whereStmt = sq.Eq{"DeleteAt": 0}
  1512  	}
  1513  
  1514  	query := s.getQueryBuilder().Select(selectStr).From(tableName).Where(whereStmt)
  1515  
  1516  	sql, args, err := query.ToSql()
  1517  	if err != nil {
  1518  		return 0, errors.Wrap(err, "count_table_with_select_and_where_tosql")
  1519  	}
  1520  
  1521  	count, err := s.GetReplica().SelectInt(sql, args...)
  1522  	if err != nil {
  1523  		return 0, errors.Wrapf(err, "failed to count from table %s", tableName)
  1524  	}
  1525  
  1526  	return count, nil
  1527  }