diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index f1958a4e437..c5a32473204 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -169,317 +169,6 @@ func (s *SQLStore) RunCategoryUUIDIDMigration() error { return nil } -func (s *SQLStore) createCategories(db sq.BaseRunner) error { - rows, err := s.getQueryBuilder(db). - Select("c.DisplayName, cm.UserId, c.TeamId, cm.ChannelId"). - From(s.tablePrefix + "boards boards"). - Join("ChannelMembers cm on boards.channel_id = cm.ChannelId"). - Join("Channels c on cm.ChannelId = c.id and (c.Type = 'O' or c.Type = 'P')"). - GroupBy("cm.UserId, c.TeamId, cm.ChannelId, c.DisplayName"). - Query() - - if err != nil { - s.logger.Error("get boards data error", mlog.Err(err)) - return err - } - defer s.CloseRows(rows) - - initQuery := func() sq.InsertBuilder { - return s.getQueryBuilder(db). - Insert(s.tablePrefix+"categories"). - Columns( - "id", - "name", - "user_id", - "team_id", - "channel_id", - "create_at", - "update_at", - "delete_at", - ) - } - // query will accumulate the insert values until the limit is - // reached, and then it will be stored and reset - query := initQuery() - // queryList stores those queries that already reached the limit - // to be run when all the data is processed - queryList := []sq.InsertBuilder{} - counter := 0 - now := model.GetMillis() - - for rows.Next() { - var displayName string - var userID string - var teamID string - var channelID string - - err := rows.Scan( - &displayName, - &userID, - &teamID, - &channelID, - ) - if err != nil { - return fmt.Errorf("cannot scan result while trying to create categories: %w", err) - } - - query = query.Values( - utils.NewID(utils.IDTypeNone), - displayName, - userID, - teamID, - channelID, - now, - 0, - 0, - ) - - counter++ - if counter%CategoryInsertBatch == 0 { - queryList = append(queryList, query) - query = initQuery() - } - } - - if counter%CategoryInsertBatch != 0 { - queryList = append(queryList, query) - } - - for _, q := range queryList { - if _, err := q.Exec(); err != nil { - return fmt.Errorf("cannot create category values: %w", err) - } - } - - return nil -} - -func (s *SQLStore) createCategoryBoards(db sq.BaseRunner) error { - rows, err := s.getQueryBuilder(db). - Select("categories.user_id, categories.id, boards.id"). - From(s.tablePrefix + "categories categories"). - Join(s.tablePrefix + "boards boards on categories.channel_id = boards.channel_id AND boards.is_template = false"). - Query() - - if err != nil { - s.logger.Error("get categories data error", mlog.Err(err)) - return err - } - defer s.CloseRows(rows) - - initQuery := func() sq.InsertBuilder { - return s.getQueryBuilder(db). - Insert(s.tablePrefix+"category_boards"). - Columns( - "id", - "user_id", - "category_id", - "board_id", - "create_at", - "update_at", - "delete_at", - ) - } - // query will accumulate the insert values until the limit is - // reached, and then it will be stored and reset - query := initQuery() - // queryList stores those queries that already reached the limit - // to be run when all the data is processed - queryList := []sq.InsertBuilder{} - counter := 0 - now := model.GetMillis() - - for rows.Next() { - var userID string - var categoryID string - var boardID string - - err := rows.Scan( - &userID, - &categoryID, - &boardID, - ) - if err != nil { - return fmt.Errorf("cannot scan result while trying to create category boards: %w", err) - } - - query = query.Values( - utils.NewID(utils.IDTypeNone), - userID, - categoryID, - boardID, - now, - 0, - 0, - ) - - counter++ - if counter%CategoryInsertBatch == 0 { - queryList = append(queryList, query) - query = initQuery() - } - } - - if counter%CategoryInsertBatch != 0 { - queryList = append(queryList, query) - } - - for _, q := range queryList { - if _, err := q.Exec(); err != nil { - return fmt.Errorf("cannot create category boards values: %w", err) - } - } - - return nil -} - -func (s *SQLStore) getDMBoards(tx sq.BaseRunner) ([]*model.Board, error) { - conditions := sq.And{ - sq.Eq{"team_id": ""}, - sq.Or{ - sq.Eq{"type": "D"}, - sq.Eq{"type": "G"}, - }, - } - - boards, err := s.getLegacyBoardsByCondition(tx, conditions) - if err != nil && model.IsErrNotFound(err) { - return []*model.Board{}, nil - } - - return boards, err -} - -// The destination is selected as the first team where all members -// of the DM are a part of. If no such team exists, -// we use the first team to which DM creator belongs to. -func (s *SQLStore) getBestTeamForBoard(tx sq.BaseRunner, board *model.Board) (string, error) { - userTeams, err := s.getBoardUserTeams(tx, board) - if err != nil { - return "", err - } - - teams := [][]interface{}{} - for _, userTeam := range userTeams { - userTeamInterfaces := make([]interface{}, len(userTeam)) - for i := range userTeam { - userTeamInterfaces[i] = userTeam[i] - } - teams = append(teams, userTeamInterfaces) - } - - commonTeams := utils.Intersection(teams...) - var teamID string - if len(commonTeams) > 0 { - teamID = commonTeams[0].(string) - } else { - // no common teams found. Let's try finding the best suitable team - if board.Type == "D" { - // get DM's creator and pick one of their team - channel, err := (s.servicesAPI).GetChannelByID(board.ChannelID) - if err != nil { - s.logger.Error("failed to fetch DM channel for board", - mlog.String("board_id", board.ID), - mlog.String("channel_id", board.ChannelID), - mlog.Err(err), - ) - return "", err - } - - if _, ok := userTeams[channel.CreatorId]; !ok { - s.logger.Error("channel creator not found in user teams", - mlog.String("board_id", board.ID), - mlog.String("channel_id", board.ChannelID), - mlog.String("creator_id", channel.CreatorId), - ) - err := fmt.Errorf("%w board_id: %s, channel_id: %s, creator_id: %s", errChannelCreatorNotInTeam, board.ID, board.ChannelID, channel.CreatorId) - return "", err - } - - teamID = userTeams[channel.CreatorId][0] - } else if board.Type == "G" { - // pick the team that has the most users as members - teamFrequency := map[string]int{} - highestFrequencyTeam := "" - highestFrequencyTeamFrequency := -1 - - for _, teams := range userTeams { - for _, teamID := range teams { - teamFrequency[teamID]++ - - if teamFrequency[teamID] > highestFrequencyTeamFrequency { - highestFrequencyTeamFrequency = teamFrequency[teamID] - highestFrequencyTeam = teamID - } - } - } - - teamID = highestFrequencyTeam - } - } - - return teamID, nil -} - -func (s *SQLStore) getBoardUserTeams(tx sq.BaseRunner, board *model.Board) (map[string][]string, error) { - query := s.getQueryBuilder(tx). - Select("tm.UserId", "tm.TeamId"). - From("ChannelMembers cm"). - Join("TeamMembers tm ON cm.UserId = tm.UserId"). - Join("Teams t ON tm.TeamId = t.Id"). - Where(sq.Eq{ - "cm.ChannelId": board.ChannelID, - "t.DeleteAt": 0, - "tm.DeleteAt": 0, - }) - - rows, err := query.Query() - if err != nil { - s.logger.Error("failed to fetch user teams for board", mlog.String("boardID", board.ID), mlog.String("channelID", board.ChannelID), mlog.Err(err)) - return nil, err - } - - defer rows.Close() - - userTeams := map[string][]string{} - - for rows.Next() { - var userID, teamID string - err := rows.Scan(&userID, &teamID) - if err != nil { - s.logger.Error("getBoardUserTeams failed to scan SQL query result", mlog.String("boardID", board.ID), mlog.String("channelID", board.ChannelID), mlog.Err(err)) - return nil, err - } - - userTeams[userID] = append(userTeams[userID], teamID) - } - - return userTeams, nil -} - -// getDeletedMembershipBoards retrieves those boards whose creator is -// associated to the board's team with a deleted team membership. -func (s *SQLStore) getDeletedMembershipBoards(tx sq.BaseRunner) ([]*model.Board, error) { - rows, err := s.getQueryBuilder(tx). - Select(legacyBoardFields("b.")...). - From(s.tablePrefix + "boards b"). - Join("TeamMembers tm ON b.created_by = tm.UserId"). - Where("b.team_id = tm.TeamId"). - Where(sq.NotEq{"tm.DeleteAt": 0}). - Query() - if err != nil { - return nil, err - } - defer s.CloseRows(rows) - - boards, err := s.boardsFromRows(rows) - if err != nil { - return nil, err - } - - return boards, err -} - func (s *SQLStore) RunFixCollationsAndCharsetsMigration() error { // This is for MySQL only if s.dbType != model.MysqlDBType { diff --git a/server/services/store/sqlstore/legacy_blocks.go b/server/services/store/sqlstore/legacy_blocks.go index 55dc225d09c..4d9520802eb 100644 --- a/server/services/store/sqlstore/legacy_blocks.go +++ b/server/services/store/sqlstore/legacy_blocks.go @@ -3,7 +3,6 @@ package sqlstore import ( "database/sql" "encoding/json" - "strings" "github.com/mattermost/focalboard/server/utils" @@ -13,49 +12,6 @@ import ( "github.com/mattermost/mattermost/server/public/shared/mlog" ) -func legacyBoardFields(prefix string) []string { - // substitute new columns with `"\"\""` (empty string) so as to allow - // row scan to continue to work with new models. - - fields := []string{ - "id", - "team_id", - "COALESCE(channel_id, '')", - "COALESCE(created_by, '')", - "modified_by", - "type", - "''", // substitute for minimum_role column. - "title", - "description", - "icon", - "show_description", - "is_template", - "template_version", - "COALESCE(properties, '{}')", - "COALESCE(card_properties, '[]')", - "create_at", - "update_at", - "delete_at", - } - - if prefix == "" { - return fields - } - - prefixedFields := make([]string, len(fields)) - for i, field := range fields { - switch { - case strings.HasPrefix(field, "COALESCE("): - prefixedFields[i] = strings.Replace(field, "COALESCE(", "COALESCE("+prefix, 1) - case field == "''": - prefixedFields[i] = field - default: - prefixedFields[i] = prefix + field - } - } - return prefixedFields -} - // legacyBlocksFromRows is the old getBlock version that still uses // the old block model. This method is kept to enable the unique IDs // data migration. @@ -247,7 +203,3 @@ func (s *SQLStore) insertLegacyBlock(db sq.BaseRunner, workspaceID string, block return nil } - -func (s *SQLStore) getLegacyBoardsByCondition(db sq.BaseRunner, conditions ...interface{}) ([]*model.Board, error) { - return s.getBoardsFieldsByCondition(db, legacyBoardFields(""), conditions...) -} diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go index a2f70c89c5b..de566f62299 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -5,7 +5,6 @@ import ( "context" "database/sql" "embed" - "errors" "fmt" "strings" @@ -41,8 +40,6 @@ const ( tempSchemaMigrationTableName = "temp_schema_migration" ) -var errChannelCreatorNotInTeam = errors.New("channel creator not found in user teams") - // migrations in MySQL need to run with the multiStatements flag // enabled, so this method creates a new connection ensuring that it's // enabled. @@ -74,7 +71,6 @@ func (s *SQLStore) getMigrationConnection() (*sql.DB, error) { } func (s *SQLStore) Migrate() error { - if err := s.EnsureSchemaMigrationFormat(); err != nil { return err } diff --git a/server/services/store/sqlstore/sqlstore.go b/server/services/store/sqlstore/sqlstore.go index 76b12beba01..97e5abc52f8 100644 --- a/server/services/store/sqlstore/sqlstore.go +++ b/server/services/store/sqlstore/sqlstore.go @@ -38,7 +38,6 @@ type MutexFactory func(name string) (*cluster.Mutex, error) // New creates a new SQL implementation of the store. func New(params Params) (*SQLStore, error) { - params.Logger.Info("connectDatabase", mlog.String("dbType", params.DBType)) store := &SQLStore{ // TODO: add replica DB support too.