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  }