2019-11-29 12:59:40 +01:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
|
|
|
// See LICENSE.txt for license information.
|
2017-11-27 17:23:35 -05:00
|
|
|
|
|
|
|
|
package sqlstore
|
|
|
|
|
|
|
|
|
|
import (
|
MM-21328: Fix KVCompareAndSet when new==old (#13612)
`KVCompareAndSet(key, sameValue, sameValue)` can fail spuriously on MySQL if the underlying `UPDATE` requires no actual changes. As per the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html), we can't rely on rows affected in this case:
> For UPDATE statements, the affected-rows value by default is the number of rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is the number of rows “found”; that is, matched by the WHERE clause.
It's not tenable to change `CLIENT_FOUND_ROWS` for the all connection, so handle this case in the code instead by running a `SELECT` after the fact. Note that `KVCompareAndSet` has no guarantee of atomicity in this case, but neither would `CompareAndSwap` on which this is method was inspired.
Finally, note that no changes are required for Postgres, which has sane semantics as the default.
Fixes: https://mattermost.atlassian.net/browse/MM-21328
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-01-16 15:29:10 -04:00
|
|
|
"bytes"
|
2017-11-27 17:23:35 -05:00
|
|
|
"database/sql"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
2020-07-15 09:19:51 +02:00
|
|
|
sq "github.com/Masterminds/squirrel"
|
2020-08-13 01:35:57 -04:00
|
|
|
"github.com/pkg/errors"
|
2020-07-15 09:19:51 +02:00
|
|
|
|
2021-07-22 12:21:47 +05:30
|
|
|
"github.com/mattermost/mattermost-server/v6/model"
|
|
|
|
|
"github.com/mattermost/mattermost-server/v6/store"
|
2017-11-27 17:23:35 -05:00
|
|
|
)
|
|
|
|
|
|
2018-10-03 13:04:37 -07:00
|
|
|
const (
|
2020-02-18 16:32:46 -04:00
|
|
|
defaultPluginKeyFetchLimit = 10
|
2018-10-03 13:04:37 -07:00
|
|
|
)
|
|
|
|
|
|
2017-11-27 17:23:35 -05:00
|
|
|
type SqlPluginStore struct {
|
2020-12-02 12:06:23 +01:00
|
|
|
*SqlStore
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-02 12:06:23 +01:00
|
|
|
func newSqlPluginStore(sqlStore *SqlStore) store.PluginStore {
|
|
|
|
|
s := &SqlPluginStore{sqlStore}
|
2017-11-27 17:23:35 -05:00
|
|
|
|
2020-12-02 12:06:23 +01:00
|
|
|
for _, db := range sqlStore.GetAllConns() {
|
2017-11-27 17:23:35 -05:00
|
|
|
table := db.AddTableWithName(model.PluginKeyValue{}, "PluginKeyValueStore").SetKeys(false, "PluginId", "Key")
|
2017-12-05 18:19:33 -05:00
|
|
|
table.ColMap("PluginId").SetMaxSize(190)
|
|
|
|
|
table.ColMap("Key").SetMaxSize(50)
|
2017-11-27 17:23:35 -05:00
|
|
|
table.ColMap("Value").SetMaxSize(8192)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-03 11:45:49 +01:00
|
|
|
func (ps SqlPluginStore) createIndexesIfNotExists() {
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) (*model.PluginKeyValue, error) {
|
2019-07-09 09:08:28 -04:00
|
|
|
if err := kv.IsValid(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2017-11-27 17:23:35 -05:00
|
|
|
|
2020-01-31 09:58:48 -04:00
|
|
|
if kv.Value == nil {
|
|
|
|
|
// Setting a key to nil is the same as removing it
|
|
|
|
|
err := ps.Delete(kv.PluginId, kv.Key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return kv, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-09 19:36:35 +05:30
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Insert("PluginKeyValueStore").
|
|
|
|
|
Columns("PluginId", "PKey", "PValue", "ExpireAt").
|
|
|
|
|
Values(kv.PluginId, kv.Key, kv.Value, kv.ExpireAt)
|
2021-07-12 20:05:36 +02:00
|
|
|
if ps.DriverName() == model.DatabaseDriverPostgres {
|
2021-07-09 19:36:35 +05:30
|
|
|
query = query.SuffixExpr(sq.Expr("ON CONFLICT (pluginid, pkey) DO UPDATE SET PValue = ?, ExpireAt = ?", kv.Value, kv.ExpireAt))
|
2021-07-12 20:05:36 +02:00
|
|
|
} else if ps.DriverName() == model.DatabaseDriverMysql {
|
2021-07-09 19:36:35 +05:30
|
|
|
query = query.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE PValue = ?, ExpireAt = ?", kv.Value, kv.ExpireAt))
|
|
|
|
|
}
|
2020-07-15 23:52:35 +07:00
|
|
|
|
2021-07-09 19:36:35 +05:30
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "plugin_tosql")
|
|
|
|
|
}
|
2020-07-15 23:52:35 +07:00
|
|
|
|
2021-07-09 19:36:35 +05:30
|
|
|
if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "failed to upsert PluginKeyValue")
|
2019-07-09 09:08:28 -04:00
|
|
|
}
|
2017-11-27 17:23:35 -05:00
|
|
|
|
2019-07-09 09:08:28 -04:00
|
|
|
return kv, nil
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte) (bool, error) {
|
2019-04-23 13:35:17 -04:00
|
|
|
if err := kv.IsValid(); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 23:25:38 -03:00
|
|
|
if kv.Value == nil {
|
|
|
|
|
// Setting a key to nil is the same as removing it
|
|
|
|
|
return ps.CompareAndDelete(kv, oldValue)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 13:35:17 -04:00
|
|
|
if oldValue == nil {
|
2020-02-18 16:32:46 -04:00
|
|
|
// Delete any existing, expired value.
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Delete("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": kv.PluginId}).
|
|
|
|
|
Where(sq.Eq{"PKey": kv.Key}).
|
|
|
|
|
Where(sq.NotEq{"ExpireAt": int(0)}).
|
|
|
|
|
Where(sq.Lt{"ExpireAt": model.GetMillis()})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "failed to delete PluginKeyValue")
|
2020-02-18 16:32:46 -04:00
|
|
|
}
|
|
|
|
|
|
2019-04-23 13:35:17 -04:00
|
|
|
// Insert if oldValue is nil
|
|
|
|
|
if err := ps.GetMaster().Insert(kv); err != nil {
|
|
|
|
|
// If the error is from unique constraints violation, it's the result of a
|
|
|
|
|
// race condition, return false and no error. Otherwise we have a real error and
|
|
|
|
|
// need to return it.
|
2020-02-04 20:05:08 +01:00
|
|
|
if IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey", "pkey"}) {
|
2019-04-23 13:35:17 -04:00
|
|
|
return false, nil
|
|
|
|
|
}
|
2020-12-21 21:20:47 +05:30
|
|
|
return false, errors.Wrap(err, "failed to insert PluginKeyValue")
|
2019-04-23 13:35:17 -04:00
|
|
|
}
|
|
|
|
|
} else {
|
2020-02-18 16:32:46 -04:00
|
|
|
currentTime := model.GetMillis()
|
|
|
|
|
|
2019-04-23 13:35:17 -04:00
|
|
|
// Update if oldValue is not nil
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Update("PluginKeyValueStore").
|
|
|
|
|
Set("PValue", kv.Value).
|
|
|
|
|
Set("ExpireAt", kv.ExpireAt).
|
|
|
|
|
Where(sq.Eq{"PluginId": kv.PluginId}).
|
|
|
|
|
Where(sq.Eq{"PKey": kv.Key}).
|
|
|
|
|
Where(sq.Eq{"PValue": oldValue}).
|
|
|
|
|
Where(sq.Or{
|
|
|
|
|
sq.Eq{"ExpireAt": int(0)},
|
|
|
|
|
sq.Gt{"ExpireAt": currentTime},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateResult, err := ps.GetMaster().Exec(queryString, args...)
|
2019-04-23 13:35:17 -04:00
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "failed to update PluginKeyValue")
|
2019-04-23 13:35:17 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rowsAffected, err := updateResult.RowsAffected(); err != nil {
|
|
|
|
|
// Failed to update
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "unable to get rows affected")
|
2019-04-23 13:35:17 -04:00
|
|
|
} else if rowsAffected == 0 {
|
2021-07-12 20:05:36 +02:00
|
|
|
if ps.DriverName() == model.DatabaseDriverMysql && bytes.Equal(oldValue, kv.Value) {
|
MM-21328: Fix KVCompareAndSet when new==old (#13612)
`KVCompareAndSet(key, sameValue, sameValue)` can fail spuriously on MySQL if the underlying `UPDATE` requires no actual changes. As per the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html), we can't rely on rows affected in this case:
> For UPDATE statements, the affected-rows value by default is the number of rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is the number of rows “found”; that is, matched by the WHERE clause.
It's not tenable to change `CLIENT_FOUND_ROWS` for the all connection, so handle this case in the code instead by running a `SELECT` after the fact. Note that `KVCompareAndSet` has no guarantee of atomicity in this case, but neither would `CompareAndSwap` on which this is method was inspired.
Finally, note that no changes are required for Postgres, which has sane semantics as the default.
Fixes: https://mattermost.atlassian.net/browse/MM-21328
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-01-16 15:29:10 -04:00
|
|
|
// ROW_COUNT on MySQL is zero even if the row existed but no changes to the row were required.
|
|
|
|
|
// Check if the row exists with the required value to distinguish this case. Strictly speaking,
|
|
|
|
|
// this isn't a good use of CompareAndSet anyway, since there's no corresponding guarantee of
|
|
|
|
|
// atomicity. Nevertheless, let's return results consistent with Postgres and with what might
|
|
|
|
|
// be expected in this case.
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Select("COUNT(*)").
|
|
|
|
|
From("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": kv.PluginId}).
|
|
|
|
|
Where(sq.Eq{"PKey": kv.Key}).
|
|
|
|
|
Where(sq.Eq{"PValue": kv.Value}).
|
|
|
|
|
Where(sq.Or{
|
|
|
|
|
sq.Eq{"ExpireAt": int(0)},
|
|
|
|
|
sq.Gt{"ExpireAt": currentTime},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count, err := ps.GetReplica().SelectInt(queryString, args...)
|
MM-21328: Fix KVCompareAndSet when new==old (#13612)
`KVCompareAndSet(key, sameValue, sameValue)` can fail spuriously on MySQL if the underlying `UPDATE` requires no actual changes. As per the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html), we can't rely on rows affected in this case:
> For UPDATE statements, the affected-rows value by default is the number of rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is the number of rows “found”; that is, matched by the WHERE clause.
It's not tenable to change `CLIENT_FOUND_ROWS` for the all connection, so handle this case in the code instead by running a `SELECT` after the fact. Note that `KVCompareAndSet` has no guarantee of atomicity in this case, but neither would `CompareAndSwap` on which this is method was inspired.
Finally, note that no changes are required for Postgres, which has sane semantics as the default.
Fixes: https://mattermost.atlassian.net/browse/MM-21328
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-01-16 15:29:10 -04:00
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrapf(err, "failed to count PluginKeyValue with pluginId=%s and key=%s", kv.PluginId, kv.Key)
|
MM-21328: Fix KVCompareAndSet when new==old (#13612)
`KVCompareAndSet(key, sameValue, sameValue)` can fail spuriously on MySQL if the underlying `UPDATE` requires no actual changes. As per the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html), we can't rely on rows affected in this case:
> For UPDATE statements, the affected-rows value by default is the number of rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is the number of rows “found”; that is, matched by the WHERE clause.
It's not tenable to change `CLIENT_FOUND_ROWS` for the all connection, so handle this case in the code instead by running a `SELECT` after the fact. Note that `KVCompareAndSet` has no guarantee of atomicity in this case, but neither would `CompareAndSwap` on which this is method was inspired.
Finally, note that no changes are required for Postgres, which has sane semantics as the default.
Fixes: https://mattermost.atlassian.net/browse/MM-21328
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-01-16 15:29:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if count == 0 {
|
|
|
|
|
return false, nil
|
|
|
|
|
} else if count == 1 {
|
|
|
|
|
return true, nil
|
|
|
|
|
} else {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrapf(err, "got too many rows when counting PluginKeyValue with pluginId=%s, key=%s, rows=%d", kv.PluginId, kv.Key, count)
|
MM-21328: Fix KVCompareAndSet when new==old (#13612)
`KVCompareAndSet(key, sameValue, sameValue)` can fail spuriously on MySQL if the underlying `UPDATE` requires no actual changes. As per the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/mysql-affected-rows.html), we can't rely on rows affected in this case:
> For UPDATE statements, the affected-rows value by default is the number of rows actually changed. If you specify the CLIENT_FOUND_ROWS flag to mysql_real_connect() when connecting to mysqld, the affected-rows value is the number of rows “found”; that is, matched by the WHERE clause.
It's not tenable to change `CLIENT_FOUND_ROWS` for the all connection, so handle this case in the code instead by running a `SELECT` after the fact. Note that `KVCompareAndSet` has no guarantee of atomicity in this case, but neither would `CompareAndSwap` on which this is method was inspired.
Finally, note that no changes are required for Postgres, which has sane semantics as the default.
Fixes: https://mattermost.atlassian.net/browse/MM-21328
Co-authored-by: mattermod <mattermod@users.noreply.github.com>
2020-01-16 15:29:10 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 13:35:17 -04:00
|
|
|
// No rows were affected by the update, where condition was not satisfied,
|
|
|
|
|
// return false, but no error.
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) CompareAndDelete(kv *model.PluginKeyValue, oldValue []byte) (bool, error) {
|
2019-08-21 23:25:38 -03:00
|
|
|
if err := kv.IsValid(); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if oldValue == nil {
|
|
|
|
|
// nil can't be stored. Return showing that we didn't do anything
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Delete("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": kv.PluginId}).
|
|
|
|
|
Where(sq.Eq{"PKey": kv.Key}).
|
|
|
|
|
Where(sq.Eq{"PValue": oldValue}).
|
|
|
|
|
Where(sq.Or{
|
|
|
|
|
sq.Eq{"ExpireAt": int(0)},
|
|
|
|
|
sq.Gt{"ExpireAt": model.GetMillis()},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deleteResult, err := ps.GetMaster().Exec(queryString, args...)
|
2019-08-21 23:25:38 -03:00
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "failed to delete PluginKeyValue")
|
2019-08-21 23:25:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rowsAffected, err := deleteResult.RowsAffected(); err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return false, errors.Wrap(err, "unable to get rows affected")
|
2019-08-21 23:25:38 -03:00
|
|
|
} else if rowsAffected == 0 {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) SetWithOptions(pluginId string, key string, value []byte, opt model.PluginKVSetOptions) (bool, error) {
|
2019-11-04 09:49:54 -03:00
|
|
|
if err := opt.IsValid(); err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kv, err := model.NewPluginKeyValueFromOptions(pluginId, key, value, opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if opt.Atomic {
|
2019-12-03 10:46:15 +01:00
|
|
|
return ps.CompareAndSet(kv, opt.OldValue)
|
2019-11-04 09:49:54 -03:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
savedKv, nErr := ps.SaveOrUpdate(kv)
|
|
|
|
|
if nErr != nil {
|
|
|
|
|
return false, nErr
|
2019-11-04 09:49:54 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return savedKv != nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) Get(pluginId, key string) (*model.PluginKeyValue, error) {
|
2019-07-08 14:13:10 +03:00
|
|
|
currentTime := model.GetMillis()
|
2020-07-15 09:19:51 +02:00
|
|
|
query := ps.getQueryBuilder().Select("PluginId, PKey, PValue, ExpireAt").
|
|
|
|
|
From("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": pluginId}).
|
|
|
|
|
Where(sq.Eq{"PKey": key}).
|
|
|
|
|
Where(sq.Or{sq.Eq{"ExpireAt": 0}, sq.Gt{"ExpireAt": currentTime}})
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return nil, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 09:19:51 +02:00
|
|
|
}
|
2020-08-13 01:35:57 -04:00
|
|
|
|
2020-07-15 09:19:51 +02:00
|
|
|
row := ps.GetReplica().Db.QueryRow(queryString, args...)
|
|
|
|
|
var kv model.PluginKeyValue
|
|
|
|
|
if err := row.Scan(&kv.PluginId, &kv.Key, &kv.Value, &kv.ExpireAt); err != nil {
|
2019-07-08 14:13:10 +03:00
|
|
|
if err == sql.ErrNoRows {
|
2020-08-13 01:35:57 -04:00
|
|
|
return nil, store.NewErrNotFound("PluginKeyValue", fmt.Sprintf("pluginId=%s, key=%s", pluginId, key))
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
2020-08-13 01:35:57 -04:00
|
|
|
return nil, errors.Wrapf(err, "failed to get PluginKeyValue with pluginId=%s and key=%s", pluginId, key)
|
2019-07-08 14:13:10 +03:00
|
|
|
}
|
|
|
|
|
|
2020-07-15 09:19:51 +02:00
|
|
|
return &kv, nil
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) Delete(pluginId, key string) error {
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Delete("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": pluginId}).
|
|
|
|
|
Where(sq.Eq{"Pkey": key})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrapf(err, "failed to delete PluginKeyValue with pluginId=%s and key=%s", pluginId, key)
|
2019-07-16 16:10:17 +03:00
|
|
|
}
|
|
|
|
|
return nil
|
2017-11-27 17:23:35 -05:00
|
|
|
}
|
2018-10-03 13:04:37 -07:00
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) DeleteAllForPlugin(pluginId string) error {
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Delete("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": pluginId})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrapf(err, "failed to get all PluginKeyValues with pluginId=%s ", pluginId)
|
2019-07-10 21:54:38 +03:00
|
|
|
}
|
|
|
|
|
return nil
|
2018-10-10 19:55:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) DeleteAllExpired() error {
|
2019-07-09 18:15:35 +03:00
|
|
|
currentTime := model.GetMillis()
|
2020-07-15 23:52:35 +07:00
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Delete("PluginKeyValueStore").
|
|
|
|
|
Where(sq.NotEq{"ExpireAt": 0}).
|
|
|
|
|
Where(sq.Lt{"ExpireAt": currentTime})
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return errors.Wrap(err, "failed to delete all expired PluginKeyValues")
|
2019-07-09 18:15:35 +03:00
|
|
|
}
|
|
|
|
|
return nil
|
2018-10-10 19:55:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-13 01:35:57 -04:00
|
|
|
func (ps SqlPluginStore) List(pluginId string, offset int, limit int) ([]string, error) {
|
2018-10-03 13:04:37 -07:00
|
|
|
if limit <= 0 {
|
2020-02-18 16:32:46 -04:00
|
|
|
limit = defaultPluginKeyFetchLimit
|
2018-10-03 13:04:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if offset <= 0 {
|
|
|
|
|
offset = 0
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-08 18:35:33 +03:00
|
|
|
var keys []string
|
2020-07-15 23:52:35 +07:00
|
|
|
|
|
|
|
|
query := ps.getQueryBuilder().
|
|
|
|
|
Select("Pkey").
|
|
|
|
|
From("PluginKeyValueStore").
|
|
|
|
|
Where(sq.Eq{"PluginId": pluginId}).
|
|
|
|
|
Where(sq.Or{
|
|
|
|
|
sq.Eq{"ExpireAt": int(0)},
|
|
|
|
|
sq.Gt{"ExpireAt": model.GetMillis()},
|
|
|
|
|
}).
|
|
|
|
|
OrderBy("PKey").
|
|
|
|
|
Limit(uint64(limit)).
|
|
|
|
|
Offset(uint64(offset))
|
|
|
|
|
|
|
|
|
|
queryString, args, err := query.ToSql()
|
|
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return nil, errors.Wrap(err, "plugin_tosql")
|
2020-07-15 23:52:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = ps.GetReplica().Select(&keys, queryString, args...)
|
2019-07-08 18:35:33 +03:00
|
|
|
if err != nil {
|
2020-08-13 01:35:57 -04:00
|
|
|
return nil, errors.Wrapf(err, "failed to get PluginKeyValues with pluginId=%s", pluginId)
|
2019-07-08 18:35:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return keys, nil
|
2018-10-03 13:04:37 -07:00
|
|
|
}
|