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 }