github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/store/sqlstore/plugin_store.go (about) 1 // Copyright (c) 2017-present Xenia, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "database/sql" 8 "fmt" 9 "net/http" 10 11 "github.com/xzl8028/xenia-server/model" 12 "github.com/xzl8028/xenia-server/store" 13 ) 14 15 const ( 16 DEFAULT_PLUGIN_KEY_FETCH_LIMIT = 10 17 ) 18 19 type SqlPluginStore struct { 20 SqlStore 21 } 22 23 func NewSqlPluginStore(sqlStore SqlStore) store.PluginStore { 24 s := &SqlPluginStore{sqlStore} 25 26 for _, db := range sqlStore.GetAllConns() { 27 table := db.AddTableWithName(model.PluginKeyValue{}, "PluginKeyValueStore").SetKeys(false, "PluginId", "Key") 28 table.ColMap("PluginId").SetMaxSize(190) 29 table.ColMap("Key").SetMaxSize(50) 30 table.ColMap("Value").SetMaxSize(8192) 31 } 32 33 return s 34 } 35 36 func (ps SqlPluginStore) CreateIndexesIfNotExists() { 37 } 38 39 func (ps SqlPluginStore) SaveOrUpdate(kv *model.PluginKeyValue) store.StoreChannel { 40 return store.Do(func(result *store.StoreResult) { 41 if result.Err = kv.IsValid(); result.Err != nil { 42 return 43 } 44 45 if ps.DriverName() == model.DATABASE_DRIVER_POSTGRES { 46 // Unfortunately PostgreSQL pre-9.5 does not have an atomic upsert, so we use 47 // separate update and insert queries to accomplish our upsert 48 if rowsAffected, err := ps.GetMaster().Update(kv); err != nil { 49 result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 50 return 51 } else if rowsAffected == 0 { 52 // No rows were affected by the update, so let's try an insert 53 if err := ps.GetMaster().Insert(kv); err != nil { 54 // If the error is from unique constraints violation, it's the result of a 55 // valid race and we can report success. Otherwise we have a real error and 56 // need to return it 57 if !IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey"}) { 58 result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 59 return 60 } 61 } 62 } 63 } else if ps.DriverName() == model.DATABASE_DRIVER_MYSQL { 64 if _, err := ps.GetMaster().Exec("INSERT INTO PluginKeyValueStore (PluginId, PKey, PValue, ExpireAt) VALUES(:PluginId, :Key, :Value, :ExpireAt) ON DUPLICATE KEY UPDATE PValue = :Value, ExpireAt = :ExpireAt", map[string]interface{}{"PluginId": kv.PluginId, "Key": kv.Key, "Value": kv.Value, "ExpireAt": kv.ExpireAt}); err != nil { 65 result.Err = model.NewAppError("SqlPluginStore.SaveOrUpdate", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 66 return 67 } 68 } 69 70 result.Data = kv 71 }) 72 } 73 74 func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) { 75 if err := kv.IsValid(); err != nil { 76 return false, err 77 } 78 79 if oldValue == nil { 80 // Insert if oldValue is nil 81 if err := ps.GetMaster().Insert(kv); err != nil { 82 // If the error is from unique constraints violation, it's the result of a 83 // race condition, return false and no error. Otherwise we have a real error and 84 // need to return it. 85 if IsUniqueConstraintError(err, []string{"PRIMARY", "PluginId", "Key", "PKey"}) { 86 return false, nil 87 } else { 88 return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 89 } 90 } 91 } else { 92 // Update if oldValue is not nil 93 updateResult, err := ps.GetMaster().Exec( 94 `UPDATE PluginKeyValueStore SET PValue = :New WHERE PluginId = :PluginId AND PKey = :Key AND PValue = :Old`, 95 map[string]interface{}{ 96 "PluginId": kv.PluginId, 97 "Key": kv.Key, 98 "Old": oldValue, 99 "New": kv.Value, 100 }, 101 ) 102 if err != nil { 103 return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 104 } 105 106 if rowsAffected, err := updateResult.RowsAffected(); err != nil { 107 // Failed to update 108 return false, model.NewAppError("SqlPluginStore.CompareAndSet", "store.sql_plugin_store.save.app_error", nil, err.Error(), http.StatusInternalServerError) 109 } else if rowsAffected == 0 { 110 // No rows were affected by the update, where condition was not satisfied, 111 // return false, but no error. 112 return false, nil 113 } 114 } 115 116 return true, nil 117 } 118 119 func (ps SqlPluginStore) Get(pluginId, key string) store.StoreChannel { 120 return store.Do(func(result *store.StoreResult) { 121 var kv *model.PluginKeyValue 122 currentTime := model.GetMillis() 123 if err := ps.GetReplica().SelectOne(&kv, "SELECT * FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key AND (ExpireAt = 0 OR ExpireAt > :CurrentTime)", map[string]interface{}{"PluginId": pluginId, "Key": key, "CurrentTime": currentTime}); err != nil { 124 if err == sql.ErrNoRows { 125 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) 126 } else { 127 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) 128 } 129 } else { 130 result.Data = kv 131 } 132 }) 133 } 134 135 func (ps SqlPluginStore) Delete(pluginId, key string) store.StoreChannel { 136 return store.Do(func(result *store.StoreResult) { 137 if _, err := ps.GetMaster().Exec("DELETE FROM PluginKeyValueStore WHERE PluginId = :PluginId AND PKey = :Key", map[string]interface{}{"PluginId": pluginId, "Key": key}); err != nil { 138 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) 139 } else { 140 result.Data = true 141 } 142 }) 143 } 144 145 func (ps SqlPluginStore) DeleteAllForPlugin(pluginId string) store.StoreChannel { 146 return store.Do(func(result *store.StoreResult) { 147 if _, err := ps.GetMaster().Exec("DELETE FROM PluginKeyValueStore WHERE PluginId = :PluginId", map[string]interface{}{"PluginId": pluginId}); err != nil { 148 result.Err = model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError) 149 } else { 150 result.Data = true 151 } 152 }) 153 } 154 155 func (ps SqlPluginStore) DeleteAllExpired() store.StoreChannel { 156 return store.Do(func(result *store.StoreResult) { 157 currentTime := model.GetMillis() 158 if _, err := ps.GetMaster().Exec("DELETE FROM PluginKeyValueStore WHERE ExpireAt != 0 AND ExpireAt < :CurrentTime", map[string]interface{}{"CurrentTime": currentTime}); err != nil { 159 result.Err = model.NewAppError("SqlPluginStore.Delete", "store.sql_plugin_store.delete.app_error", nil, fmt.Sprintf("current_time=%v, err=%v", currentTime, err.Error()), http.StatusInternalServerError) 160 } else { 161 result.Data = true 162 } 163 }) 164 } 165 166 func (ps SqlPluginStore) List(pluginId string, offset int, limit int) store.StoreChannel { 167 if limit <= 0 { 168 limit = DEFAULT_PLUGIN_KEY_FETCH_LIMIT 169 } 170 171 if offset <= 0 { 172 offset = 0 173 } 174 175 return store.Do(func(result *store.StoreResult) { 176 var keys []string 177 _, err := ps.GetReplica().Select(&keys, "SELECT PKey FROM PluginKeyValueStore WHERE PluginId = :PluginId order by PKey limit :Limit offset :Offset", map[string]interface{}{"PluginId": pluginId, "Limit": limit, "Offset": offset}) 178 if err != nil { 179 result.Err = model.NewAppError("SqlPluginStore.List", "store.sql_plugin_store.list.app_error", nil, fmt.Sprintf("plugin_id=%v, err=%v", pluginId, err.Error()), http.StatusInternalServerError) 180 } else { 181 result.Data = keys 182 } 183 }) 184 }