github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+incompatible/store/sqlstore/preference_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  	"net/http"
     8  
     9  	l4g "github.com/alecthomas/log4go"
    10  	"github.com/mattermost/gorp"
    11  
    12  	"github.com/mattermost/mattermost-server/model"
    13  	"github.com/mattermost/mattermost-server/store"
    14  	"github.com/mattermost/mattermost-server/utils"
    15  )
    16  
    17  type SqlPreferenceStore struct {
    18  	SqlStore
    19  }
    20  
    21  func NewSqlPreferenceStore(sqlStore SqlStore) store.PreferenceStore {
    22  	s := &SqlPreferenceStore{sqlStore}
    23  
    24  	for _, db := range sqlStore.GetAllConns() {
    25  		table := db.AddTableWithName(model.Preference{}, "Preferences").SetKeys(false, "UserId", "Category", "Name")
    26  		table.ColMap("UserId").SetMaxSize(26)
    27  		table.ColMap("Category").SetMaxSize(32)
    28  		table.ColMap("Name").SetMaxSize(32)
    29  		table.ColMap("Value").SetMaxSize(2000)
    30  	}
    31  
    32  	return s
    33  }
    34  
    35  func (s SqlPreferenceStore) CreateIndexesIfNotExists() {
    36  	s.CreateIndexIfNotExists("idx_preferences_user_id", "Preferences", "UserId")
    37  	s.CreateIndexIfNotExists("idx_preferences_category", "Preferences", "Category")
    38  	s.CreateIndexIfNotExists("idx_preferences_name", "Preferences", "Name")
    39  }
    40  
    41  func (s SqlPreferenceStore) DeleteUnusedFeatures() {
    42  	l4g.Debug(utils.T("store.sql_preference.delete_unused_features.debug"))
    43  
    44  	sql := `DELETE
    45  		FROM Preferences
    46  	WHERE
    47  	Category = :Category
    48  	AND Value = :Value
    49  	AND Name LIKE '` + store.FEATURE_TOGGLE_PREFIX + `%'`
    50  
    51  	queryParams := map[string]string{
    52  		"Category": model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS,
    53  		"Value":    "false",
    54  	}
    55  	s.GetMaster().Exec(sql, queryParams)
    56  }
    57  
    58  func (s SqlPreferenceStore) Save(preferences *model.Preferences) store.StoreChannel {
    59  	return store.Do(func(result *store.StoreResult) {
    60  		// wrap in a transaction so that if one fails, everything fails
    61  		transaction, err := s.GetMaster().Begin()
    62  		if err != nil {
    63  			result.Err = model.NewAppError("SqlPreferenceStore.Save", "store.sql_preference.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
    64  		} else {
    65  			for _, preference := range *preferences {
    66  				if upsertResult := s.save(transaction, &preference); upsertResult.Err != nil {
    67  					*result = upsertResult
    68  					break
    69  				}
    70  			}
    71  
    72  			if result.Err == nil {
    73  				if err := transaction.Commit(); err != nil {
    74  					// don't need to rollback here since the transaction is already closed
    75  					result.Err = model.NewAppError("SqlPreferenceStore.Save", "store.sql_preference.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
    76  				} else {
    77  					result.Data = len(*preferences)
    78  				}
    79  			} else {
    80  				if err := transaction.Rollback(); err != nil {
    81  					result.Err = model.NewAppError("SqlPreferenceStore.Save", "store.sql_preference.save.rollback_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
    82  				}
    83  			}
    84  		}
    85  	})
    86  }
    87  
    88  func (s SqlPreferenceStore) save(transaction *gorp.Transaction, preference *model.Preference) store.StoreResult {
    89  	result := store.StoreResult{}
    90  
    91  	preference.PreUpdate()
    92  
    93  	if result.Err = preference.IsValid(); result.Err != nil {
    94  		return result
    95  	}
    96  
    97  	params := map[string]interface{}{
    98  		"UserId":   preference.UserId,
    99  		"Category": preference.Category,
   100  		"Name":     preference.Name,
   101  		"Value":    preference.Value,
   102  	}
   103  
   104  	if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
   105  		if _, err := transaction.Exec(
   106  			`INSERT INTO
   107  				Preferences
   108  				(UserId, Category, Name, Value)
   109  			VALUES
   110  				(:UserId, :Category, :Name, :Value)
   111  			ON DUPLICATE KEY UPDATE
   112  				Value = :Value`, params); err != nil {
   113  			result.Err = model.NewAppError("SqlPreferenceStore.save", "store.sql_preference.save.updating.app_error", nil, err.Error(), http.StatusInternalServerError)
   114  		}
   115  	} else if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   116  		// postgres has no way to upsert values until version 9.5 and trying inserting and then updating causes transactions to abort
   117  		count, err := transaction.SelectInt(
   118  			`SELECT
   119  				count(0)
   120  			FROM
   121  				Preferences
   122  			WHERE
   123  				UserId = :UserId
   124  				AND Category = :Category
   125  				AND Name = :Name`, params)
   126  		if err != nil {
   127  			result.Err = model.NewAppError("SqlPreferenceStore.save", "store.sql_preference.save.updating.app_error", nil, err.Error(), http.StatusInternalServerError)
   128  			return result
   129  		}
   130  
   131  		if count == 1 {
   132  			s.update(transaction, preference)
   133  		} else {
   134  			s.insert(transaction, preference)
   135  		}
   136  	} else {
   137  		result.Err = model.NewAppError("SqlPreferenceStore.save", "store.sql_preference.save.missing_driver.app_error", nil, "Failed to update preference because of missing driver", http.StatusNotImplemented)
   138  	}
   139  
   140  	return result
   141  }
   142  
   143  func (s SqlPreferenceStore) insert(transaction *gorp.Transaction, preference *model.Preference) store.StoreResult {
   144  	result := store.StoreResult{}
   145  
   146  	if err := transaction.Insert(preference); err != nil {
   147  		if IsUniqueConstraintError(err, []string{"UserId", "preferences_pkey"}) {
   148  			result.Err = model.NewAppError("SqlPreferenceStore.insert", "store.sql_preference.insert.exists.app_error", nil,
   149  				"user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", "+err.Error(), http.StatusBadRequest)
   150  		} else {
   151  			result.Err = model.NewAppError("SqlPreferenceStore.insert", "store.sql_preference.insert.save.app_error", nil,
   152  				"user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", "+err.Error(), http.StatusInternalServerError)
   153  		}
   154  	}
   155  
   156  	return result
   157  }
   158  
   159  func (s SqlPreferenceStore) update(transaction *gorp.Transaction, preference *model.Preference) store.StoreResult {
   160  	result := store.StoreResult{}
   161  
   162  	if _, err := transaction.Update(preference); err != nil {
   163  		result.Err = model.NewAppError("SqlPreferenceStore.update", "store.sql_preference.update.app_error", nil,
   164  			"user_id="+preference.UserId+", category="+preference.Category+", name="+preference.Name+", "+err.Error(), http.StatusInternalServerError)
   165  	}
   166  
   167  	return result
   168  }
   169  
   170  func (s SqlPreferenceStore) Get(userId string, category string, name string) store.StoreChannel {
   171  	return store.Do(func(result *store.StoreResult) {
   172  		var preference model.Preference
   173  
   174  		if err := s.GetReplica().SelectOne(&preference,
   175  			`SELECT
   176  				*
   177  			FROM
   178  				Preferences
   179  			WHERE
   180  				UserId = :UserId
   181  				AND Category = :Category
   182  				AND Name = :Name`, map[string]interface{}{"UserId": userId, "Category": category, "Name": name}); err != nil {
   183  			result.Err = model.NewAppError("SqlPreferenceStore.Get", "store.sql_preference.get.app_error", nil, err.Error(), http.StatusInternalServerError)
   184  		} else {
   185  			result.Data = preference
   186  		}
   187  	})
   188  }
   189  
   190  func (s SqlPreferenceStore) GetCategory(userId string, category string) store.StoreChannel {
   191  	return store.Do(func(result *store.StoreResult) {
   192  		var preferences model.Preferences
   193  
   194  		if _, err := s.GetReplica().Select(&preferences,
   195  			`SELECT
   196  				*
   197  			FROM
   198  				Preferences
   199  			WHERE
   200  				UserId = :UserId
   201  				AND Category = :Category`, map[string]interface{}{"UserId": userId, "Category": category}); err != nil {
   202  			result.Err = model.NewAppError("SqlPreferenceStore.GetCategory", "store.sql_preference.get_category.app_error", nil, err.Error(), http.StatusInternalServerError)
   203  		} else {
   204  			result.Data = preferences
   205  		}
   206  	})
   207  }
   208  
   209  func (s SqlPreferenceStore) GetAll(userId string) store.StoreChannel {
   210  	return store.Do(func(result *store.StoreResult) {
   211  		var preferences model.Preferences
   212  
   213  		if _, err := s.GetReplica().Select(&preferences,
   214  			`SELECT
   215  				*
   216  			FROM
   217  				Preferences
   218  			WHERE
   219  				UserId = :UserId`, map[string]interface{}{"UserId": userId}); err != nil {
   220  			result.Err = model.NewAppError("SqlPreferenceStore.GetAll", "store.sql_preference.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
   221  		} else {
   222  			result.Data = preferences
   223  		}
   224  	})
   225  }
   226  
   227  func (s SqlPreferenceStore) PermanentDeleteByUser(userId string) store.StoreChannel {
   228  	return store.Do(func(result *store.StoreResult) {
   229  		if _, err := s.GetMaster().Exec(
   230  			`DELETE FROM Preferences WHERE UserId = :UserId`, map[string]interface{}{"UserId": userId}); err != nil {
   231  			result.Err = model.NewAppError("SqlPreferenceStore.Delete", "store.sql_preference.permanent_delete_by_user.app_error", nil, err.Error(), http.StatusInternalServerError)
   232  		}
   233  	})
   234  }
   235  
   236  func (s SqlPreferenceStore) IsFeatureEnabled(feature, userId string) store.StoreChannel {
   237  	return store.Do(func(result *store.StoreResult) {
   238  		if value, err := s.GetReplica().SelectStr(`SELECT
   239  				value
   240  			FROM
   241  				Preferences
   242  			WHERE
   243  				UserId = :UserId
   244  				AND Category = :Category
   245  				AND Name = :Name`, map[string]interface{}{"UserId": userId, "Category": model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS, "Name": store.FEATURE_TOGGLE_PREFIX + feature}); err != nil {
   246  			result.Err = model.NewAppError("SqlPreferenceStore.IsFeatureEnabled", "store.sql_preference.is_feature_enabled.app_error", nil, err.Error(), http.StatusInternalServerError)
   247  		} else {
   248  			result.Data = value == "true"
   249  		}
   250  	})
   251  }
   252  
   253  func (s SqlPreferenceStore) Delete(userId, category, name string) store.StoreChannel {
   254  	return store.Do(func(result *store.StoreResult) {
   255  		if _, err := s.GetMaster().Exec(
   256  			`DELETE FROM
   257  				Preferences
   258  			WHERE
   259  				UserId = :UserId
   260  				AND Category = :Category
   261  				AND Name = :Name`, map[string]interface{}{"UserId": userId, "Category": category, "Name": name}); err != nil {
   262  			result.Err = model.NewAppError("SqlPreferenceStore.Delete", "store.sql_preference.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
   263  		}
   264  	})
   265  }
   266  
   267  func (s SqlPreferenceStore) DeleteCategory(userId string, category string) store.StoreChannel {
   268  	return store.Do(func(result *store.StoreResult) {
   269  		if _, err := s.GetMaster().Exec(
   270  			`DELETE FROM
   271  				Preferences
   272  			WHERE
   273  				UserId = :UserId
   274  				AND Category = :Category`, map[string]interface{}{"UserId": userId, "Category": category}); err != nil {
   275  			result.Err = model.NewAppError("SqlPreferenceStore.DeleteCategory", "store.sql_preference.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
   276  		}
   277  	})
   278  }
   279  
   280  func (s SqlPreferenceStore) DeleteCategoryAndName(category string, name string) store.StoreChannel {
   281  	return store.Do(func(result *store.StoreResult) {
   282  		if _, err := s.GetMaster().Exec(
   283  			`DELETE FROM
   284  				Preferences
   285  			WHERE
   286  				Name = :Name
   287  				AND Category = :Category`, map[string]interface{}{"Name": name, "Category": category}); err != nil {
   288  			result.Err = model.NewAppError("SqlPreferenceStore.DeleteCategoryAndName", "store.sql_preference.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
   289  		}
   290  	})
   291  }
   292  
   293  func (s SqlPreferenceStore) CleanupFlagsBatch(limit int64) store.StoreChannel {
   294  	return store.Do(func(result *store.StoreResult) {
   295  		query :=
   296  			`DELETE FROM
   297  				Preferences
   298  			WHERE
   299  				Category = :Category
   300  				AND Name IN (
   301  					SELECT
   302  						*
   303  					FROM (
   304  						SELECT
   305  							Preferences.Name
   306  						FROM
   307  							Preferences
   308  						LEFT JOIN
   309  							Posts
   310  						ON
   311  							Preferences.Name = Posts.Id
   312  						WHERE
   313  							Preferences.Category = :Category
   314  							AND Posts.Id IS null
   315  						LIMIT
   316  							:Limit
   317  					)
   318  					AS t
   319  				)`
   320  
   321  		sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"Category": model.PREFERENCE_CATEGORY_FLAGGED_POST, "Limit": limit})
   322  		if err != nil {
   323  			result.Err = model.NewAppError("SqlPostStore.CleanupFlagsBatch", "store.sql_preference.cleanup_flags_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   324  		} else {
   325  			rowsAffected, err1 := sqlResult.RowsAffected()
   326  			if err1 != nil {
   327  				result.Err = model.NewAppError("SqlPostStore.CleanupFlagsBatch", "store.sql_preference.cleanup_flags_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   328  				result.Data = int64(0)
   329  			} else {
   330  				result.Data = rowsAffected
   331  			}
   332  		}
   333  	})
   334  }