github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/sqlstore/plugin_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  	"bytes"
     8  	"database/sql"
     9  	"fmt"
    10  
    11  	sq "github.com/Masterminds/squirrel"
    12  	"github.com/pkg/errors"
    13  
    14  	"github.com/mattermost/mattermost-server/v5/model"
    15  	"github.com/mattermost/mattermost-server/v5/store"
    16  )
    17  
    18  const (
    19  	defaultPluginKeyFetchLimit = 10
    20  )
    21  
    22  type SqlPluginStore struct {
    23  	*SqlStore
    24  }
    25  
    26  func newSqlPluginStore(sqlStore *SqlStore) store.PluginStore {
    27  	s := &SqlPluginStore{sqlStore}
    28  
    29  	for _, db := range sqlStore.GetAllConns() {
    30  		table := db.AddTableWithName(model.PluginKeyValue{}, "PluginKeyValueStore").SetKeys(false, "PluginId", "Key")
    31  		table.ColMap("PluginId").SetMaxSize(190)
    32  		table.ColMap("Key").SetMaxSize(50)
    33  		table.ColMap("Value").SetMaxSize(8192)
    34  	}
    35  
    36  	return s
    37  }
    38  
    39  func (ps SqlPluginStore) createIndexesIfNotExists() {
    40  }
    41  
    42  func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) (*model.PluginKeyValue, error) {
    43  	if err := kv.IsValid(); err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	if kv.Value == nil {
    48  		// Setting a key to nil is the same as removing it
    49  		err := ps.Delete(kv.PluginId, kv.Key)
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  
    54  		return kv, nil
    55  	}
    56  
    57  	if ps.DriverName() == model.DATABASE_DRIVER_POSTGRES {
    58  		// Unfortunately PostgreSQL pre-9.5 does not have an atomic upsert, so we use
    59  		// separate update and insert queries to accomplish our upsert
    60  		if rowsAffected, err := ps.GetMaster().Update(kv); err != nil {
    61  			return nil, errors.Wrap(err, "failed to update PluginKeyValue")
    62  		} else if rowsAffected == 0 {
    63  			// No rows were affected by the update, so let's try an insert
    64  			if err := ps.GetMaster().Insert(kv); err != nil {
    65  				return nil, errors.Wrap(err, "failed to save PluginKeyValue")
    66  			}
    67  		}
    68  	} else if ps.DriverName() == model.DATABASE_DRIVER_MYSQL {
    69  		query := ps.getQueryBuilder().
    70  			Insert("PluginKeyValueStore").
    71  			Columns("PluginId", "PKey", "PValue", "ExpireAt").
    72  			Values(kv.PluginId, kv.Key, kv.Value, kv.ExpireAt).
    73  			SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE PValue = ?, ExpireAt = ?", kv.Value, kv.ExpireAt))
    74  
    75  		queryString, args, err := query.ToSql()
    76  		if err != nil {
    77  			return nil, errors.Wrap(err, "plugin_tosql")
    78  		}
    79  
    80  		if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
    81  			return nil, errors.Wrap(err, "failed to upsert PluginKeyValue")
    82  		}
    83  	}
    84  
    85  	return kv, nil
    86  }
    87  
    88  func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte) (bool, error) {
    89  	if err := kv.IsValid(); err != nil {
    90  		return false, err
    91  	}
    92  
    93  	if kv.Value == nil {
    94  		// Setting a key to nil is the same as removing it
    95  		return ps.CompareAndDelete(kv, oldValue)
    96  	}
    97  
    98  	if oldValue == nil {
    99  		// Delete any existing, expired value.
   100  		query := ps.getQueryBuilder().
   101  			Delete("PluginKeyValueStore").
   102  			Where(sq.Eq{"PluginId": kv.PluginId}).
   103  			Where(sq.Eq{"PKey": kv.Key}).
   104  			Where(sq.NotEq{"ExpireAt": int(0)}).
   105  			Where(sq.Lt{"ExpireAt": model.GetMillis()})
   106  
   107  		queryString, args, err := query.ToSql()
   108  		if err != nil {
   109  			return false, errors.Wrap(err, "plugin_tosql")
   110  		}
   111  
   112  		if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   113  			return false, errors.Wrap(err, "failed to delete PluginKeyValue")
   114  		}
   115  
   116  		// Insert if oldValue is nil
   117  		if err := ps.GetMaster().Insert(kv); err != nil {
   118  			// If the error is from unique constraints violation, it's the result of a
   119  			// race condition, return false and no error. Otherwise we have a real error and
   120  			// need to return it.
   121  			if IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey", "pkey"}) {
   122  				return false, nil
   123  			}
   124  			return false, errors.Wrap(err, "failed to insert PluginKeyValue")
   125  		}
   126  	} else {
   127  		currentTime := model.GetMillis()
   128  
   129  		// Update if oldValue is not nil
   130  		query := ps.getQueryBuilder().
   131  			Update("PluginKeyValueStore").
   132  			Set("PValue", kv.Value).
   133  			Set("ExpireAt", kv.ExpireAt).
   134  			Where(sq.Eq{"PluginId": kv.PluginId}).
   135  			Where(sq.Eq{"PKey": kv.Key}).
   136  			Where(sq.Eq{"PValue": oldValue}).
   137  			Where(sq.Or{
   138  				sq.Eq{"ExpireAt": int(0)},
   139  				sq.Gt{"ExpireAt": currentTime},
   140  			})
   141  
   142  		queryString, args, err := query.ToSql()
   143  		if err != nil {
   144  			return false, errors.Wrap(err, "plugin_tosql")
   145  		}
   146  
   147  		updateResult, err := ps.GetMaster().Exec(queryString, args...)
   148  		if err != nil {
   149  			return false, errors.Wrap(err, "failed to update PluginKeyValue")
   150  		}
   151  
   152  		if rowsAffected, err := updateResult.RowsAffected(); err != nil {
   153  			// Failed to update
   154  			return false, errors.Wrap(err, "unable to get rows affected")
   155  		} else if rowsAffected == 0 {
   156  			if ps.DriverName() == model.DATABASE_DRIVER_MYSQL && bytes.Equal(oldValue, kv.Value) {
   157  				// ROW_COUNT on MySQL is zero even if the row existed but no changes to the row were required.
   158  				// Check if the row exists with the required value to distinguish this case. Strictly speaking,
   159  				// this isn't a good use of CompareAndSet anyway, since there's no corresponding guarantee of
   160  				// atomicity. Nevertheless, let's return results consistent with Postgres and with what might
   161  				// be expected in this case.
   162  				query := ps.getQueryBuilder().
   163  					Select("COUNT(*)").
   164  					From("PluginKeyValueStore").
   165  					Where(sq.Eq{"PluginId": kv.PluginId}).
   166  					Where(sq.Eq{"PKey": kv.Key}).
   167  					Where(sq.Eq{"PValue": kv.Value}).
   168  					Where(sq.Or{
   169  						sq.Eq{"ExpireAt": int(0)},
   170  						sq.Gt{"ExpireAt": currentTime},
   171  					})
   172  
   173  				queryString, args, err := query.ToSql()
   174  				if err != nil {
   175  					return false, errors.Wrap(err, "plugin_tosql")
   176  				}
   177  
   178  				count, err := ps.GetReplica().SelectInt(queryString, args...)
   179  				if err != nil {
   180  					return false, errors.Wrapf(err, "failed to count PluginKeyValue with pluginId=%s and key=%s", kv.PluginId, kv.Key)
   181  				}
   182  
   183  				if count == 0 {
   184  					return false, nil
   185  				} else if count == 1 {
   186  					return true, nil
   187  				} else {
   188  					return false, errors.Wrapf(err, "got too many rows when counting PluginKeyValue with pluginId=%s, key=%s, rows=%d", kv.PluginId, kv.Key, count)
   189  				}
   190  			}
   191  
   192  			// No rows were affected by the update, where condition was not satisfied,
   193  			// return false, but no error.
   194  			return false, nil
   195  		}
   196  	}
   197  
   198  	return true, nil
   199  }
   200  
   201  func (ps SqlPluginStore) CompareAndDelete(kv *model.PluginKeyValue, oldValue []byte) (bool, error) {
   202  	if err := kv.IsValid(); err != nil {
   203  		return false, err
   204  	}
   205  
   206  	if oldValue == nil {
   207  		// nil can't be stored. Return showing that we didn't do anything
   208  		return false, nil
   209  	}
   210  
   211  	query := ps.getQueryBuilder().
   212  		Delete("PluginKeyValueStore").
   213  		Where(sq.Eq{"PluginId": kv.PluginId}).
   214  		Where(sq.Eq{"PKey": kv.Key}).
   215  		Where(sq.Eq{"PValue": oldValue}).
   216  		Where(sq.Or{
   217  			sq.Eq{"ExpireAt": int(0)},
   218  			sq.Gt{"ExpireAt": model.GetMillis()},
   219  		})
   220  
   221  	queryString, args, err := query.ToSql()
   222  	if err != nil {
   223  		return false, errors.Wrap(err, "plugin_tosql")
   224  	}
   225  
   226  	deleteResult, err := ps.GetMaster().Exec(queryString, args...)
   227  	if err != nil {
   228  		return false, errors.Wrap(err, "failed to delete PluginKeyValue")
   229  	}
   230  
   231  	if rowsAffected, err := deleteResult.RowsAffected(); err != nil {
   232  		return false, errors.Wrap(err, "unable to get rows affected")
   233  	} else if rowsAffected == 0 {
   234  		return false, nil
   235  	}
   236  
   237  	return true, nil
   238  }
   239  
   240  func (ps SqlPluginStore) SetWithOptions(pluginId string, key string, value []byte, opt model.PluginKVSetOptions) (bool, error) {
   241  	if err := opt.IsValid(); err != nil {
   242  		return false, err
   243  	}
   244  
   245  	kv, err := model.NewPluginKeyValueFromOptions(pluginId, key, value, opt)
   246  	if err != nil {
   247  		return false, err
   248  	}
   249  
   250  	if opt.Atomic {
   251  		return ps.CompareAndSet(kv, opt.OldValue)
   252  	}
   253  
   254  	savedKv, nErr := ps.SaveOrUpdate(kv)
   255  	if nErr != nil {
   256  		return false, nErr
   257  	}
   258  
   259  	return savedKv != nil, nil
   260  }
   261  
   262  func (ps SqlPluginStore) Get(pluginId, key string) (*model.PluginKeyValue, error) {
   263  	currentTime := model.GetMillis()
   264  	query := ps.getQueryBuilder().Select("PluginId, PKey, PValue, ExpireAt").
   265  		From("PluginKeyValueStore").
   266  		Where(sq.Eq{"PluginId": pluginId}).
   267  		Where(sq.Eq{"PKey": key}).
   268  		Where(sq.Or{sq.Eq{"ExpireAt": 0}, sq.Gt{"ExpireAt": currentTime}})
   269  	queryString, args, err := query.ToSql()
   270  	if err != nil {
   271  		return nil, errors.Wrap(err, "plugin_tosql")
   272  	}
   273  
   274  	row := ps.GetReplica().Db.QueryRow(queryString, args...)
   275  	var kv model.PluginKeyValue
   276  	if err := row.Scan(&kv.PluginId, &kv.Key, &kv.Value, &kv.ExpireAt); err != nil {
   277  		if err == sql.ErrNoRows {
   278  			return nil, store.NewErrNotFound("PluginKeyValue", fmt.Sprintf("pluginId=%s, key=%s", pluginId, key))
   279  		}
   280  		return nil, errors.Wrapf(err, "failed to get PluginKeyValue with pluginId=%s and key=%s", pluginId, key)
   281  	}
   282  
   283  	return &kv, nil
   284  }
   285  
   286  func (ps SqlPluginStore) Delete(pluginId, key string) error {
   287  	query := ps.getQueryBuilder().
   288  		Delete("PluginKeyValueStore").
   289  		Where(sq.Eq{"PluginId": pluginId}).
   290  		Where(sq.Eq{"Pkey": key})
   291  
   292  	queryString, args, err := query.ToSql()
   293  	if err != nil {
   294  		return errors.Wrap(err, "plugin_tosql")
   295  	}
   296  
   297  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   298  		return errors.Wrapf(err, "failed to delete PluginKeyValue with pluginId=%s and key=%s", pluginId, key)
   299  	}
   300  	return nil
   301  }
   302  
   303  func (ps SqlPluginStore) DeleteAllForPlugin(pluginId string) error {
   304  	query := ps.getQueryBuilder().
   305  		Delete("PluginKeyValueStore").
   306  		Where(sq.Eq{"PluginId": pluginId})
   307  
   308  	queryString, args, err := query.ToSql()
   309  	if err != nil {
   310  		return errors.Wrap(err, "plugin_tosql")
   311  	}
   312  
   313  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   314  		return errors.Wrapf(err, "failed to get all PluginKeyValues with pluginId=%s ", pluginId)
   315  	}
   316  	return nil
   317  }
   318  
   319  func (ps SqlPluginStore) DeleteAllExpired() error {
   320  	currentTime := model.GetMillis()
   321  	query := ps.getQueryBuilder().
   322  		Delete("PluginKeyValueStore").
   323  		Where(sq.NotEq{"ExpireAt": 0}).
   324  		Where(sq.Lt{"ExpireAt": currentTime})
   325  
   326  	queryString, args, err := query.ToSql()
   327  	if err != nil {
   328  		return errors.Wrap(err, "plugin_tosql")
   329  	}
   330  
   331  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   332  		return errors.Wrap(err, "failed to delete all expired PluginKeyValues")
   333  	}
   334  	return nil
   335  }
   336  
   337  func (ps SqlPluginStore) List(pluginId string, offset int, limit int) ([]string, error) {
   338  	if limit <= 0 {
   339  		limit = defaultPluginKeyFetchLimit
   340  	}
   341  
   342  	if offset <= 0 {
   343  		offset = 0
   344  	}
   345  
   346  	var keys []string
   347  
   348  	query := ps.getQueryBuilder().
   349  		Select("Pkey").
   350  		From("PluginKeyValueStore").
   351  		Where(sq.Eq{"PluginId": pluginId}).
   352  		Where(sq.Or{
   353  			sq.Eq{"ExpireAt": int(0)},
   354  			sq.Gt{"ExpireAt": model.GetMillis()},
   355  		}).
   356  		OrderBy("PKey").
   357  		Limit(uint64(limit)).
   358  		Offset(uint64(offset))
   359  
   360  	queryString, args, err := query.ToSql()
   361  	if err != nil {
   362  		return nil, errors.Wrap(err, "plugin_tosql")
   363  	}
   364  
   365  	_, err = ps.GetReplica().Select(&keys, queryString, args...)
   366  	if err != nil {
   367  		return nil, errors.Wrapf(err, "failed to get PluginKeyValues with pluginId=%s", pluginId)
   368  	}
   369  
   370  	return keys, nil
   371  }