github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/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  	"net/http"
    11  
    12  	sq "github.com/Masterminds/squirrel"
    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, *model.AppError) {
    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, model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
    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, model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusBadRequest)
    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, model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
    78  		}
    79  
    80  		if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
    81  			return nil, model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
    82  		}
    83  	}
    84  
    85  	return kv, nil
    86  }
    87  
    88  func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) {
    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, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
   110  		}
   111  
   112  		if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   113  			return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
   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  			} else {
   124  				return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
   125  			}
   126  		}
   127  	} else {
   128  		currentTime := model.GetMillis()
   129  
   130  		// Update if oldValue is not nil
   131  		query := ps.getQueryBuilder().
   132  			Update("PluginKeyValueStore").
   133  			Set("PValue", kv.Value).
   134  			Set("ExpireAt", kv.ExpireAt).
   135  			Where(sq.Eq{"PluginId": kv.PluginId}).
   136  			Where(sq.Eq{"PKey": kv.Key}).
   137  			Where(sq.Eq{"PValue": oldValue}).
   138  			Where(sq.Or{
   139  				sq.Eq{"ExpireAt": int(0)},
   140  				sq.Gt{"ExpireAt": currentTime},
   141  			})
   142  
   143  		queryString, args, err := query.ToSql()
   144  		if err != nil {
   145  			return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
   146  		}
   147  
   148  		updateResult, err := ps.GetMaster().Exec(queryString, args...)
   149  		if err != nil {
   150  			return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
   151  		}
   152  
   153  		if rowsAffected, err := updateResult.RowsAffected(); err != nil {
   154  			// Failed to update
   155  			return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
   156  		} else if rowsAffected == 0 {
   157  			if ps.DriverName() == model.DATABASE_DRIVER_MYSQL && bytes.Equal(oldValue, kv.Value) {
   158  				// ROW_COUNT on MySQL is zero even if the row existed but no changes to the row were required.
   159  				// Check if the row exists with the required value to distinguish this case. Strictly speaking,
   160  				// this isn't a good use of CompareAndSet anyway, since there's no corresponding guarantee of
   161  				// atomicity. Nevertheless, let's return results consistent with Postgres and with what might
   162  				// be expected in this case.
   163  				query := ps.getQueryBuilder().
   164  					Select("COUNT(*)").
   165  					From("PluginKeyValueStore").
   166  					Where(sq.Eq{"PluginId": kv.PluginId}).
   167  					Where(sq.Eq{"PKey": kv.Key}).
   168  					Where(sq.Eq{"PValue": kv.Value}).
   169  					Where(sq.Or{
   170  						sq.Eq{"ExpireAt": int(0)},
   171  						sq.Gt{"ExpireAt": currentTime},
   172  					})
   173  
   174  				queryString, args, err := query.ToSql()
   175  				if err != nil {
   176  					return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql.build_query.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", kv.PluginId, kv.Key, err.Error()), http.StatusInternalServerError)
   177  				}
   178  
   179  				count, err := ps.GetReplica().SelectInt(queryString, args...)
   180  				if err != nil {
   181  					return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.compare_and_set.mysql_select.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", kv.PluginId, kv.Key, err.Error()), http.StatusInternalServerError)
   182  				}
   183  
   184  				if count == 0 {
   185  					return false, nil
   186  				} else if count == 1 {
   187  					return true, nil
   188  				} else {
   189  					return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.compare_and_set.too_many_rows.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, count=%d", kv.PluginId, kv.Key, count), http.StatusInternalServerError)
   190  				}
   191  			}
   192  
   193  			// No rows were affected by the update, where condition was not satisfied,
   194  			// return false, but no error.
   195  			return false, nil
   196  		}
   197  	}
   198  
   199  	return true, nil
   200  }
   201  
   202  func (ps SqlPluginStore) CompareAndDelete(kv *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) {
   203  	if err := kv.IsValid(); err != nil {
   204  		return false, err
   205  	}
   206  
   207  	if oldValue == nil {
   208  		// nil can't be stored. Return showing that we didn't do anything
   209  		return false, nil
   210  	}
   211  
   212  	query := ps.getQueryBuilder().
   213  		Delete("PluginKeyValueStore").
   214  		Where(sq.Eq{"PluginId": kv.PluginId}).
   215  		Where(sq.Eq{"PKey": kv.Key}).
   216  		Where(sq.Eq{"PValue": oldValue}).
   217  		Where(sq.Or{
   218  			sq.Eq{"ExpireAt": int(0)},
   219  			sq.Gt{"ExpireAt": model.GetMillis()},
   220  		})
   221  
   222  	queryString, args, err := query.ToSql()
   223  	if err != nil {
   224  		return false, model.NewAppError("SqlPluginStore.CompareAndDelete", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
   225  	}
   226  
   227  	deleteResult, err := ps.GetMaster().Exec(queryString, args...)
   228  	if err != nil {
   229  		return false, model.NewAppError("SqlPluginStore.CompareAndDelete", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
   230  	}
   231  
   232  	if rowsAffected, err := deleteResult.RowsAffected(); err != nil {
   233  		return false, model.NewAppError("SqlPluginStore.CompareAndDelete", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError)
   234  	} else if rowsAffected == 0 {
   235  		return false, nil
   236  	}
   237  
   238  	return true, nil
   239  }
   240  
   241  func (ps SqlPluginStore) SetWithOptions(pluginId string, key string, value []byte, opt model.PluginKVSetOptions) (bool, *model.AppError) {
   242  	if err := opt.IsValid(); err != nil {
   243  		return false, err
   244  	}
   245  
   246  	kv, err := model.NewPluginKeyValueFromOptions(pluginId, key, value, opt)
   247  	if err != nil {
   248  		return false, err
   249  	}
   250  
   251  	if opt.Atomic {
   252  		return ps.CompareAndSet(kv, opt.OldValue)
   253  	}
   254  
   255  	savedKv, err := ps.SaveOrUpdate(kv)
   256  	if err != nil {
   257  		return false, err
   258  	}
   259  
   260  	return savedKv != nil, nil
   261  }
   262  
   263  func (ps SqlPluginStore) Get(pluginId, key string) (*model.PluginKeyValue, *model.AppError) {
   264  	currentTime := model.GetMillis()
   265  
   266  	failure := func(err error, statusCode int) *model.AppError {
   267  		return model.NewAppError(
   268  			"SqlPluginStore.Get",
   269  			"store.sql_plugin_store.get.app_error",
   270  			nil,
   271  			fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()),
   272  			statusCode,
   273  		)
   274  	}
   275  
   276  	query := ps.getQueryBuilder().Select("PluginId, PKey, PValue, ExpireAt").
   277  		From("PluginKeyValueStore").
   278  		Where(sq.Eq{"PluginId": pluginId}).
   279  		Where(sq.Eq{"PKey": key}).
   280  		Where(sq.Or{sq.Eq{"ExpireAt": 0}, sq.Gt{"ExpireAt": currentTime}})
   281  	queryString, args, err := query.ToSql()
   282  	if err != nil {
   283  		return nil, failure(err, http.StatusInternalServerError)
   284  	}
   285  	row := ps.GetReplica().Db.QueryRow(queryString, args...)
   286  	var kv model.PluginKeyValue
   287  	if err := row.Scan(&kv.PluginId, &kv.Key, &kv.Value, &kv.ExpireAt); err != nil {
   288  		if err == sql.ErrNoRows {
   289  			return nil, failure(err, http.StatusNotFound)
   290  		}
   291  		return nil, failure(err, http.StatusInternalServerError)
   292  	}
   293  
   294  	return &kv, nil
   295  }
   296  
   297  func (ps SqlPluginStore) Delete(pluginId, key string) *model.AppError {
   298  	query := ps.getQueryBuilder().
   299  		Delete("PluginKeyValueStore").
   300  		Where(sq.Eq{"PluginId": pluginId}).
   301  		Where(sq.Eq{"Pkey": key})
   302  
   303  	queryString, args, err := query.ToSql()
   304  	if err != nil {
   305  		return model.NewAppError("SqlPluginStore.Delete", "store.sql.build_query.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusInternalServerError)
   306  	}
   307  
   308  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   309  		return model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusInternalServerError)
   310  	}
   311  	return nil
   312  }
   313  
   314  func (ps SqlPluginStore) DeleteAllForPlugin(pluginId string) *model.AppError {
   315  	query := ps.getQueryBuilder().
   316  		Delete("PluginKeyValueStore").
   317  		Where(sq.Eq{"PluginId": pluginId})
   318  
   319  	queryString, args, err := query.ToSql()
   320  	if err != nil {
   321  		return model.NewAppError("SqlPluginStore.Delete", "store.sql.build_query.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError)
   322  	}
   323  
   324  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   325  		return model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError)
   326  	}
   327  	return nil
   328  }
   329  
   330  func (ps SqlPluginStore) DeleteAllExpired() *model.AppError {
   331  	currentTime := model.GetMillis()
   332  
   333  	query := ps.getQueryBuilder().
   334  		Delete("PluginKeyValueStore").
   335  		Where(sq.NotEq{"ExpireAt": 0}).
   336  		Where(sq.Lt{"ExpireAt": currentTime})
   337  
   338  	queryString, args, err := query.ToSql()
   339  	if err != nil {
   340  		return model.NewAppError("SqlPluginStore.Delete", "store.sql.build_query.app_error", nil, fmt.Sprintf("current_time=%v, err=%v", currentTime, err.Error()), http.StatusInternalServerError)
   341  	}
   342  
   343  	if _, err := ps.GetMaster().Exec(queryString, args...); err != nil {
   344  		return model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("current_time=%v, err=%v", currentTime, err.Error()), http.StatusInternalServerError)
   345  	}
   346  	return nil
   347  }
   348  
   349  func (ps SqlPluginStore) List(pluginId string, offset int, limit int) ([]string, *model.AppError) {
   350  	if limit <= 0 {
   351  		limit = defaultPluginKeyFetchLimit
   352  	}
   353  
   354  	if offset <= 0 {
   355  		offset = 0
   356  	}
   357  
   358  	var keys []string
   359  
   360  	query := ps.getQueryBuilder().
   361  		Select("Pkey").
   362  		From("PluginKeyValueStore").
   363  		Where(sq.Eq{"PluginId": pluginId}).
   364  		Where(sq.Or{
   365  			sq.Eq{"ExpireAt": int(0)},
   366  			sq.Gt{"ExpireAt": model.GetMillis()},
   367  		}).
   368  		OrderBy("PKey").
   369  		Limit(uint64(limit)).
   370  		Offset(uint64(offset))
   371  
   372  	queryString, args, err := query.ToSql()
   373  	if err != nil {
   374  		return nil, model.NewAppError("SqlPluginStore.List", "store.sql.build_query.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError)
   375  	}
   376  
   377  	_, err = ps.GetReplica().Select(&keys, queryString, args...)
   378  	if err != nil {
   379  		return nil, model.NewAppError("SqlPluginStore.List", "store.sql_plugin_store.list.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError)
   380  	}
   381  
   382  	return keys, nil
   383  }