github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/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  	sq "github.com/Masterminds/squirrel"
    19  	"github.com/dyatlov/go-opengraph/opengraph"
    20  	"github.com/go-sql-driver/mysql"
    21  	"github.com/lib/pq"
    22  	"github.com/mattermost/gorp"
    23  	"github.com/crspeller/mattermost-server/einterfaces"
    24  	"github.com/crspeller/mattermost-server/mlog"
    25  	"github.com/crspeller/mattermost-server/model"
    26  	"github.com/crspeller/mattermost-server/store"
    27  	"github.com/crspeller/mattermost-server/utils"
    28  )
    29  
    30  const (
    31  	INDEX_TYPE_FULL_TEXT = "full_text"
    32  	INDEX_TYPE_DEFAULT   = "default"
    33  	DB_PING_ATTEMPTS     = 18
    34  	DB_PING_TIMEOUT_SECS = 10
    35  )
    36  
    37  const (
    38  	EXIT_GENERIC_FAILURE             = 1
    39  	EXIT_CREATE_TABLE                = 100
    40  	EXIT_DB_OPEN                     = 101
    41  	EXIT_PING                        = 102
    42  	EXIT_NO_DRIVER                   = 103
    43  	EXIT_TABLE_EXISTS                = 104
    44  	EXIT_TABLE_EXISTS_MYSQL          = 105
    45  	EXIT_COLUMN_EXISTS               = 106
    46  	EXIT_DOES_COLUMN_EXISTS_POSTGRES = 107
    47  	EXIT_DOES_COLUMN_EXISTS_MYSQL    = 108
    48  	EXIT_DOES_COLUMN_EXISTS_MISSING  = 109
    49  	EXIT_CREATE_COLUMN_POSTGRES      = 110
    50  	EXIT_CREATE_COLUMN_MYSQL         = 111
    51  	EXIT_CREATE_COLUMN_MISSING       = 112
    52  	EXIT_REMOVE_COLUMN               = 113
    53  	EXIT_RENAME_COLUMN               = 114
    54  	EXIT_MAX_COLUMN                  = 115
    55  	EXIT_ALTER_COLUMN                = 116
    56  	EXIT_CREATE_INDEX_POSTGRES       = 117
    57  	EXIT_CREATE_INDEX_MYSQL          = 118
    58  	EXIT_CREATE_INDEX_FULL_MYSQL     = 119
    59  	EXIT_CREATE_INDEX_MISSING        = 120
    60  	EXIT_REMOVE_INDEX_POSTGRES       = 121
    61  	EXIT_REMOVE_INDEX_MYSQL          = 122
    62  	EXIT_REMOVE_INDEX_MISSING        = 123
    63  	EXIT_REMOVE_TABLE                = 134
    64  	EXIT_CREATE_INDEX_SQLITE         = 135
    65  	EXIT_REMOVE_INDEX_SQLITE         = 136
    66  	EXIT_TABLE_EXISTS_SQLITE         = 137
    67  	EXIT_DOES_COLUMN_EXISTS_SQLITE   = 138
    68  )
    69  
    70  type SqlSupplierOldStores struct {
    71  	team                 store.TeamStore
    72  	channel              store.ChannelStore
    73  	post                 store.PostStore
    74  	user                 store.UserStore
    75  	bot                  store.BotStore
    76  	audit                store.AuditStore
    77  	cluster              store.ClusterDiscoveryStore
    78  	compliance           store.ComplianceStore
    79  	session              store.SessionStore
    80  	oauth                store.OAuthStore
    81  	system               store.SystemStore
    82  	webhook              store.WebhookStore
    83  	command              store.CommandStore
    84  	commandWebhook       store.CommandWebhookStore
    85  	preference           store.PreferenceStore
    86  	license              store.LicenseStore
    87  	token                store.TokenStore
    88  	emoji                store.EmojiStore
    89  	status               store.StatusStore
    90  	fileInfo             store.FileInfoStore
    91  	reaction             store.ReactionStore
    92  	job                  store.JobStore
    93  	userAccessToken      store.UserAccessTokenStore
    94  	plugin               store.PluginStore
    95  	channelMemberHistory store.ChannelMemberHistoryStore
    96  	role                 store.RoleStore
    97  	scheme               store.SchemeStore
    98  	TermsOfService       store.TermsOfServiceStore
    99  	group                store.GroupStore
   100  	UserTermsOfService   store.UserTermsOfServiceStore
   101  	linkMetadata         store.LinkMetadataStore
   102  }
   103  
   104  type SqlSupplier struct {
   105  	// rrCounter and srCounter should be kept first.
   106  	// See https://github.com/crspeller/mattermost-server/pull/7281
   107  	rrCounter      int64
   108  	srCounter      int64
   109  	next           store.LayeredStoreSupplier
   110  	master         *gorp.DbMap
   111  	replicas       []*gorp.DbMap
   112  	searchReplicas []*gorp.DbMap
   113  	oldStores      SqlSupplierOldStores
   114  	settings       *model.SqlSettings
   115  	lockedToMaster bool
   116  }
   117  
   118  func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlSupplier {
   119  	supplier := &SqlSupplier{
   120  		rrCounter: 0,
   121  		srCounter: 0,
   122  		settings:  &settings,
   123  	}
   124  
   125  	supplier.initConnection()
   126  
   127  	supplier.oldStores.team = NewSqlTeamStore(supplier)
   128  	supplier.oldStores.channel = NewSqlChannelStore(supplier, metrics)
   129  	supplier.oldStores.post = NewSqlPostStore(supplier, metrics)
   130  	supplier.oldStores.user = NewSqlUserStore(supplier, metrics)
   131  	supplier.oldStores.bot = NewSqlBotStore(supplier, metrics)
   132  	supplier.oldStores.audit = NewSqlAuditStore(supplier)
   133  	supplier.oldStores.cluster = NewSqlClusterDiscoveryStore(supplier)
   134  	supplier.oldStores.compliance = NewSqlComplianceStore(supplier)
   135  	supplier.oldStores.session = NewSqlSessionStore(supplier)
   136  	supplier.oldStores.oauth = NewSqlOAuthStore(supplier)
   137  	supplier.oldStores.system = NewSqlSystemStore(supplier)
   138  	supplier.oldStores.webhook = NewSqlWebhookStore(supplier, metrics)
   139  	supplier.oldStores.command = NewSqlCommandStore(supplier)
   140  	supplier.oldStores.commandWebhook = NewSqlCommandWebhookStore(supplier)
   141  	supplier.oldStores.preference = NewSqlPreferenceStore(supplier)
   142  	supplier.oldStores.license = NewSqlLicenseStore(supplier)
   143  	supplier.oldStores.token = NewSqlTokenStore(supplier)
   144  	supplier.oldStores.emoji = NewSqlEmojiStore(supplier, metrics)
   145  	supplier.oldStores.status = NewSqlStatusStore(supplier)
   146  	supplier.oldStores.fileInfo = NewSqlFileInfoStore(supplier, metrics)
   147  	supplier.oldStores.job = NewSqlJobStore(supplier)
   148  	supplier.oldStores.userAccessToken = NewSqlUserAccessTokenStore(supplier)
   149  	supplier.oldStores.channelMemberHistory = NewSqlChannelMemberHistoryStore(supplier)
   150  	supplier.oldStores.plugin = NewSqlPluginStore(supplier)
   151  	supplier.oldStores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics)
   152  	supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier)
   153  	supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier)
   154  
   155  	initSqlSupplierReactions(supplier)
   156  	initSqlSupplierRoles(supplier)
   157  	initSqlSupplierSchemes(supplier)
   158  	initSqlSupplierGroups(supplier)
   159  
   160  	err := supplier.GetMaster().CreateTablesIfNotExists()
   161  	if err != nil {
   162  		mlog.Critical(fmt.Sprintf("Error creating database tables: %v", err))
   163  		time.Sleep(time.Second)
   164  		os.Exit(EXIT_CREATE_TABLE)
   165  	}
   166  
   167  	UpgradeDatabase(supplier)
   168  
   169  	supplier.oldStores.team.(*SqlTeamStore).CreateIndexesIfNotExists()
   170  	supplier.oldStores.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
   171  	supplier.oldStores.post.(*SqlPostStore).CreateIndexesIfNotExists()
   172  	supplier.oldStores.user.(*SqlUserStore).CreateIndexesIfNotExists()
   173  	supplier.oldStores.bot.(*SqlBotStore).CreateIndexesIfNotExists()
   174  	supplier.oldStores.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
   175  	supplier.oldStores.compliance.(*SqlComplianceStore).CreateIndexesIfNotExists()
   176  	supplier.oldStores.session.(*SqlSessionStore).CreateIndexesIfNotExists()
   177  	supplier.oldStores.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
   178  	supplier.oldStores.system.(*SqlSystemStore).CreateIndexesIfNotExists()
   179  	supplier.oldStores.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
   180  	supplier.oldStores.command.(*SqlCommandStore).CreateIndexesIfNotExists()
   181  	supplier.oldStores.commandWebhook.(*SqlCommandWebhookStore).CreateIndexesIfNotExists()
   182  	supplier.oldStores.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
   183  	supplier.oldStores.license.(*SqlLicenseStore).CreateIndexesIfNotExists()
   184  	supplier.oldStores.token.(*SqlTokenStore).CreateIndexesIfNotExists()
   185  	supplier.oldStores.emoji.(*SqlEmojiStore).CreateIndexesIfNotExists()
   186  	supplier.oldStores.status.(*SqlStatusStore).CreateIndexesIfNotExists()
   187  	supplier.oldStores.fileInfo.(*SqlFileInfoStore).CreateIndexesIfNotExists()
   188  	supplier.oldStores.job.(*SqlJobStore).CreateIndexesIfNotExists()
   189  	supplier.oldStores.userAccessToken.(*SqlUserAccessTokenStore).CreateIndexesIfNotExists()
   190  	supplier.oldStores.plugin.(*SqlPluginStore).CreateIndexesIfNotExists()
   191  	supplier.oldStores.TermsOfService.(SqlTermsOfServiceStore).CreateIndexesIfNotExists()
   192  	supplier.oldStores.UserTermsOfService.(SqlUserTermsOfServiceStore).CreateIndexesIfNotExists()
   193  	supplier.oldStores.linkMetadata.(*SqlLinkMetadataStore).CreateIndexesIfNotExists()
   194  
   195  	supplier.CreateIndexesIfNotExistsGroups()
   196  
   197  	supplier.oldStores.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
   198  
   199  	return supplier
   200  }
   201  
   202  func (s *SqlSupplier) SetChainNext(next store.LayeredStoreSupplier) {
   203  	s.next = next
   204  }
   205  
   206  func (s *SqlSupplier) Next() store.LayeredStoreSupplier {
   207  	return s.next
   208  }
   209  
   210  func setupConnection(con_type string, dataSource string, settings *model.SqlSettings) *gorp.DbMap {
   211  	db, err := dbsql.Open(*settings.DriverName, dataSource)
   212  	if err != nil {
   213  		mlog.Critical(fmt.Sprintf("Failed to open SQL connection to err:%v", err.Error()))
   214  		time.Sleep(time.Second)
   215  		os.Exit(EXIT_DB_OPEN)
   216  	}
   217  
   218  	for i := 0; i < DB_PING_ATTEMPTS; i++ {
   219  		mlog.Info(fmt.Sprintf("Pinging SQL %v database", con_type))
   220  		ctx, cancel := context.WithTimeout(context.Background(), DB_PING_TIMEOUT_SECS*time.Second)
   221  		defer cancel()
   222  		err = db.PingContext(ctx)
   223  		if err == nil {
   224  			break
   225  		} else {
   226  			if i == DB_PING_ATTEMPTS-1 {
   227  				mlog.Critical(fmt.Sprintf("Failed to ping DB, server will exit err=%v", err))
   228  				time.Sleep(time.Second)
   229  				os.Exit(EXIT_PING)
   230  			} else {
   231  				mlog.Error(fmt.Sprintf("Failed to ping DB retrying in %v seconds err=%v", DB_PING_TIMEOUT_SECS, err))
   232  				time.Sleep(DB_PING_TIMEOUT_SECS * time.Second)
   233  			}
   234  		}
   235  	}
   236  
   237  	db.SetMaxIdleConns(*settings.MaxIdleConns)
   238  	db.SetMaxOpenConns(*settings.MaxOpenConns)
   239  	db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond)
   240  
   241  	var dbmap *gorp.DbMap
   242  
   243  	connectionTimeout := time.Duration(*settings.QueryTimeout) * time.Second
   244  
   245  	if *settings.DriverName == model.DATABASE_DRIVER_SQLITE {
   246  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}, QueryTimeout: connectionTimeout}
   247  	} else if *settings.DriverName == model.DATABASE_DRIVER_MYSQL {
   248  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}, QueryTimeout: connectionTimeout}
   249  	} else if *settings.DriverName == model.DATABASE_DRIVER_POSTGRES {
   250  		dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.PostgresDialect{}, QueryTimeout: connectionTimeout}
   251  	} else {
   252  		mlog.Critical("Failed to create dialect specific driver")
   253  		time.Sleep(time.Second)
   254  		os.Exit(EXIT_NO_DRIVER)
   255  	}
   256  
   257  	if settings.Trace != nil && *settings.Trace {
   258  		dbmap.TraceOn("", sqltrace.New(os.Stdout, "sql-trace:", sqltrace.Lmicroseconds))
   259  	}
   260  
   261  	return dbmap
   262  }
   263  
   264  func (s *SqlSupplier) initConnection() {
   265  	s.master = setupConnection("master", *s.settings.DataSource, s.settings)
   266  
   267  	if len(s.settings.DataSourceReplicas) > 0 {
   268  		s.replicas = make([]*gorp.DbMap, len(s.settings.DataSourceReplicas))
   269  		for i, replica := range s.settings.DataSourceReplicas {
   270  			s.replicas[i] = setupConnection(fmt.Sprintf("replica-%v", i), replica, s.settings)
   271  		}
   272  	}
   273  
   274  	if len(s.settings.DataSourceSearchReplicas) > 0 {
   275  		s.searchReplicas = make([]*gorp.DbMap, len(s.settings.DataSourceSearchReplicas))
   276  		for i, replica := range s.settings.DataSourceSearchReplicas {
   277  			s.searchReplicas[i] = setupConnection(fmt.Sprintf("search-replica-%v", i), replica, s.settings)
   278  		}
   279  	}
   280  }
   281  
   282  func (ss *SqlSupplier) DriverName() string {
   283  	return *ss.settings.DriverName
   284  }
   285  
   286  func (ss *SqlSupplier) GetCurrentSchemaVersion() string {
   287  	version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'")
   288  	return version
   289  }
   290  
   291  func (ss *SqlSupplier) GetMaster() *gorp.DbMap {
   292  	return ss.master
   293  }
   294  
   295  func (ss *SqlSupplier) GetSearchReplica() *gorp.DbMap {
   296  	if len(ss.settings.DataSourceSearchReplicas) == 0 {
   297  		return ss.GetReplica()
   298  	}
   299  
   300  	rrNum := atomic.AddInt64(&ss.srCounter, 1) % int64(len(ss.searchReplicas))
   301  	return ss.searchReplicas[rrNum]
   302  }
   303  
   304  func (ss *SqlSupplier) GetReplica() *gorp.DbMap {
   305  	if len(ss.settings.DataSourceReplicas) == 0 || ss.lockedToMaster {
   306  		return ss.GetMaster()
   307  	}
   308  
   309  	rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.replicas))
   310  	return ss.replicas[rrNum]
   311  }
   312  
   313  func (ss *SqlSupplier) TotalMasterDbConnections() int {
   314  	return ss.GetMaster().Db.Stats().OpenConnections
   315  }
   316  
   317  func (ss *SqlSupplier) TotalReadDbConnections() int {
   318  	if len(ss.settings.DataSourceReplicas) == 0 {
   319  		return 0
   320  	}
   321  
   322  	count := 0
   323  	for _, db := range ss.replicas {
   324  		count = count + db.Db.Stats().OpenConnections
   325  	}
   326  
   327  	return count
   328  }
   329  
   330  func (ss *SqlSupplier) TotalSearchDbConnections() int {
   331  	if len(ss.settings.DataSourceSearchReplicas) == 0 {
   332  		return 0
   333  	}
   334  
   335  	count := 0
   336  	for _, db := range ss.searchReplicas {
   337  		count = count + db.Db.Stats().OpenConnections
   338  	}
   339  
   340  	return count
   341  }
   342  
   343  func (ss *SqlSupplier) MarkSystemRanUnitTests() {
   344  	if result := <-ss.System().Get(); result.Err == nil {
   345  		props := result.Data.(model.StringMap)
   346  		unitTests := props[model.SYSTEM_RAN_UNIT_TESTS]
   347  		if len(unitTests) == 0 {
   348  			systemTests := &model.System{Name: model.SYSTEM_RAN_UNIT_TESTS, Value: "1"}
   349  			<-ss.System().Save(systemTests)
   350  		}
   351  	}
   352  }
   353  
   354  func (ss *SqlSupplier) DoesTableExist(tableName string) bool {
   355  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   356  		count, err := ss.GetMaster().SelectInt(
   357  			`SELECT count(relname) FROM pg_class WHERE relname=$1`,
   358  			strings.ToLower(tableName),
   359  		)
   360  
   361  		if err != nil {
   362  			mlog.Critical(fmt.Sprintf("Failed to check if table exists %v", err))
   363  			time.Sleep(time.Second)
   364  			os.Exit(EXIT_TABLE_EXISTS)
   365  		}
   366  
   367  		return count > 0
   368  
   369  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   370  
   371  		count, err := ss.GetMaster().SelectInt(
   372  			`SELECT
   373  		    COUNT(0) AS table_exists
   374  			FROM
   375  			    information_schema.TABLES
   376  			WHERE
   377  			    TABLE_SCHEMA = DATABASE()
   378  			        AND TABLE_NAME = ?
   379  		    `,
   380  			tableName,
   381  		)
   382  
   383  		if err != nil {
   384  			mlog.Critical(fmt.Sprintf("Failed to check if table exists %v", err))
   385  			time.Sleep(time.Second)
   386  			os.Exit(EXIT_TABLE_EXISTS_MYSQL)
   387  		}
   388  
   389  		return count > 0
   390  
   391  	} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
   392  		count, err := ss.GetMaster().SelectInt(
   393  			`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
   394  			tableName,
   395  		)
   396  
   397  		if err != nil {
   398  			mlog.Critical(fmt.Sprintf("Failed to check if table exists %v", err))
   399  			time.Sleep(time.Second)
   400  			os.Exit(EXIT_TABLE_EXISTS_SQLITE)
   401  		}
   402  
   403  		return count > 0
   404  
   405  	} else {
   406  		mlog.Critical("Failed to check if column exists because of missing driver")
   407  		time.Sleep(time.Second)
   408  		os.Exit(EXIT_COLUMN_EXISTS)
   409  		return false
   410  	}
   411  }
   412  
   413  func (ss *SqlSupplier) DoesColumnExist(tableName string, columnName string) bool {
   414  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   415  		count, err := ss.GetMaster().SelectInt(
   416  			`SELECT COUNT(0)
   417  			FROM   pg_attribute
   418  			WHERE  attrelid = $1::regclass
   419  			AND    attname = $2
   420  			AND    NOT attisdropped`,
   421  			strings.ToLower(tableName),
   422  			strings.ToLower(columnName),
   423  		)
   424  
   425  		if err != nil {
   426  			if err.Error() == "pq: relation \""+strings.ToLower(tableName)+"\" does not exist" {
   427  				return false
   428  			}
   429  
   430  			mlog.Critical(fmt.Sprintf("Failed to check if column exists %v", err))
   431  			time.Sleep(time.Second)
   432  			os.Exit(EXIT_DOES_COLUMN_EXISTS_POSTGRES)
   433  		}
   434  
   435  		return count > 0
   436  
   437  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   438  
   439  		count, err := ss.GetMaster().SelectInt(
   440  			`SELECT
   441  		    COUNT(0) AS column_exists
   442  		FROM
   443  		    information_schema.COLUMNS
   444  		WHERE
   445  		    TABLE_SCHEMA = DATABASE()
   446  		        AND TABLE_NAME = ?
   447  		        AND COLUMN_NAME = ?`,
   448  			tableName,
   449  			columnName,
   450  		)
   451  
   452  		if err != nil {
   453  			mlog.Critical(fmt.Sprintf("Failed to check if column exists %v", err))
   454  			time.Sleep(time.Second)
   455  			os.Exit(EXIT_DOES_COLUMN_EXISTS_MYSQL)
   456  		}
   457  
   458  		return count > 0
   459  
   460  	} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
   461  		count, err := ss.GetMaster().SelectInt(
   462  			`SELECT COUNT(*) FROM pragma_table_info(?) WHERE name=?`,
   463  			tableName,
   464  			columnName,
   465  		)
   466  
   467  		if err != nil {
   468  			mlog.Critical(fmt.Sprintf("Failed to check if column exists %v", err))
   469  			time.Sleep(time.Second)
   470  			os.Exit(EXIT_DOES_COLUMN_EXISTS_SQLITE)
   471  		}
   472  
   473  		return count > 0
   474  
   475  	} else {
   476  		mlog.Critical("Failed to check if column exists because of missing driver")
   477  		time.Sleep(time.Second)
   478  		os.Exit(EXIT_DOES_COLUMN_EXISTS_MISSING)
   479  		return false
   480  	}
   481  }
   482  
   483  func (ss *SqlSupplier) DoesTriggerExist(triggerName string) bool {
   484  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   485  		count, err := ss.GetMaster().SelectInt(`
   486  			SELECT
   487  				COUNT(0)
   488  			FROM
   489  				pg_trigger
   490  			WHERE
   491  				tgname = $1
   492  		`, triggerName)
   493  
   494  		if err != nil {
   495  			mlog.Critical(fmt.Sprintf("Failed to check if trigger exists %v", err))
   496  			time.Sleep(time.Second)
   497  			os.Exit(EXIT_GENERIC_FAILURE)
   498  		}
   499  
   500  		return count > 0
   501  
   502  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   503  		count, err := ss.GetMaster().SelectInt(`
   504  			SELECT
   505  				COUNT(0)
   506  			FROM
   507  				information_schema.triggers
   508  			WHERE
   509  				trigger_schema = DATABASE()
   510  			AND	trigger_name = ?
   511  		`, triggerName)
   512  
   513  		if err != nil {
   514  			mlog.Critical(fmt.Sprintf("Failed to check if trigger exists %v", err))
   515  			time.Sleep(time.Second)
   516  			os.Exit(EXIT_GENERIC_FAILURE)
   517  		}
   518  
   519  		return count > 0
   520  
   521  	} else {
   522  		mlog.Critical("Failed to check if column exists because of missing driver")
   523  		time.Sleep(time.Second)
   524  		os.Exit(EXIT_GENERIC_FAILURE)
   525  		return false
   526  	}
   527  }
   528  
   529  func (ss *SqlSupplier) CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool {
   530  
   531  	if ss.DoesColumnExist(tableName, columnName) {
   532  		return false
   533  	}
   534  
   535  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   536  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'")
   537  		if err != nil {
   538  			mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
   539  			time.Sleep(time.Second)
   540  			os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
   541  		}
   542  
   543  		return true
   544  
   545  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   546  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'")
   547  		if err != nil {
   548  			mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
   549  			time.Sleep(time.Second)
   550  			os.Exit(EXIT_CREATE_COLUMN_MYSQL)
   551  		}
   552  
   553  		return true
   554  
   555  	} else {
   556  		mlog.Critical("Failed to create column because of missing driver")
   557  		time.Sleep(time.Second)
   558  		os.Exit(EXIT_CREATE_COLUMN_MISSING)
   559  		return false
   560  	}
   561  }
   562  
   563  func (ss *SqlSupplier) CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
   564  
   565  	if ss.DoesColumnExist(tableName, columnName) {
   566  		return false
   567  	}
   568  
   569  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   570  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType)
   571  		if err != nil {
   572  			mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
   573  			time.Sleep(time.Second)
   574  			os.Exit(EXIT_CREATE_COLUMN_POSTGRES)
   575  		}
   576  
   577  		return true
   578  
   579  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   580  		_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType)
   581  		if err != nil {
   582  			mlog.Critical(fmt.Sprintf("Failed to create column %v", err))
   583  			time.Sleep(time.Second)
   584  			os.Exit(EXIT_CREATE_COLUMN_MYSQL)
   585  		}
   586  
   587  		return true
   588  
   589  	} else {
   590  		mlog.Critical("Failed to create column because of missing driver")
   591  		time.Sleep(time.Second)
   592  		os.Exit(EXIT_CREATE_COLUMN_MISSING)
   593  		return false
   594  	}
   595  }
   596  
   597  func (ss *SqlSupplier) RemoveColumnIfExists(tableName string, columnName string) bool {
   598  
   599  	if !ss.DoesColumnExist(tableName, columnName) {
   600  		return false
   601  	}
   602  
   603  	_, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " DROP COLUMN " + columnName)
   604  	if err != nil {
   605  		mlog.Critical(fmt.Sprintf("Failed to drop column %v", err))
   606  		time.Sleep(time.Second)
   607  		os.Exit(EXIT_REMOVE_COLUMN)
   608  	}
   609  
   610  	return true
   611  }
   612  
   613  func (ss *SqlSupplier) RemoveTableIfExists(tableName string) bool {
   614  	if !ss.DoesTableExist(tableName) {
   615  		return false
   616  	}
   617  
   618  	_, err := ss.GetMaster().ExecNoTimeout("DROP TABLE " + tableName)
   619  	if err != nil {
   620  		mlog.Critical(fmt.Sprintf("Failed to drop table %v", err))
   621  		time.Sleep(time.Second)
   622  		os.Exit(EXIT_REMOVE_TABLE)
   623  	}
   624  
   625  	return true
   626  }
   627  
   628  func (ss *SqlSupplier) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool {
   629  	if !ss.DoesColumnExist(tableName, oldColumnName) {
   630  		return false
   631  	}
   632  
   633  	var err error
   634  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   635  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType)
   636  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   637  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName)
   638  	}
   639  
   640  	if err != nil {
   641  		mlog.Critical(fmt.Sprintf("Failed to rename column %v", err))
   642  		time.Sleep(time.Second)
   643  		os.Exit(EXIT_RENAME_COLUMN)
   644  	}
   645  
   646  	return true
   647  }
   648  
   649  func (ss *SqlSupplier) GetMaxLengthOfColumnIfExists(tableName string, columnName string) string {
   650  	if !ss.DoesColumnExist(tableName, columnName) {
   651  		return ""
   652  	}
   653  
   654  	var result string
   655  	var err error
   656  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   657  		result, err = ss.GetMaster().SelectStr("SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE table_name = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "'")
   658  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   659  		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) + "'")
   660  	}
   661  
   662  	if err != nil {
   663  		mlog.Critical(fmt.Sprintf("Failed to get max length of column %v", err))
   664  		time.Sleep(time.Second)
   665  		os.Exit(EXIT_MAX_COLUMN)
   666  	}
   667  
   668  	return result
   669  }
   670  
   671  func (ss *SqlSupplier) AlterColumnTypeIfExists(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
   672  	if !ss.DoesColumnExist(tableName, columnName) {
   673  		return false
   674  	}
   675  
   676  	var err error
   677  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   678  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " MODIFY " + columnName + " " + mySqlColType)
   679  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   680  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + strings.ToLower(tableName) + " ALTER COLUMN " + strings.ToLower(columnName) + " TYPE " + postgresColType)
   681  	}
   682  
   683  	if err != nil {
   684  		mlog.Critical(fmt.Sprintf("Failed to alter column type %v", err))
   685  		time.Sleep(time.Second)
   686  		os.Exit(EXIT_ALTER_COLUMN)
   687  	}
   688  
   689  	return true
   690  }
   691  
   692  func (ss *SqlSupplier) AlterColumnDefaultIfExists(tableName string, columnName string, mySqlColDefault *string, postgresColDefault *string) bool {
   693  	if !ss.DoesColumnExist(tableName, columnName) {
   694  		return false
   695  	}
   696  
   697  	var defaultValue = ""
   698  	if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   699  		// Some column types in MySQL cannot have defaults, so don't try to configure anything.
   700  		if mySqlColDefault == nil {
   701  			return true
   702  		}
   703  
   704  		defaultValue = *mySqlColDefault
   705  	} else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   706  		// Postgres doesn't have the same limitation, but preserve the interface.
   707  		if postgresColDefault == nil {
   708  			return true
   709  		}
   710  
   711  		tableName = strings.ToLower(tableName)
   712  		columnName = strings.ToLower(columnName)
   713  		defaultValue = *postgresColDefault
   714  	} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
   715  		// SQLite doesn't support altering column defaults, but we don't use this in
   716  		// production so just ignore.
   717  		return true
   718  	} else {
   719  		mlog.Critical("Failed to alter column default because of missing driver")
   720  		time.Sleep(time.Second)
   721  		os.Exit(EXIT_GENERIC_FAILURE)
   722  		return false
   723  	}
   724  
   725  	var err error
   726  	if defaultValue == "" {
   727  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " DROP DEFAULT")
   728  	} else {
   729  		_, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " SET DEFAULT " + defaultValue)
   730  	}
   731  
   732  	if err != nil {
   733  		mlog.Critical(fmt.Sprintf("Failed to alter column %s.%s default %s: %v", tableName, columnName, defaultValue, err))
   734  		time.Sleep(time.Second)
   735  		os.Exit(EXIT_GENERIC_FAILURE)
   736  		return false
   737  	}
   738  
   739  	return true
   740  }
   741  
   742  func (ss *SqlSupplier) CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   743  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_DEFAULT, true)
   744  }
   745  
   746  func (ss *SqlSupplier) CreateIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   747  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_DEFAULT, false)
   748  }
   749  
   750  func (ss *SqlSupplier) CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool {
   751  	return ss.createIndexIfNotExists(indexName, tableName, columnNames, INDEX_TYPE_DEFAULT, false)
   752  }
   753  
   754  func (ss *SqlSupplier) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool {
   755  	return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_FULL_TEXT, false)
   756  }
   757  
   758  func (ss *SqlSupplier) createIndexIfNotExists(indexName string, tableName string, columnNames []string, indexType string, unique bool) bool {
   759  
   760  	uniqueStr := ""
   761  	if unique {
   762  		uniqueStr = "UNIQUE "
   763  	}
   764  
   765  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   766  		_, errExists := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
   767  		// It should fail if the index does not exist
   768  		if errExists == nil {
   769  			return false
   770  		}
   771  
   772  		query := ""
   773  		if indexType == INDEX_TYPE_FULL_TEXT {
   774  			if len(columnNames) != 1 {
   775  				mlog.Critical("Unable to create multi column full text index")
   776  				os.Exit(EXIT_CREATE_INDEX_POSTGRES)
   777  			}
   778  			columnName := columnNames[0]
   779  			postgresColumnNames := convertMySQLFullTextColumnsToPostgres(columnName)
   780  			query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + postgresColumnNames + "))"
   781  		} else {
   782  			query = "CREATE " + uniqueStr + "INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")"
   783  		}
   784  
   785  		_, err := ss.GetMaster().ExecNoTimeout(query)
   786  		if err != nil {
   787  			mlog.Critical(fmt.Sprintf("Failed to create index %v, %v", errExists, err))
   788  			time.Sleep(time.Second)
   789  			os.Exit(EXIT_CREATE_INDEX_POSTGRES)
   790  		}
   791  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   792  
   793  		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)
   794  		if err != nil {
   795  			mlog.Critical(fmt.Sprintf("Failed to check index %v", err))
   796  			time.Sleep(time.Second)
   797  			os.Exit(EXIT_CREATE_INDEX_MYSQL)
   798  		}
   799  
   800  		if count > 0 {
   801  			return false
   802  		}
   803  
   804  		fullTextIndex := ""
   805  		if indexType == INDEX_TYPE_FULL_TEXT {
   806  			fullTextIndex = " FULLTEXT "
   807  		}
   808  
   809  		_, err = ss.GetMaster().ExecNoTimeout("CREATE  " + uniqueStr + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")")
   810  		if err != nil {
   811  			mlog.Critical(fmt.Sprintf("Failed to create index %v", err))
   812  			time.Sleep(time.Second)
   813  			os.Exit(EXIT_CREATE_INDEX_FULL_MYSQL)
   814  		}
   815  	} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
   816  		_, err := ss.GetMaster().ExecNoTimeout("CREATE INDEX IF NOT EXISTS " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")")
   817  		if err != nil {
   818  			mlog.Critical(fmt.Sprintf("Failed to create index %v", err))
   819  			time.Sleep(time.Second)
   820  			os.Exit(EXIT_CREATE_INDEX_SQLITE)
   821  		}
   822  	} else {
   823  		mlog.Critical("Failed to create index because of missing driver")
   824  		time.Sleep(time.Second)
   825  		os.Exit(EXIT_CREATE_INDEX_MISSING)
   826  	}
   827  
   828  	return true
   829  }
   830  
   831  func (ss *SqlSupplier) RemoveIndexIfExists(indexName string, tableName string) bool {
   832  
   833  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   834  		_, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
   835  		// It should fail if the index does not exist
   836  		if err != nil {
   837  			return false
   838  		}
   839  
   840  		_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName)
   841  		if err != nil {
   842  			mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
   843  			time.Sleep(time.Second)
   844  			os.Exit(EXIT_REMOVE_INDEX_POSTGRES)
   845  		}
   846  
   847  		return true
   848  	} else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL {
   849  
   850  		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)
   851  		if err != nil {
   852  			mlog.Critical(fmt.Sprintf("Failed to check index %v", err))
   853  			time.Sleep(time.Second)
   854  			os.Exit(EXIT_REMOVE_INDEX_MYSQL)
   855  		}
   856  
   857  		if count <= 0 {
   858  			return false
   859  		}
   860  
   861  		_, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName + " ON " + tableName)
   862  		if err != nil {
   863  			mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
   864  			time.Sleep(time.Second)
   865  			os.Exit(EXIT_REMOVE_INDEX_MYSQL)
   866  		}
   867  	} else if ss.DriverName() == model.DATABASE_DRIVER_SQLITE {
   868  		_, err := ss.GetMaster().ExecNoTimeout("DROP INDEX IF EXISTS " + indexName)
   869  		if err != nil {
   870  			mlog.Critical(fmt.Sprintf("Failed to remove index %v", err))
   871  			time.Sleep(time.Second)
   872  			os.Exit(EXIT_REMOVE_INDEX_SQLITE)
   873  		}
   874  	} else {
   875  		mlog.Critical("Failed to create index because of missing driver")
   876  		time.Sleep(time.Second)
   877  		os.Exit(EXIT_REMOVE_INDEX_MISSING)
   878  	}
   879  
   880  	return true
   881  }
   882  
   883  func IsUniqueConstraintError(err error, indexName []string) bool {
   884  	unique := false
   885  	if pqErr, ok := err.(*pq.Error); ok && pqErr.Code == "23505" {
   886  		unique = true
   887  	}
   888  
   889  	if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1062 {
   890  		unique = true
   891  	}
   892  
   893  	field := false
   894  	for _, contain := range indexName {
   895  		if strings.Contains(err.Error(), contain) {
   896  			field = true
   897  			break
   898  		}
   899  	}
   900  
   901  	return unique && field
   902  }
   903  
   904  func (ss *SqlSupplier) GetAllConns() []*gorp.DbMap {
   905  	all := make([]*gorp.DbMap, len(ss.replicas)+1)
   906  	copy(all, ss.replicas)
   907  	all[len(ss.replicas)] = ss.master
   908  	return all
   909  }
   910  
   911  func (ss *SqlSupplier) Close() {
   912  	mlog.Info("Closing SqlStore")
   913  	ss.master.Db.Close()
   914  	for _, replica := range ss.replicas {
   915  		replica.Db.Close()
   916  	}
   917  }
   918  
   919  func (ss *SqlSupplier) LockToMaster() {
   920  	ss.lockedToMaster = true
   921  }
   922  
   923  func (ss *SqlSupplier) UnlockFromMaster() {
   924  	ss.lockedToMaster = false
   925  }
   926  
   927  func (ss *SqlSupplier) Team() store.TeamStore {
   928  	return ss.oldStores.team
   929  }
   930  
   931  func (ss *SqlSupplier) Channel() store.ChannelStore {
   932  	return ss.oldStores.channel
   933  }
   934  
   935  func (ss *SqlSupplier) Post() store.PostStore {
   936  	return ss.oldStores.post
   937  }
   938  
   939  func (ss *SqlSupplier) User() store.UserStore {
   940  	return ss.oldStores.user
   941  }
   942  
   943  func (ss *SqlSupplier) Bot() store.BotStore {
   944  	return ss.oldStores.bot
   945  }
   946  
   947  func (ss *SqlSupplier) Session() store.SessionStore {
   948  	return ss.oldStores.session
   949  }
   950  
   951  func (ss *SqlSupplier) Audit() store.AuditStore {
   952  	return ss.oldStores.audit
   953  }
   954  
   955  func (ss *SqlSupplier) ClusterDiscovery() store.ClusterDiscoveryStore {
   956  	return ss.oldStores.cluster
   957  }
   958  
   959  func (ss *SqlSupplier) Compliance() store.ComplianceStore {
   960  	return ss.oldStores.compliance
   961  }
   962  
   963  func (ss *SqlSupplier) OAuth() store.OAuthStore {
   964  	return ss.oldStores.oauth
   965  }
   966  
   967  func (ss *SqlSupplier) System() store.SystemStore {
   968  	return ss.oldStores.system
   969  }
   970  
   971  func (ss *SqlSupplier) Webhook() store.WebhookStore {
   972  	return ss.oldStores.webhook
   973  }
   974  
   975  func (ss *SqlSupplier) Command() store.CommandStore {
   976  	return ss.oldStores.command
   977  }
   978  
   979  func (ss *SqlSupplier) CommandWebhook() store.CommandWebhookStore {
   980  	return ss.oldStores.commandWebhook
   981  }
   982  
   983  func (ss *SqlSupplier) Preference() store.PreferenceStore {
   984  	return ss.oldStores.preference
   985  }
   986  
   987  func (ss *SqlSupplier) License() store.LicenseStore {
   988  	return ss.oldStores.license
   989  }
   990  
   991  func (ss *SqlSupplier) Token() store.TokenStore {
   992  	return ss.oldStores.token
   993  }
   994  
   995  func (ss *SqlSupplier) Emoji() store.EmojiStore {
   996  	return ss.oldStores.emoji
   997  }
   998  
   999  func (ss *SqlSupplier) Status() store.StatusStore {
  1000  	return ss.oldStores.status
  1001  }
  1002  
  1003  func (ss *SqlSupplier) FileInfo() store.FileInfoStore {
  1004  	return ss.oldStores.fileInfo
  1005  }
  1006  
  1007  func (ss *SqlSupplier) Reaction() store.ReactionStore {
  1008  	return ss.oldStores.reaction
  1009  }
  1010  
  1011  func (ss *SqlSupplier) Job() store.JobStore {
  1012  	return ss.oldStores.job
  1013  }
  1014  
  1015  func (ss *SqlSupplier) UserAccessToken() store.UserAccessTokenStore {
  1016  	return ss.oldStores.userAccessToken
  1017  }
  1018  
  1019  func (ss *SqlSupplier) ChannelMemberHistory() store.ChannelMemberHistoryStore {
  1020  	return ss.oldStores.channelMemberHistory
  1021  }
  1022  
  1023  func (ss *SqlSupplier) Plugin() store.PluginStore {
  1024  	return ss.oldStores.plugin
  1025  }
  1026  
  1027  func (ss *SqlSupplier) Role() store.RoleStore {
  1028  	return ss.oldStores.role
  1029  }
  1030  
  1031  func (ss *SqlSupplier) TermsOfService() store.TermsOfServiceStore {
  1032  	return ss.oldStores.TermsOfService
  1033  }
  1034  
  1035  func (ss *SqlSupplier) UserTermsOfService() store.UserTermsOfServiceStore {
  1036  	return ss.oldStores.UserTermsOfService
  1037  }
  1038  
  1039  func (ss *SqlSupplier) Scheme() store.SchemeStore {
  1040  	return ss.oldStores.scheme
  1041  }
  1042  
  1043  func (ss *SqlSupplier) Group() store.GroupStore {
  1044  	return ss.oldStores.group
  1045  }
  1046  
  1047  func (ss *SqlSupplier) LinkMetadata() store.LinkMetadataStore {
  1048  	return ss.oldStores.linkMetadata
  1049  }
  1050  
  1051  func (ss *SqlSupplier) DropAllTables() {
  1052  	ss.master.TruncateTables()
  1053  }
  1054  
  1055  func (ss *SqlSupplier) getQueryBuilder() sq.StatementBuilderType {
  1056  	builder := sq.StatementBuilder.PlaceholderFormat(sq.Question)
  1057  	if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES {
  1058  		builder = builder.PlaceholderFormat(sq.Dollar)
  1059  	}
  1060  	return builder
  1061  }
  1062  
  1063  type mattermConverter struct{}
  1064  
  1065  func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {
  1066  
  1067  	switch t := val.(type) {
  1068  	case model.StringMap:
  1069  		return model.MapToJson(t), nil
  1070  	case map[string]string:
  1071  		return model.MapToJson(model.StringMap(t)), nil
  1072  	case model.StringArray:
  1073  		return model.ArrayToJson(t), nil
  1074  	case model.StringInterface:
  1075  		return model.StringInterfaceToJson(t), nil
  1076  	case map[string]interface{}:
  1077  		return model.StringInterfaceToJson(model.StringInterface(t)), nil
  1078  	case JSONSerializable:
  1079  		return t.ToJson(), nil
  1080  	case *opengraph.OpenGraph:
  1081  		return json.Marshal(t)
  1082  	}
  1083  
  1084  	return val, nil
  1085  }
  1086  
  1087  func (me mattermConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
  1088  	switch target.(type) {
  1089  	case *model.StringMap:
  1090  		binder := func(holder, target interface{}) error {
  1091  			s, ok := holder.(*string)
  1092  			if !ok {
  1093  				return errors.New(utils.T("store.sql.convert_string_map"))
  1094  			}
  1095  			b := []byte(*s)
  1096  			return json.Unmarshal(b, target)
  1097  		}
  1098  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
  1099  	case *map[string]string:
  1100  		binder := func(holder, target interface{}) error {
  1101  			s, ok := holder.(*string)
  1102  			if !ok {
  1103  				return errors.New(utils.T("store.sql.convert_string_map"))
  1104  			}
  1105  			b := []byte(*s)
  1106  			return json.Unmarshal(b, target)
  1107  		}
  1108  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
  1109  	case *model.StringArray:
  1110  		binder := func(holder, target interface{}) error {
  1111  			s, ok := holder.(*string)
  1112  			if !ok {
  1113  				return errors.New(utils.T("store.sql.convert_string_array"))
  1114  			}
  1115  			b := []byte(*s)
  1116  			return json.Unmarshal(b, target)
  1117  		}
  1118  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
  1119  	case *model.StringInterface:
  1120  		binder := func(holder, target interface{}) error {
  1121  			s, ok := holder.(*string)
  1122  			if !ok {
  1123  				return errors.New(utils.T("store.sql.convert_string_interface"))
  1124  			}
  1125  			b := []byte(*s)
  1126  			return json.Unmarshal(b, target)
  1127  		}
  1128  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
  1129  	case *map[string]interface{}:
  1130  		binder := func(holder, target interface{}) error {
  1131  			s, ok := holder.(*string)
  1132  			if !ok {
  1133  				return errors.New(utils.T("store.sql.convert_string_interface"))
  1134  			}
  1135  			b := []byte(*s)
  1136  			return json.Unmarshal(b, target)
  1137  		}
  1138  		return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true
  1139  	}
  1140  
  1141  	return gorp.CustomScanner{}, false
  1142  }
  1143  
  1144  type JSONSerializable interface {
  1145  	ToJson() string
  1146  }
  1147  
  1148  func convertMySQLFullTextColumnsToPostgres(columnNames string) string {
  1149  	columns := strings.Split(columnNames, ", ")
  1150  	concatenatedColumnNames := ""
  1151  	for i, c := range columns {
  1152  		concatenatedColumnNames += c
  1153  		if i < len(columns)-1 {
  1154  			concatenatedColumnNames += " || ' ' || "
  1155  		}
  1156  	}
  1157  
  1158  	return concatenatedColumnNames
  1159  }