From 6176bcff6977bda71f4fde10a52dde6d7d7ceb9a Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Mon, 27 Nov 2017 17:23:35 -0500 Subject: PLT-8131 (part2) Add plugin key value store support (#7902) * Add plugin key value store support * Add localization strings * Updates per feedback --- store/sqlstore/plugin_store.go | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 store/sqlstore/plugin_store.go (limited to 'store/sqlstore/plugin_store.go') diff --git a/store/sqlstore/plugin_store.go b/store/sqlstore/plugin_store.go new file mode 100644 index 000000000..c7c075d8a --- /dev/null +++ b/store/sqlstore/plugin_store.go @@ -0,0 +1,92 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package sqlstore + +import ( + "database/sql" + "fmt" + "net/http" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/store" +) + +type SqlPluginStore struct { + SqlStore +} + +func NewSqlPluginStore(sqlStore SqlStore) store.PluginStore { + s := &SqlPluginStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(model.PluginKeyValue{}, "PluginKeyValueStore").SetKeys(false, "PluginId", "Key") + table.ColMap("Value").SetMaxSize(8192) + } + + return s +} + +func (ps SqlPluginStore) CreateIndexesIfNotExists() { +} + +func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + if result.Err = kv.IsValid(); result.Err != nil { + return + } + + if ps.DriverName() == model.DATABASE_DRIVER_POSTGRES { + // Unfortunately PostgreSQL pre-9.5 does not have an atomic upsert, so we use + // separate update and insert queries to accomplish our upsert + if rowsAffected, err := ps.GetMaster().Update(kv); err != nil { + result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } else if rowsAffected == 0 { + // No rows were affected by the update, so let's try an insert + if err := ps.GetMaster().Insert(kv); err != nil { + // If the error is from unique constraints violation, it's the result of a + // valid race and we can report success. Otherwise we have a real error and + // need to return it + if !IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey"}) { + result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } + } + } + } else if ps.DriverName() == model.DATABASE_DRIVER_MYSQL { + if _, err := ps.GetMaster().Exec("INSERT INTO PluginKeyValueStore (PluginId, PKey, PValue) VALUES(:PluginId, :Key, :Value) ON DUPLICATE KEY UPDATE PValue = :Value", map[string]interface{}{"PluginId": kv.PluginId, "Key": kv.Key, "Value": kv.Value}); err != nil { + result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) + return + } + } + + result.Data = kv + }) +} + +func (ps SqlPluginStore) Get(pluginId, key string) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + var kv *model.PluginKeyValue + + if err := ps.GetReplica().SelectOne(&kv, "SELECT * FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key", map[string]interface{}{"PluginId": pluginId, "Key": key}); err != nil { + if err == sql.ErrNoRows { + result.Err = model.NewAppError("SqlPluginStore.Get", "store.sql_plugin_store.get.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusNotFound) + } else { + result.Err = model.NewAppError("SqlPluginStore.Get", "store.sql_plugin_store.get.app_error", nil, fmt.Sprintf("plugin_id=%v, key=%v, err=%v", pluginId, key, err.Error()), http.StatusInternalServerError) + } + } else { + result.Data = kv + } + }) +} + +func (ps SqlPluginStore) Delete(pluginId, key string) store.StoreChannel { + return store.Do(func(result *store.StoreResult) { + if _, err := ps.GetMaster().Exec("DELETE FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key", map[string]interface{}{"PluginId": pluginId, "Key": key}); err != nil { + result.Err = 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) + } else { + result.Data = true + } + }) +} -- cgit v1.2.3-1-g7c22