github.com/mattermost/mattermost-server/v5@v5.39.3/store/sqlstore/preference_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  	sq "github.com/Masterminds/squirrel"
     8  	"github.com/mattermost/gorp"
     9  	"github.com/mattermost/mattermost-server/v5/model"
    10  	"github.com/mattermost/mattermost-server/v5/shared/mlog"
    11  	"github.com/mattermost/mattermost-server/v5/store"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  type SqlPreferenceStore struct {
    16  	*SqlStore
    17  }
    18  
    19  func newSqlPreferenceStore(sqlStore *SqlStore) store.PreferenceStore {
    20  	s := &SqlPreferenceStore{sqlStore}
    21  
    22  	for _, db := range sqlStore.GetAllConns() {
    23  		table := db.AddTableWithName(model.Preference{}, "Preferences").SetKeys(false, "UserId", "Category", "Name")
    24  		table.ColMap("UserId").SetMaxSize(26)
    25  		table.ColMap("Category").SetMaxSize(32)
    26  		table.ColMap("Name").SetMaxSize(32)
    27  		table.ColMap("Value").SetMaxSize(2000)
    28  	}
    29  
    30  	return s
    31  }
    32  
    33  func (s SqlPreferenceStore) createIndexesIfNotExists() {
    34  	s.CreateIndexIfNotExists("idx_preferences_category", "Preferences", "Category")
    35  	s.CreateIndexIfNotExists("idx_preferences_name", "Preferences", "Name")
    36  }
    37  
    38  func (s SqlPreferenceStore) deleteUnusedFeatures() {
    39  	mlog.Debug("Deleting any unused pre-release features")
    40  	sql, args, err := s.getQueryBuilder().
    41  		Delete("Preferences").
    42  		Where(sq.Eq{"Category": model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS}).
    43  		Where(sq.Eq{"Value": "false"}).
    44  		Where(sq.Like{"Name": store.FeatureTogglePrefix + "%"}).ToSql()
    45  	if err != nil {
    46  		mlog.Warn(errors.Wrap(err, "could not build sql query to delete unused features!").Error())
    47  	}
    48  	if _, err = s.GetMaster().Exec(sql, args...); err != nil {
    49  		mlog.Warn("Failed to delete unused features", mlog.Err(err))
    50  	}
    51  }
    52  
    53  func (s SqlPreferenceStore) Save(preferences *model.Preferences) error {
    54  	// wrap in a transaction so that if one fails, everything fails
    55  	transaction, err := s.GetMaster().Begin()
    56  	if err != nil {
    57  		return errors.Wrap(err, "begin_transaction")
    58  	}
    59  
    60  	defer finalizeTransaction(transaction)
    61  	for _, preference := range *preferences {
    62  		preference := preference
    63  		if upsertErr := s.save(transaction, &preference); upsertErr != nil {
    64  			return upsertErr
    65  		}
    66  	}
    67  
    68  	if err := transaction.Commit(); err != nil {
    69  		// don't need to rollback here since the transaction is already closed
    70  		return errors.Wrap(err, "commit_transaction")
    71  	}
    72  	return nil
    73  }
    74  
    75  func (s SqlPreferenceStore) save(transaction *gorp.Transaction, preference *model.Preference) error {
    76  	preference.PreUpdate()
    77  
    78  	if err := preference.IsValid(); err != nil {
    79  		return err
    80  	}
    81  
    82  	query := s.getQueryBuilder().
    83  		Insert("Preferences").
    84  		Columns("UserId", "Category", "Name", "Value").
    85  		Values(preference.UserId, preference.Category, preference.Name, preference.Value)
    86  
    87  	if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
    88  		query = query.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE Value = ?", preference.Value))
    89  	} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
    90  		query = query.SuffixExpr(sq.Expr("ON CONFLICT (userid, category, name) DO UPDATE SET Value = ?", preference.Value))
    91  	} else {
    92  		return store.NewErrNotImplemented("failed to update preference because of missing driver")
    93  	}
    94  
    95  	queryString, args, err := query.ToSql()
    96  	if err != nil {
    97  		return errors.Wrap(err, "failed to generate sqlquery")
    98  	}
    99  
   100  	if _, err = transaction.Exec(queryString, args...); err != nil {
   101  		return errors.Wrap(err, "failed to save Preference")
   102  	}
   103  	return nil
   104  }
   105  
   106  func (s SqlPreferenceStore) Get(userId string, category string, name string) (*model.Preference, error) {
   107  	var preference *model.Preference
   108  	query, args, err := s.getQueryBuilder().
   109  		Select("*").
   110  		From("Preferences").
   111  		Where(sq.Eq{"UserId": userId}).
   112  		Where(sq.Eq{"Category": category}).
   113  		Where(sq.Eq{"Name": name}).
   114  		ToSql()
   115  
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, "could not build sql query to get preference")
   118  	}
   119  	if err = s.GetReplica().SelectOne(&preference, query, args...); err != nil {
   120  		return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s, name=%s", userId, category, name)
   121  	}
   122  
   123  	return preference, nil
   124  }
   125  
   126  func (s SqlPreferenceStore) GetCategory(userId string, category string) (model.Preferences, error) {
   127  	var preferences model.Preferences
   128  	query, args, err := s.getQueryBuilder().
   129  		Select("*").
   130  		From("Preferences").
   131  		Where(sq.Eq{"UserId": userId}).
   132  		Where(sq.Eq{"Category": category}).
   133  		ToSql()
   134  	if err != nil {
   135  		return nil, errors.Wrap(err, "could not build sql query to get preference")
   136  	}
   137  	if _, err = s.GetReplica().Select(&preferences, query, args...); err != nil {
   138  		return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s", userId, category)
   139  	}
   140  	return preferences, nil
   141  
   142  }
   143  
   144  func (s SqlPreferenceStore) GetAll(userId string) (model.Preferences, error) {
   145  	var preferences model.Preferences
   146  	query, args, err := s.getQueryBuilder().
   147  		Select("*").
   148  		From("Preferences").
   149  		Where(sq.Eq{"UserId": userId}).
   150  		ToSql()
   151  	if err != nil {
   152  		return nil, errors.Wrap(err, "could not build sql query to get preference")
   153  	}
   154  	if _, err = s.GetReplica().Select(&preferences, query, args...); err != nil {
   155  		return nil, errors.Wrapf(err, "failed to find Preference with userId=%s", userId)
   156  	}
   157  	return preferences, nil
   158  }
   159  
   160  func (s SqlPreferenceStore) PermanentDeleteByUser(userId string) error {
   161  	sql, args, err := s.getQueryBuilder().
   162  		Delete("Preferences").
   163  		Where(sq.Eq{"UserId": userId}).ToSql()
   164  	if err != nil {
   165  		return errors.Wrap(err, "could not build sql query to get delete preference by user")
   166  	}
   167  	if _, err := s.GetMaster().Exec(sql, args...); err != nil {
   168  		return errors.Wrapf(err, "failed to delete Preference with userId=%s", userId)
   169  	}
   170  	return nil
   171  }
   172  
   173  func (s SqlPreferenceStore) Delete(userId, category, name string) error {
   174  
   175  	sql, args, err := s.getQueryBuilder().
   176  		Delete("Preferences").
   177  		Where(sq.Eq{"UserId": userId}).
   178  		Where(sq.Eq{"Category": category}).
   179  		Where(sq.Eq{"Name": name}).ToSql()
   180  
   181  	if err != nil {
   182  		return errors.Wrap(err, "could not build sql query to get delete preference")
   183  	}
   184  
   185  	if _, err = s.GetMaster().Exec(sql, args...); err != nil {
   186  		return errors.Wrapf(err, "failed to delete Preference with userId=%s, category=%s and name=%s", userId, category, name)
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  func (s SqlPreferenceStore) DeleteCategory(userId string, category string) error {
   193  
   194  	sql, args, err := s.getQueryBuilder().
   195  		Delete("Preferences").
   196  		Where(sq.Eq{"UserId": userId}).
   197  		Where(sq.Eq{"Category": category}).ToSql()
   198  
   199  	if err != nil {
   200  		return errors.Wrap(err, "could not build sql query to get delete preference by category")
   201  	}
   202  
   203  	if _, err = s.GetMaster().Exec(sql, args...); err != nil {
   204  		return errors.Wrapf(err, "failed to delete Preference with userId=%s and category=%s", userId, category)
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string) error {
   211  	sql, args, err := s.getQueryBuilder().
   212  		Delete("Preferences").
   213  		Where(sq.Eq{"Name": name}).
   214  		Where(sq.Eq{"Category": category}).ToSql()
   215  
   216  	if err != nil {
   217  		return errors.Wrap(err, "could not build sql query to get delete preference by category and name")
   218  	}
   219  
   220  	if _, err = s.GetMaster().Exec(sql, args...); err != nil {
   221  		return errors.Wrapf(err, "failed to delete Preference with category=%s and name=%s", category, name)
   222  	}
   223  
   224  	return nil
   225  }
   226  
   227  // DeleteOrphanedRows removes entries from Preferences (flagged post) when a
   228  // corresponding post no longer exists.
   229  func (s *SqlPreferenceStore) DeleteOrphanedRows(limit int) (deleted int64, err error) {
   230  	// We need the extra level of nesting to deal with MySQL's locking
   231  	const query = `
   232  	DELETE FROM Preferences WHERE Name IN (
   233  		SELECT * FROM (
   234  			SELECT Preferences.Name FROM Preferences
   235  			LEFT JOIN Posts ON Preferences.Name = Posts.Id
   236  			WHERE Posts.Id IS NULL AND Category = :Category
   237  			LIMIT :Limit
   238  		) AS A
   239  	)`
   240  	props := map[string]interface{}{"Limit": limit, "Category": model.PREFERENCE_CATEGORY_FLAGGED_POST}
   241  	result, err := s.GetMaster().Exec(query, props)
   242  	if err != nil {
   243  		return
   244  	}
   245  	deleted, err = result.RowsAffected()
   246  	return
   247  }
   248  
   249  func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
   250  	if limit < 0 {
   251  		// uint64 does not throw an error, it overflows if it is negative.
   252  		// it is better to manually check here, or change the function type to uint64
   253  		return int64(0), errors.Errorf("Received a negative limit")
   254  	}
   255  	nameInQ, nameInArgs, err := sq.Select("*").
   256  		FromSelect(
   257  			sq.Select("Preferences.Name").
   258  				From("Preferences").
   259  				LeftJoin("Posts ON Preferences.Name = Posts.Id").
   260  				Where(sq.Eq{"Preferences.Category": model.PREFERENCE_CATEGORY_FLAGGED_POST}).
   261  				Where(sq.Eq{"Posts.Id": nil}).
   262  				Limit(uint64(limit)),
   263  			"t").
   264  		ToSql()
   265  	if err != nil {
   266  		return int64(0), errors.Wrap(err, "could not build nested sql query to delete preference")
   267  	}
   268  	query, args, err := s.getQueryBuilder().Delete("Preferences").
   269  		Where(sq.Eq{"Category": model.PREFERENCE_CATEGORY_FLAGGED_POST}).
   270  		Where(sq.Expr("name IN ("+nameInQ+")", nameInArgs...)).
   271  		ToSql()
   272  
   273  	if err != nil {
   274  		return int64(0), errors.Wrap(err, "could not build sql query to delete preference")
   275  	}
   276  
   277  	sqlResult, err := s.GetMaster().Exec(query, args...)
   278  	if err != nil {
   279  		return int64(0), errors.Wrap(err, "failed to delete Preference")
   280  	}
   281  	rowsAffected, err := sqlResult.RowsAffected()
   282  	if err != nil {
   283  		return int64(0), errors.Wrap(err, "unable to get rows affected")
   284  	}
   285  
   286  	return rowsAffected, nil
   287  }