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 }