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