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