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 }