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