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

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