github.com/wgh-/mattermost-server@v4.8.0-rc2+incompatible/store/sqlstore/supplier.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"context"
     8  	dbsql "database/sql"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	sqltrace "log"
    13  	"os"
    14  	"strings"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	l4g "github.com/alecthomas/log4go"
    19  	"github.com/go-sql-driver/mysql"
    20  	"github.com/lib/pq"
    21  	"github.com/mattermost/gorp"
    22  	"github.com/mattermost/mattermost-server/einterfaces"
    23  	"github.com/mattermost/mattermost-server/model"
    24  	"github.com/mattermost/mattermost-server/store"
    25  	"github.com/mattermost/mattermost-server/utils"
    26  )
    27  
    28  const (
    29  	INDEX_TYPE_FULL_TEXT = "full_text"
    30  	INDEX_TYPE_DEFAULT   = "default"
    31  	MAX_DB_CONN_LIFETIME = 60
    32  	DB_PING_ATTEMPTS     = 18
    33  	DB_PING_TIMEOUT_SECS = 10
    34  )
    35  
    36  const (
    37  	EXIT_CREATE_TABLE                = 100
    38  	EXIT_DB_OPEN                     = 101
    39  	EXIT_PING                        = 102
    40  	EXIT_NO_DRIVER                   = 103
    41  	EXIT_TABLE_EXISTS                = 104
    42  	EXIT_TABLE_EXISTS_MYSQL          = 105
    43  	EXIT_COLUMN_EXISTS               = 106
    44  	EXIT_DOES_COLUMN_EXISTS_POSTGRES = 107
    45  	EXIT_DOES_COLUMN_EXISTS_MYSQL    = 108
    46  	EXIT_DOES_COLUMN_EXISTS_MISSING  = 109
    47  	EXIT_CREATE_COLUMN_POSTGRES      = 110
    48  	EXIT_CREATE_COLUMN_MYSQL         = 111
    49  	EXIT_CREATE_COLUMN_MISSING       = 112
    50  	EXIT_REMOVE_COLUMN               = 113
    51  	EXIT_RENAME_COLUMN               = 114
    52  	EXIT_MAX_COLUMN                  = 115
    53  	EXIT_ALTER_COLUMN                = 116
    54  	EXIT_CREATE_INDEX_POSTGRES       = 117
    55  	EXIT_CREATE_INDEX_MYSQL          = 118
    56  	EXIT_CREATE_INDEX_FULL_MYSQL     = 119
    57  	EXIT_CREATE_INDEX_MISSING        = 120
    58  	EXIT_REMOVE_INDEX_POSTGRES       = 121
    59  	EXIT_REMOVE_INDEX_MYSQL          = 122
    60  	EXIT_REMOVE_INDEX_MISSING        = 123
    61  	EXIT_REMOVE_TABLE                = 134
    62  )
    63  
    64  type SqlSupplierOldStores struct {
    65  	team                 store.TeamStore
    66  	channel              store.ChannelStore
    67  	post                 store.PostStore
    68  	user                 store.UserStore
    69  	audit                store.AuditStore
    70  	cluster              store.ClusterDiscoveryStore
    71  	compliance           store.ComplianceStore
    72  	session              store.SessionStore
    73  	oauth                store.OAuthStore
    74  	system               store.SystemStore
    75  	webhook              store.WebhookStore
    76  	command              store.CommandStore
    77  	commandWebhook       store.CommandWebhookStore
    78  	preference           store.PreferenceStore
    79  	license              store.LicenseStore
    80  	token                store.TokenStore
    81  	emoji                store.EmojiStore
    82  	status               store.StatusStore
    83  	fileInfo             store.FileInfoStore
    84  	reaction             store.ReactionStore
    85  	job                  store.JobStore
    86  	userAccessToken      store.UserAccessTokenStore
    87  	plugin               store.PluginStore
    88  	channelMemberHistory store.ChannelMemberHistoryStore
    89  }
    90  
    91  type SqlSupplier struct {
    92  	// rrCounter and srCounter should be kept first.
    93  	// See https://github.com/mattermost/mattermost-server/pull/7281
    94  	rrCounter      int64
    95  	srCounter      int64
    96  	next           store.LayeredStoreSupplier
    97  	master         *gorp.DbMap
    98  	replicas       []*gorp.DbMap
    99  	searchReplicas []*gorp.DbMap
   100  	oldStores      SqlSupplierOldStores
   101  	settings       *model.SqlSettings
   102  }
   103  
   104  func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlSupplier {
   105  	supplier := &SqlSupplier{
   106  		rrCounter: 0,
   107  		srCounter: 0,
   108  		settings:  &settings,
   109  	}
   110  
   111  	supplier.initConnection()
   112  
   113  	supplier.oldStores.team = NewSqlTeamStore(supplier)
   114  	supplier.oldStores.channel = NewSqlChannelStore(supplier, metrics)
   115  	supplier.oldStores.post = NewSqlPostStore(supplier, metrics)
   116  	supplier.oldStores.user = NewSqlUserStore(supplier, metrics)
   117  	supplier.oldStores.audit = NewSqlAuditStore(supplier)
   118  	supplier.oldStores.cluster = NewSqlClusterDiscoveryStore(supplier)
   119  	supplier.oldStores.compliance = NewSqlComplianceStore(supplier)
   120  	supplier.oldStores.session = NewSqlSessionStore(supplier)
   121  	supplier.oldStores.oauth = NewSqlOAuthStore(supplier)
   122  	supplier.oldStores.system = NewSqlSystemStore(supplier)
   123  	supplier.oldStores.webhook = NewSqlWebhookStore(supplier, metrics)
   124  	supplier.oldStores.command = NewSqlCommandStore(supplier)
   125  	supplier.oldStores.commandWebhook = NewSqlCommandWebhookStore(supplier)
   126  	supplier.oldStores.preference = NewSqlPreferenceStore(supplier)
   127  	supplier.oldStores.license = NewSqlLicenseStore(supplier)
   128  	supplier.oldStores.token = NewSqlTokenStore(supplier)
   129  	supplier.oldStores.emoji = NewSqlEmojiStore(supplier, metrics)
   130  	supplier.oldStores.status = NewSqlStatusStore(supplier)
   131  	supplier.oldStores.fileInfo = NewSqlFileInfoStore(supplier, metrics)
   132  	supplier.oldStores.job = NewSqlJobStore(supplier)
   133  	supplier.oldStores.userAccessToken = NewSqlUserAccessTokenStore(supplier)
   134  	supplier.oldStores.channelMemberHistory = NewSqlChannelMemberHistoryStore(supplier)
   135  	supplier.oldStores.plugin = NewSqlPluginStore(supplier)
   136  
   137  	initSqlSupplierReactions(supplier)
   138  
   139  	err := supplier.GetMaster().CreateTablesIfNotExists()
   140  	if err != nil {
   141  		l4g.Critical(utils.T("store.sql.creating_tables.critical"), err)
   142  		time.Sleep(time.Second)
   143  		os.Exit(EXIT_CREATE_TABLE)
   144  	}
   145  
   146  	UpgradeDatabase(supplier)
   147  
   148  	supplier.oldStores.team.(*SqlTeamStore).CreateIndexesIfNotExists()
   149  	supplier.oldStores.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
   150  	supplier.oldStores.post.(*SqlPostStore).CreateIndexesIfNotExists()
   151  	supplier.oldStores.user.(*SqlUserStore).CreateIndexesIfNotExists()
   152  	supplier.oldStores.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
   153  	supplier.oldStores.compliance.(*SqlComplianceStore).CreateIndexesIfNotExists()
   154  	supplier.oldStores.session.(*SqlSessionStore).CreateIndexesIfNotExists()
   155  	supplier.oldStores.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
   156  	supplier.oldStores.system.(*SqlSystemStore).CreateIndexesIfNotExists()
   157  	supplier.oldStores.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
   158  	supplier.oldStores.command.(*SqlCommandStore).CreateIndexesIfNotExists()
   159  	supplier.oldStores.commandWebhook.(*SqlCommandWebhookStore).CreateIndexesIfNotExists()
   160  	supplier.oldStores.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
   161  	supplier.oldStores.license.(*SqlLicenseStore).CreateIndexesIfNotExists()
   162  	supplier.oldStores.token.(*SqlTokenStore).CreateIndexesIfNotExists()
   163  	supplier.oldStores.emoji.(*SqlEmojiStore).CreateIndexesIfNotExists()
   164  	supplier.oldStores.status.(*SqlStatusStore).CreateIndexesIfNotExists()
   165  	supplier.oldStores.fileInfo.(*SqlFileInfoStore).CreateIndexesIfNotExists()
   166  	supplier.oldStores.job.(*SqlJobStore).CreateIndexesIfNotExists()
   167  	supplier.oldStores.userAccessToken.(*SqlUserAccessTokenStore).CreateIndexesIfNotExists()
   168  	supplier.oldStores.plugin.(*SqlPluginStore).CreateIndexesIfNotExists()
   169  
   170  	supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
   171  
   172  	return supplier
   173  }
   174  
   175  func (s *SqlSupplier) SetChainNext(next store.LayeredStoreSupplier) {
   176  	s.next = next
   177  }
   178  
   179  func (s *SqlSupplier) Next() store.LayeredStoreSupplier {
   180  	return s.next
   181  }
   182  
   183  func setupConnection(con_type string, dataSource string, settings *model.SqlSettings) *gorp.DbMap {
   184  	db, err := dbsql.Open(*settings.DriverName, dataSource)
   185  	if err != nil {
   186  		l4g.Critical(utils.T("store.sql.open_conn.critical"), err)
   187  		time.Sleep(time.Second)
   188  		os.Exit(EXIT_DB_OPEN)
   189  	}
   190  
   191  	for i := 0; i < DB_PING_ATTEMPTS; i++ {
   192  		l4g.Info("Pinging SQL %v database", con_type)
   193  		ctx, cancel := context.WithTimeout(context.Background(), DB_PING_TIMEOUT_SECS*time.Second)
   194  		defer cancel()
   195  		err = db.PingContext(ctx)
   196  		if err == nil {
   197  			break
   198  		} else {
   199  			if i == DB_PING_ATTEMPTS-1 {
   200  				l4g.Critical("Failed to ping DB, server will exit err=%v", err)
   201  				time.Sleep(time.Second)
   202  				os.Exit(EXIT_PING)
   203  			} else {
   204  				l4g.Error("Failed to ping DB retrying in %v seconds err=%v", DB_PING_TIMEOUT_SECS, err)
   205  				time.Sleep(DB_PING_TIMEOUT_SECS * time.Second)
   206  			}
   207  		}
   208  	}
   209  
   210  	db.SetMaxIdleConns(*settings.MaxIdleConns)
   211  	db.SetMaxOpenConns(*settings.MaxOpenConns)
   212  	db.SetConnMaxLifetime(time.Duration(MAX_DB_CONN_LIFETIME) * time.Minute)
   213  
   214  	var dbmap *gorp.DbMap
   215  
   216  	connectionTimeout := time.Duration(*settings.QueryTimeout) * time.Second
   217  
   218  	if *settings.DriverName == "sqlite3" {
   219  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}, QueryTimeout: connectionTimeout}
   220  	} else if *settings.DriverName == model.DATABASE_DRIVER_MYSQL {
   221  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}, QueryTimeout: connectionTimeout}
   222  	} else if *settings.DriverName == model.DATABASE_DRIVER_POSTGRES {
   223  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}, QueryTimeout: connectionTimeout}
   224  	} else {
   225  		l4g.Critical(utils.T("store.sql.dialect_driver.critical"))
   226  		time.Sleep(time.Second)
   227  		os.Exit(EXIT_NO_DRIVER)
   228  	}
   229  
   230  	if settings.Trace {
   231  		dbmap.TraceOn("", sqltrace.New(os.Stdout, "sql-trace:", sqltrace.Lmicroseconds))
   232  	}
   233  
   234  	return dbmap
   235  }
   236  
   237  func (s *SqlSupplier) initConnection() {
   238  	s.master = setupConnection("master", *s.settings.DataSource, s.settings)
   239  
   240  	if len(s.settings.DataSourceReplicas) == 0 {
   241  		s.replicas = make([]*gorp.DbMap, 1)
   242  		s.replicas[0] = s.master
   243  	} else {
   244  		s.replicas = make([]*gorp.DbMap, len(s.settings.DataSourceReplicas))
   245  		for i, replica := range s.settings.DataSourceReplicas {
   246  			s.replicas[i] = setupConnection(fmt.Sprintf("replica-%v", i), replica, s.settings)
   247  		}
   248  	}
   249  
   250  	if len(s.settings.DataSourceSearchReplicas) == 0 {
   251  		s.searchReplicas = s.replicas
   252  	} else {
   253  		s.searchReplicas = make([]*gorp.DbMap, len(s.settings.DataSourceSearchReplicas))
   254  		for i, replica := range s.settings.DataSourceSearchReplicas {
   255  			s.searchReplicas[i] = setupConnection(fmt.Sprintf("search-replica-%v", i), replica, s.settings)
   256  		}
   257  	}
   258  }
   259  
   260  func (ss *SqlSupplier) DriverName() string {
   261  	return *ss.settings.DriverName
   262  }
   263  
   264  func (ss *SqlSupplier) GetCurrentSchemaVersion() string {
   265  	version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'")
   266  	return version
   267  }
   268  
   269  func (ss *SqlSupplier) GetMaster() *gorp.DbMap {
   270  	return ss.master
   271  }
   272  
   273  func (ss *SqlSupplier) GetSearchReplica() *gorp.DbMap {
   274  	rrNum := atomic.AddInt64(&ss.srCounter, 1) % int64(len(ss.searchReplicas))
   275  	return ss.searchReplicas[rrNum]
   276  }
   277  
   278  func (ss *SqlSupplier) GetReplica() *gorp.DbMap {
   279  	rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.replicas))
   280  	return ss.replicas[rrNum]
   281  }
   282  
   283  func (ss *SqlSupplier) TotalMasterDbConnections() int {
   284  	return ss.GetMaster().Db.Stats().OpenConnections
   285  }
   286  
   287  func (ss *SqlSupplier) TotalReadDbConnections() int {
   288  
   289  	if len(ss.settings.DataSourceReplicas) == 0 {
   290  		return 0
   291  	}
   292  
   293  	count := 0
   294  	for _, db := range ss.replicas {
   295  		count = count + db.Db.Stats().OpenConnections
   296  	}
   297  
   298  	return count
   299  }
   300  
   301  func (ss *SqlSupplier) TotalSearchDbConnections() int {
   302  	if len(ss.settings.DataSourceSearchReplicas) == 0 {
   303  		return 0
   304  	}
   305  
   306  	count := 0
   307  	for _, db := range ss.searchReplicas {
   308  		count = count + db.Db.Stats().OpenConnections
   309  	}
   310  
   311  	return count
   312  }
   313  
   314  func (ss *SqlSupplier) MarkSystemRanUnitTests() {
   315  	if result := <-ss.System().Get(); result.Err == nil {
   316  		props := result.Data.(model.StringMap)
   317  		unitTests := props[model.SYSTEM_RAN_UNIT_TESTS]
   318  		if len(unitTests) == 0 {
   319  			systemTests := &model.System{Name: model.SYSTEM_RAN_UNIT_TESTS, Value: "1"}
   320  			<-ss.System().Save(systemTests)
   321  		}
   322  	}
   323  }
   324  
   325  func (ss *SqlSupplier) DoesTableExist(tableName string) bool {
   326  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   327  		count, err := ss.GetMaster().SelectInt(
   328  			`SELECT count(relname) FROM pg_class WHERE relname=$1`,
   329  			strings.ToLower(tableName),
   330  		)
   331  
   332  		if err != nil {
   333  			l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
   334  			time.Sleep(time.Second)
   335  			os.Exit(EXIT_TABLE_EXISTS)
   336  		}
   337  
   338  		return count > 0
   339  
   340  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   341  
   342  		count, err := ss.GetMaster().SelectInt(
   343  			`SELECT
   344  		    COUNT(0) AS table_exists
   345  			FROM
   346  			    information_schema.TABLES
   347  			WHERE
   348  			    TABLE_SCHEMA = DATABASE()
   349  			        AND TABLE_NAME = ?
   350  		    `,
   351  			tableName,
   352  		)
   353  
   354  		if err != nil {
   355  			l4g.Critical(utils.T("store.sql.table_exists.critical"), err)
   356  			time.Sleep(time.Second)
   357  			os.Exit(EXIT_TABLE_EXISTS_MYSQL)
   358  		}
   359  
   360  		return count > 0
   361  
   362  	} else {
   363  		l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
   364  		time.Sleep(time.Second)
   365  		os.Exit(EXIT_COLUMN_EXISTS)
   366  		return false
   367  	}
   368  }
   369  
   370  func (ss *SqlSupplier) DoesColumnExist(tableName string, columnName string) bool {
   371  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   372  		count, err := ss.GetMaster().SelectInt(
   373  			`SELECT COUNT(0)
   374  			FROM   pg_attribute
   375  			WHERE  attrelid = $1::regclass
   376  			AND    attname = $2
   377  			AND    NOT attisdropped`,
   378  			strings.ToLower(tableName),
   379  			strings.ToLower(columnName),
   380  		)
   381  
   382  		if err != nil {
   383  			if err.Error() == "pq: relation \""+strings.ToLower(tableName)+"\" does not exist" {
   384  				return false
   385  			}
   386  
   387  			l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
   388  			time.Sleep(time.Second)
   389  			os.Exit(EXIT_DOES_COLUMN_EXISTS_POSTGRES)
   390  		}
   391  
   392  		return count > 0
   393  
   394  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   395  
   396  		count, err := ss.GetMaster().SelectInt(
   397  			`SELECT
   398  		    COUNT(0) AS column_exists
   399  		FROM
   400  		    information_schema.COLUMNS
   401  		WHERE
   402  		    TABLE_SCHEMA = DATABASE()
   403  		        AND TABLE_NAME = ?
   404  		        AND COLUMN_NAME = ?`,
   405  			tableName,
   406  			columnName,
   407  		)
   408  
   409  		if err != nil {
   410  			l4g.Critical(utils.T("store.sql.column_exists.critical"), err)
   411  			time.Sleep(time.Second)
   412  			os.Exit(EXIT_DOES_COLUMN_EXISTS_MYSQL)
   413  		}
   414  
   415  		return count > 0
   416  
   417  	} else {
   418  		l4g.Critical(utils.T("store.sql.column_exists_missing_driver.critical"))
   419  		time.Sleep(time.Second)
   420  		os.Exit(EXIT_DOES_COLUMN_EXISTS_MISSING)
   421  		return false
   422  	}
   423  }
   424  
   425  func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool {
   426  
   427  	if ss.DoesColumnExist(tableName, columnName) {
   428  		return false
   429  	}
   430  
   431  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   432  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'")
   433  		if err != nil {
   434  			l4g.Critical(utils.T("store.sql.create_column.critical"), err)
   435  			time.Sleep(time.Second)
   436  			os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
   437  		}
   438  
   439  		return true
   440  
   441  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   442  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'")
   443  		if err != nil {
   444  			l4g.Critical(utils.T("store.sql.create_column.critical"), err)
   445  			time.Sleep(time.Second)
   446  			os.Exit(EXIT_CREATE_COLUMN_MYSQL)
   447  		}
   448  
   449  		return true
   450  
   451  	} else {
   452  		l4g.Critical(utils.T("store.sql.create_column_missing_driver.critical"))
   453  		time.Sleep(time.Second)
   454  		os.Exit(EXIT_CREATE_COLUMN_MISSING)
   455  		return false
   456  	}
   457  }
   458  
   459  func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string) bool {
   460  
   461  	if !ss.DoesColumnExist(tableName, columnName) {
   462  		return false
   463  	}
   464  
   465  	_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
   466  	if err != nil {
   467  		l4g.Critical("Failed to drop column %v", err)
   468  		time.Sleep(time.Second)
   469  		os.Exit(EXIT_REMOVE_COLUMN)
   470  	}
   471  
   472  	return true
   473  }
   474  
   475  func (ss *SqlSupplier) RemoveTableIfExists(tableName string) bool {
   476  	if !ss.DoesTableExist(tableName) {
   477  		return false
   478  	}
   479  
   480  	_, err := ss.GetMaster().ExecNoTimeout("DROP TABLE " + tableName)
   481  	if err != nil {
   482  		l4g.Critical("Failed to drop table %v", err)
   483  		time.Sleep(time.Second)
   484  		os.Exit(EXIT_REMOVE_TABLE)
   485  	}
   486  
   487  	return true
   488  }
   489  
   490  func (ss *SqlSupplier) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
   491  	if !ss.DoesColumnExist(tableName, oldColumnName) {
   492  		return false
   493  	}
   494  
   495  	var err error
   496  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   497  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
   498  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   499  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName)
   500  	}
   501  
   502  	if err != nil {
   503  		l4g.Critical(utils.T("store.sql.rename_column.critical"), err)
   504  		time.Sleep(time.Second)
   505  		os.Exit(EXIT_RENAME_COLUMN)
   506  	}
   507  
   508  	return true
   509  }
   510  
   511  func (ss *SqlSupplier) GetMaxLengthOfColumnIfExists(tableName string, columnName string) string {
   512  	if !ss.DoesColumnExist(tableName, columnName) {
   513  		return ""
   514  	}
   515  
   516  	var result string
   517  	var err error
   518  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   519  		result, err = ss.GetMaster().SelectStr("SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE table_name = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "'")
   520  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   521  		result, err = ss.GetMaster().SelectStr("SELECT character_maximum_length FROM information_schema.columns WHERE table_name = '" + strings.ToLower(tableName) + "' AND column_name = '" + strings.ToLower(columnName) + "'")
   522  	}
   523  
   524  	if err != nil {
   525  		l4g.Critical(utils.T("store.sql.maxlength_column.critical"), err)
   526  		time.Sleep(time.Second)
   527  		os.Exit(EXIT_MAX_COLUMN)
   528  	}
   529  
   530  	return result
   531  }
   532  
   533  func (ss *SqlSupplier) AlterColumnTypeIfExists(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
   534  	if !ss.DoesColumnExist(tableName, columnName) {
   535  		return false
   536  	}
   537  
   538  	var err error
   539  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   540  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " MODIFY " + columnName + " " + mySqlColType)
   541  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   542  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + strings.ToLower(tableName) + " ALTER COLUMN " + strings.ToLower(columnName) + " TYPE " + postgresColType)
   543  	}
   544  
   545  	if err != nil {
   546  		l4g.Critical(utils.T("store.sql.alter_column_type.critical"), err)
   547  		time.Sleep(time.Second)
   548  		os.Exit(EXIT_ALTER_COLUMN)
   549  	}
   550  
   551  	return true
   552  }
   553  
   554  func (ss *SqlSupplier) CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   555  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_DEFAULT, true)
   556  }
   557  
   558  func (ss *SqlSupplier) CreateIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   559  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_DEFAULT, false)
   560  }
   561  
   562  func (ss *SqlSupplier) CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool {
   563  	return ss.createIndexIfNotExists(indexName, tableName, columnNames, INDEX_TYPE_DEFAULT, false)
   564  }
   565  
   566  func (ss *SqlSupplier) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   567  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_FULL_TEXT, false)
   568  }
   569  
   570  func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string, columnNames []string, indexType string, unique bool) bool {
   571  
   572  	uniqueStr := ""
   573  	if unique {
   574  		uniqueStr = "UNIQUE "
   575  	}
   576  
   577  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   578  		_, errExists := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
   579  		// It should fail if the index does not exist
   580  		if errExists == nil {
   581  			return false
   582  		}
   583  
   584  		query := ""
   585  		if indexType == INDEX_TYPE_FULL_TEXT {
   586  			if len(columnNames) != 1 {
   587  				l4g.Critical("Unable to create multi column full text index")
   588  				os.Exit(EXIT_CREATE_INDEX_POSTGRES)
   589  			}
   590  			columnName := columnNames[0]
   591  			postgresColumnNames := convertMySQLFullTextColumnsToPostgres(columnName)
   592  			query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + postgresColumnNames + "))"
   593  		} else {
   594  			query = "CREATE " + uniqueStr + "INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")"
   595  		}
   596  
   597  		_, err := ss.GetMaster().ExecNoTimeout(query)
   598  		if err != nil {
   599  			l4g.Critical(utils.T("store.sql.create_index.critical"), errExists)
   600  			l4g.Critical(utils.T("store.sql.create_index.critical"), err)
   601  			time.Sleep(time.Second)
   602  			os.Exit(EXIT_CREATE_INDEX_POSTGRES)
   603  		}
   604  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   605  
   606  		count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
   607  		if err != nil {
   608  			l4g.Critical(utils.T("store.sql.check_index.critical"), err)
   609  			time.Sleep(time.Second)
   610  			os.Exit(EXIT_CREATE_INDEX_MYSQL)
   611  		}
   612  
   613  		if count > 0 {
   614  			return false
   615  		}
   616  
   617  		fullTextIndex := ""
   618  		if indexType == INDEX_TYPE_FULL_TEXT {
   619  			fullTextIndex = " FULLTEXT "
   620  		}
   621  
   622  		_, err = ss.GetMaster().ExecNoTimeout("CREATE  " + uniqueStr + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")")
   623  		if err != nil {
   624  			l4g.Critical(utils.T("store.sql.create_index.critical"), err)
   625  			time.Sleep(time.Second)
   626  			os.Exit(EXIT_CREATE_INDEX_FULL_MYSQL)
   627  		}
   628  	} else {
   629  		l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
   630  		time.Sleep(time.Second)
   631  		os.Exit(EXIT_CREATE_INDEX_MISSING)
   632  	}
   633  
   634  	return true
   635  }
   636  
   637  func (ss *SqlSupplier) RemoveIndexIfExists(indexName string, tableName string) bool {
   638  
   639  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   640  		_, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
   641  		// It should fail if the index does not exist
   642  		if err != nil {
   643  			return false
   644  		}
   645  
   646  		_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName)
   647  		if err != nil {
   648  			l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
   649  			time.Sleep(time.Second)
   650  			os.Exit(EXIT_REMOVE_INDEX_POSTGRES)
   651  		}
   652  
   653  		return true
   654  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   655  
   656  		count, err := ss.GetMaster().SelectInt("SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?", tableName, indexName)
   657  		if err != nil {
   658  			l4g.Critical(utils.T("store.sql.check_index.critical"), err)
   659  			time.Sleep(time.Second)
   660  			os.Exit(EXIT_REMOVE_INDEX_MYSQL)
   661  		}
   662  
   663  		if count <= 0 {
   664  			return false
   665  		}
   666  
   667  		_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName + " ON " + tableName)
   668  		if err != nil {
   669  			l4g.Critical(utils.T("store.sql.remove_index.critical"), err)
   670  			time.Sleep(time.Second)
   671  			os.Exit(EXIT_REMOVE_INDEX_MYSQL)
   672  		}
   673  	} else {
   674  		l4g.Critical(utils.T("store.sql.create_index_missing_driver.critical"))
   675  		time.Sleep(time.Second)
   676  		os.Exit(EXIT_REMOVE_INDEX_MISSING)
   677  	}
   678  
   679  	return true
   680  }
   681  
   682  func IsUniqueConstraintError(err error, indexName []string) bool {
   683  	unique := false
   684  	if pqErr, ok := err.(*pq.Error); ok && pqErr.Code == "23505" {
   685  		unique = true
   686  	}
   687  
   688  	if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1062 {
   689  		unique = true
   690  	}
   691  
   692  	field := false
   693  	for _, contain := range indexName {
   694  		if strings.Contains(err.Error(), contain) {
   695  			field = true
   696  			break
   697  		}
   698  	}
   699  
   700  	return unique && field
   701  }
   702  
   703  func (ss *SqlSupplier) GetAllConns() []*gorp.DbMap {
   704  	all := make([]*gorp.DbMap, len(ss.replicas)+1)
   705  	copy(all, ss.replicas)
   706  	all[len(ss.replicas)] = ss.master
   707  	return all
   708  }
   709  
   710  func (ss *SqlSupplier) Close() {
   711  	l4g.Info(utils.T("store.sql.closing.info"))
   712  	ss.master.Db.Close()
   713  	for _, replica := range ss.replicas {
   714  		replica.Db.Close()
   715  	}
   716  }
   717  
   718  func (ss *SqlSupplier) Team() store.TeamStore {
   719  	return ss.oldStores.team
   720  }
   721  
   722  func (ss *SqlSupplier) Channel() store.ChannelStore {
   723  	return ss.oldStores.channel
   724  }
   725  
   726  func (ss *SqlSupplier) Post() store.PostStore {
   727  	return ss.oldStores.post
   728  }
   729  
   730  func (ss *SqlSupplier) User() store.UserStore {
   731  	return ss.oldStores.user
   732  }
   733  
   734  func (ss *SqlSupplier) Session() store.SessionStore {
   735  	return ss.oldStores.session
   736  }
   737  
   738  func (ss *SqlSupplier) Audit() store.AuditStore {
   739  	return ss.oldStores.audit
   740  }
   741  
   742  func (ss *SqlSupplier) ClusterDiscovery() store.ClusterDiscoveryStore {
   743  	return ss.oldStores.cluster
   744  }
   745  
   746  func (ss *SqlSupplier) Compliance() store.ComplianceStore {
   747  	return ss.oldStores.compliance
   748  }
   749  
   750  func (ss *SqlSupplier) OAuth() store.OAuthStore {
   751  	return ss.oldStores.oauth
   752  }
   753  
   754  func (ss *SqlSupplier) System() store.SystemStore {
   755  	return ss.oldStores.system
   756  }
   757  
   758  func (ss *SqlSupplier) Webhook() store.WebhookStore {
   759  	return ss.oldStores.webhook
   760  }
   761  
   762  func (ss *SqlSupplier) Command() store.CommandStore {
   763  	return ss.oldStores.command
   764  }
   765  
   766  func (ss *SqlSupplier) CommandWebhook() store.CommandWebhookStore {
   767  	return ss.oldStores.commandWebhook
   768  }
   769  
   770  func (ss *SqlSupplier) Preference() store.PreferenceStore {
   771  	return ss.oldStores.preference
   772  }
   773  
   774  func (ss *SqlSupplier) License() store.LicenseStore {
   775  	return ss.oldStores.license
   776  }
   777  
   778  func (ss *SqlSupplier) Token() store.TokenStore {
   779  	return ss.oldStores.token
   780  }
   781  
   782  func (ss *SqlSupplier) Emoji() store.EmojiStore {
   783  	return ss.oldStores.emoji
   784  }
   785  
   786  func (ss *SqlSupplier) Status() store.StatusStore {
   787  	return ss.oldStores.status
   788  }
   789  
   790  func (ss *SqlSupplier) FileInfo() store.FileInfoStore {
   791  	return ss.oldStores.fileInfo
   792  }
   793  
   794  func (ss *SqlSupplier) Reaction() store.ReactionStore {
   795  	return ss.oldStores.reaction
   796  }
   797  
   798  func (ss *SqlSupplier) Job() store.JobStore {
   799  	return ss.oldStores.job
   800  }
   801  
   802  func (ss *SqlSupplier) UserAccessToken() store.UserAccessTokenStore {
   803  	return ss.oldStores.userAccessToken
   804  }
   805  
   806  func (ss *SqlSupplier) ChannelMemberHistory() store.ChannelMemberHistoryStore {
   807  	return ss.oldStores.channelMemberHistory
   808  }
   809  
   810  func (ss *SqlSupplier) Plugin() store.PluginStore {
   811  	return ss.oldStores.plugin
   812  }
   813  
   814  func (ss *SqlSupplier) DropAllTables() {
   815  	ss.master.TruncateTables()
   816  }
   817  
   818  type mattermConverter struct{}
   819  
   820  func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
   821  
   822  	switch t := val.(type) {
   823  	case model.StringMap:
   824  		return model.MapToJson(t), nil
   825  	case map[string]string:
   826  		return model.MapToJson(model.StringMap(t)), nil
   827  	case model.StringArray:
   828  		return model.ArrayToJson(t), nil
   829  	case model.StringInterface:
   830  		return model.StringInterfaceToJson(t), nil
   831  	case map[string]interface{}:
   832  		return model.StringInterfaceToJson(model.StringInterface(t)), nil
   833  	}
   834  
   835  	return val, nil
   836  }
   837  
   838  func (me mattermConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
   839  	switch target.(type) {
   840  	case *model.StringMap:
   841  		binder := func(holder, target interface{}) error {
   842  			s, ok := holder.(*string)
   843  			if !ok {
   844  				return errors.New(utils.T("store.sql.convert_string_map"))
   845  			}
   846  			b := []byte(*s)
   847  			return json.Unmarshal(b, target)
   848  		}
   849  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
   850  	case *map[string]string:
   851  		binder := func(holder, target interface{}) error {
   852  			s, ok := holder.(*string)
   853  			if !ok {
   854  				return errors.New(utils.T("store.sql.convert_string_map"))
   855  			}
   856  			b := []byte(*s)
   857  			return json.Unmarshal(b, target)
   858  		}
   859  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
   860  	case *model.StringArray:
   861  		binder := func(holder, target interface{}) error {
   862  			s, ok := holder.(*string)
   863  			if !ok {
   864  				return errors.New(utils.T("store.sql.convert_string_array"))
   865  			}
   866  			b := []byte(*s)
   867  			return json.Unmarshal(b, target)
   868  		}
   869  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
   870  	case *model.StringInterface:
   871  		binder := func(holder, target interface{}) error {
   872  			s, ok := holder.(*string)
   873  			if !ok {
   874  				return errors.New(utils.T("store.sql.convert_string_interface"))
   875  			}
   876  			b := []byte(*s)
   877  			return json.Unmarshal(b, target)
   878  		}
   879  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
   880  	case *map[string]interface{}:
   881  		binder := func(holder, target interface{}) error {
   882  			s, ok := holder.(*string)
   883  			if !ok {
   884  				return errors.New(utils.T("store.sql.convert_string_interface"))
   885  			}
   886  			b := []byte(*s)
   887  			return json.Unmarshal(b, target)
   888  		}
   889  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
   890  	}
   891  
   892  	return gorp.CustomScanner{}, false
   893  }
   894  
   895  func convertMySQLFullTextColumnsToPostgres(columnNames string) string {
   896  	columns := strings.Split(columnNames, ", ")
   897  	concatenatedColumnNames := ""
   898  	for i, c := range columns {
   899  		concatenatedColumnNames += c
   900  		if i < len(columns)-1 {
   901  			concatenatedColumnNames += " || ' ' || "
   902  		}
   903  	}
   904  
   905  	return concatenatedColumnNames
   906  }