Refactor SQL queries in store/sqlstore/preference_store.go to use the squirrel builder (#17086)

* refactor: refactored deleteUnusedFeatures

* refactor: refactored Get to remove hardcoded sql queries

* refactor: remove debug log

* refactor: refactored getcategory

* refactor: refactored GetAll to replace with sq

* refactor: refactor delete with sq

* refactor: refactored delete category with sq

* refactor: refactored cleanup flagsbatch

* refactor: refactor save to usq sq

* fix: fixed missing wildcard in LIKE operator

* refactor: fixed previous double call to database with a cleaner approach

* refactor: removed debug logs

* refactor: removed debug logs

* fix: added a new error checking as Limit accepts uint and the function parameter accepts int

* fix: golangcilint error

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
jingkai
2021-04-09 17:13:31 +08:00
коммит произвёл GitHub
родитель dfa476a688
Коммит 0eafaa502d
2 изменённых файлов: 132 добавлений и 122 удалений

Просмотреть файл

@@ -6,12 +6,12 @@ package sqlstore
import ( import (
"fmt" "fmt"
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/gorp" "github.com/mattermost/gorp"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/shared/mlog" "github.com/mattermost/mattermost-server/v5/shared/mlog"
"github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store"
"github.com/pkg/errors"
) )
type SqlPreferenceStore struct { type SqlPreferenceStore struct {
@@ -40,20 +40,15 @@ func (s SqlPreferenceStore) createIndexesIfNotExists() {
func (s SqlPreferenceStore) deleteUnusedFeatures() { func (s SqlPreferenceStore) deleteUnusedFeatures() {
mlog.Debug("Deleting any unused pre-release features") mlog.Debug("Deleting any unused pre-release features")
sql, args, err := s.getQueryBuilder().
sql := `DELETE Delete("Preferences").
FROM Preferences Where(sq.Eq{"Category": model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS}).
WHERE Where(sq.Eq{"Value": "false"}).
Category = :Category Where(sq.Like{"Name": store.FeatureTogglePrefix + "%"}).ToSql()
AND Value = :Value
AND Name LIKE '` + store.FeatureTogglePrefix + `%'`
queryParams := map[string]string{
"Category": model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS,
"Value": "false",
}
_, err := s.GetMaster().Exec(sql, queryParams)
if err != nil { if err != nil {
mlog.Warn(errors.Wrap(err, "could not build sql query to delete unused features!").Error())
}
if _, err = s.GetMaster().Exec(sql, args...); err != nil {
mlog.Warn("Failed to delete unused features", mlog.Err(err)) mlog.Warn("Failed to delete unused features", mlog.Err(err))
} }
} }
@@ -87,36 +82,38 @@ func (s SqlPreferenceStore) save(transaction *gorp.Transaction, preference *mode
return err return err
} }
params := map[string]interface{}{
"UserId": preference.UserId,
"Category": preference.Category,
"Name": preference.Name,
"Value": preference.Value,
}
if s.DriverName() == model.DATABASE_DRIVER_MYSQL { if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
if _, err := transaction.Exec( queryString, args, err := s.getQueryBuilder().
`INSERT INTO Insert("Preferences").
Preferences Columns("UserId", "Category", "Name", "Value").
(UserId, Category, Name, Value) Values(preference.UserId, preference.Category, preference.Name, preference.Value).
VALUES SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE Value = ?", preference.Value)).
(:UserId, :Category, :Name, :Value) ToSql()
ON DUPLICATE KEY UPDATE
Value = :Value`, params); err != nil { if err != nil {
return errors.Wrap(err, "failed to generate sqlquery")
}
if _, err = transaction.Exec(queryString, args...); err != nil {
return errors.Wrap(err, "failed to save Preference") return errors.Wrap(err, "failed to save Preference")
} }
return nil return nil
} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { } else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
// postgres has no way to upsert values until version 9.5 and trying inserting and then updating causes transactions to abort // postgres has no way to upsert values until version 9.5 and trying inserting and then updating causes transactions to abort
count, err := transaction.SelectInt( queryString, args, err := s.getQueryBuilder().
`SELECT Select("count(0)").
count(0) From("Preferences").
FROM Where(sq.Eq{"UserId": preference.UserId}).
Preferences Where(sq.Eq{"Category": preference.Category}).
WHERE Where(sq.Eq{"Name": preference.Name}).
UserId = :UserId ToSql()
AND Category = :Category
AND Name = :Name`, params) if err != nil {
return errors.Wrap(err, "failed to generate sqlquery")
}
count, err := transaction.SelectInt(queryString, args...)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to count Preferences") return errors.Wrap(err, "failed to count Preferences")
} }
@@ -150,79 +147,84 @@ func (s SqlPreferenceStore) update(transaction *gorp.Transaction, preference *mo
func (s SqlPreferenceStore) Get(userId string, category string, name string) (*model.Preference, error) { func (s SqlPreferenceStore) Get(userId string, category string, name string) (*model.Preference, error) {
var preference *model.Preference var preference *model.Preference
query, args, err := s.getQueryBuilder().
Select("*").
From("Preferences").
Where(sq.Eq{"UserId": userId}).
Where(sq.Eq{"Category": category}).
Where(sq.Eq{"Name": name}).
ToSql()
if err := s.GetReplica().SelectOne(&preference, if err != nil {
`SELECT return nil, errors.Wrap(err, "could not build sql query to get preference")
* }
FROM if err = s.GetReplica().SelectOne(&preference, query, args...); err != nil {
Preferences
WHERE
UserId = :UserId
AND Category = :Category
AND Name = :Name`, map[string]interface{}{"UserId": userId, "Category": category, "Name": name}); err != nil {
return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s, name=%s", userId, category, name) return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s, name=%s", userId, category, name)
} }
return preference, nil return preference, nil
} }
func (s SqlPreferenceStore) GetCategory(userId string, category string) (model.Preferences, error) { func (s SqlPreferenceStore) GetCategory(userId string, category string) (model.Preferences, error) {
var preferences model.Preferences var preferences model.Preferences
query, args, err := s.getQueryBuilder().
if _, err := s.GetReplica().Select(&preferences, Select("*").
`SELECT From("Preferences").
* Where(sq.Eq{"UserId": userId}).
FROM Where(sq.Eq{"Category": category}).
Preferences ToSql()
WHERE if err != nil {
UserId = :UserId return nil, errors.Wrap(err, "could not build sql query to get preference")
AND Category = :Category`, map[string]interface{}{"UserId": userId, "Category": category}); err != nil { }
return nil, errors.Wrapf(err, "failed to find Preferences with userId=%s and category=%s", userId, category) if _, err = s.GetReplica().Select(&preferences, query, args...); err != nil {
return nil, errors.Wrapf(err, "failed to find Preference with userId=%s, category=%s", userId, category)
} }
return preferences, nil return preferences, nil
} }
func (s SqlPreferenceStore) GetAll(userId string) (model.Preferences, error) { func (s SqlPreferenceStore) GetAll(userId string) (model.Preferences, error) {
var preferences model.Preferences var preferences model.Preferences
query, args, err := s.getQueryBuilder().
if _, err := s.GetReplica().Select(&preferences, Select("*").
`SELECT From("Preferences").
* Where(sq.Eq{"UserId": userId}).
FROM ToSql()
Preferences if err != nil {
WHERE return nil, errors.Wrap(err, "could not build sql query to get preference")
UserId = :UserId`, map[string]interface{}{"UserId": userId}); err != nil { }
return nil, errors.Wrapf(err, "failed to find Preferences with userId=%s", userId) if _, err = s.GetReplica().Select(&preferences, query, args...); err != nil {
return nil, errors.Wrapf(err, "failed to find Preference with userId=%s", userId)
} }
return preferences, nil return preferences, nil
} }
func (s SqlPreferenceStore) PermanentDeleteByUser(userId string) error { func (s SqlPreferenceStore) PermanentDeleteByUser(userId string) error {
query := sql, args, err := s.getQueryBuilder().
`DELETE FROM Delete("Preferences").
Preferences Where(sq.Eq{"UserId": userId}).ToSql()
WHERE if err != nil {
UserId = :UserId` return errors.Wrap(err, "could not build sql query to get delete preference by user")
}
if _, err := s.GetMaster().Exec(query, map[string]interface{}{"UserId": userId}); err != nil { if _, err := s.GetMaster().Exec(sql, args...); err != nil {
return errors.Wrapf(err, "failed to delete Preference with userId=%s", userId) return errors.Wrapf(err, "failed to delete Preference with userId=%s", userId)
} }
return nil return nil
} }
func (s SqlPreferenceStore) Delete(userId, category, name string) error { func (s SqlPreferenceStore) Delete(userId, category, name string) error {
query :=
`DELETE FROM Preferences
WHERE
UserId = :UserId
AND Category = :Category
AND Name = :Name`
_, err := s.GetMaster().Exec(query, map[string]interface{}{"UserId": userId, "Category": category, "Name": name}) sql, args, err := s.getQueryBuilder().
Delete("Preferences").
Where(sq.Eq{"UserId": userId}).
Where(sq.Eq{"Category": category}).
Where(sq.Eq{"Name": name}).ToSql()
if err != nil { if err != nil {
return errors.Wrap(err, "could not build sql query to get delete preference")
}
if _, err = s.GetMaster().Exec(sql, args...); err != nil {
return errors.Wrapf(err, "failed to delete Preference with userId=%s, category=%s and name=%s", userId, category, name) return errors.Wrapf(err, "failed to delete Preference with userId=%s, category=%s and name=%s", userId, category, name)
} }
@@ -230,14 +232,17 @@ func (s SqlPreferenceStore) Delete(userId, category, name string) error {
} }
func (s SqlPreferenceStore) DeleteCategory(userId string, category string) error { func (s SqlPreferenceStore) DeleteCategory(userId string, category string) error {
_, err := s.GetMaster().Exec(
`DELETE FROM sql, args, err := s.getQueryBuilder().
Preferences Delete("Preferences").
WHERE Where(sq.Eq{"UserId": userId}).
UserId = :UserId Where(sq.Eq{"Category": category}).ToSql()
AND Category = :Category`, map[string]interface{}{"UserId": userId, "Category": category})
if err != nil { if err != nil {
return errors.Wrap(err, "could not build sql query to get delete preference by category")
}
if _, err = s.GetMaster().Exec(sql, args...); err != nil {
return errors.Wrapf(err, "failed to delete Preference with userId=%s and category=%s", userId, category) return errors.Wrapf(err, "failed to delete Preference with userId=%s and category=%s", userId, category)
} }
@@ -245,14 +250,16 @@ func (s SqlPreferenceStore) DeleteCategory(userId string, category string) error
} }
func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string) error { func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string) error {
_, err := s.GetMaster().Exec( sql, args, err := s.getQueryBuilder().
`DELETE FROM Delete("Preferences").
Preferences Where(sq.Eq{"Name": name}).
WHERE Where(sq.Eq{"Category": category}).ToSql()
Name = :Name
AND Category = :Category`, map[string]interface{}{"Name": name, "Category": category})
if err != nil { if err != nil {
return errors.Wrap(err, "could not build sql query to get delete preference by category and name")
}
if _, err = s.GetMaster().Exec(sql, args...); err != nil {
return errors.Wrapf(err, "failed to delete Preference with category=%s and name=%s", category, name) return errors.Wrapf(err, "failed to delete Preference with category=%s and name=%s", category, name)
} }
@@ -260,37 +267,37 @@ func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string)
} }
func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) { func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
query := if limit < 0 {
`DELETE FROM // uint64 does not throw an error, it overflows if it is negative.
Preferences // it is better to manually check here, or change the function type to uint64
WHERE return int64(0), errors.Errorf("Received a negative limit")
Category = :Category }
AND Name IN ( nameInQ, nameInArgs, err := sq.Select("*").
SELECT FromSelect(
* sq.Select("Preferences.Name").
FROM ( From("Preferences").
SELECT LeftJoin("Posts ON Preferences.Name = Posts.Id").
Preferences.Name Where(sq.Eq{"Preferences.Category": model.PREFERENCE_CATEGORY_FLAGGED_POST}).
FROM Where(sq.Eq{"Posts.Id": nil}).
Preferences Limit(uint64(limit)),
LEFT JOIN "t").
Posts ToSql()
ON if err != nil {
Preferences.Name = Posts.Id return int64(0), errors.Wrap(err, "could not build nested sql query to delete preference")
WHERE }
Preferences.Category = :Category query, args, err := s.getQueryBuilder().Delete("Preferences").
AND Posts.Id IS null Where(sq.Eq{"Category": model.PREFERENCE_CATEGORY_FLAGGED_POST}).
LIMIT Where(sq.Expr("name IN ("+nameInQ+")", nameInArgs...)).
:Limit ToSql()
)
AS t
)`
sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"Category": model.PREFERENCE_CATEGORY_FLAGGED_POST, "Limit": limit}) if err != nil {
return int64(0), errors.Wrap(err, "could not build sql query to delete preference")
}
sqlResult, err := s.GetMaster().Exec(query, args...)
if err != nil { if err != nil {
return int64(0), errors.Wrap(err, "failed to delete Preference") return int64(0), errors.Wrap(err, "failed to delete Preference")
} }
rowsAffected, err := sqlResult.RowsAffected() rowsAffected, err := sqlResult.RowsAffected()
if err != nil { if err != nil {
return int64(0), errors.Wrap(err, "unable to get rows affected") return int64(0), errors.Wrap(err, "unable to get rows affected")

Просмотреть файл

@@ -359,6 +359,9 @@ func testPreferenceCleanupFlagsBatch(t *testing.T, ss store.Store) {
nErr := ss.Preference().Save(&model.Preferences{preference1, preference2}) nErr := ss.Preference().Save(&model.Preferences{preference1, preference2})
require.NoError(t, nErr) require.NoError(t, nErr)
_, nErr = ss.Preference().CleanupFlagsBatch(-1)
require.Error(t, nErr)
_, nErr = ss.Preference().CleanupFlagsBatch(10000) _, nErr = ss.Preference().CleanupFlagsBatch(10000)
assert.NoError(t, nErr) assert.NoError(t, nErr)