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 }