github.com/mattermost/mattermost-server/v5@v5.39.3/store/sqlstore/store_test.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  	"fmt"
     8  	"os"
     9  	"regexp"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/go-sql-driver/mysql"
    15  	"github.com/lib/pq"
    16  	"github.com/mattermost/gorp"
    17  	"github.com/pkg/errors"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  
    21  	"github.com/mattermost/mattermost-server/v5/einterfaces/mocks"
    22  	"github.com/mattermost/mattermost-server/v5/model"
    23  	"github.com/mattermost/mattermost-server/v5/store"
    24  	"github.com/mattermost/mattermost-server/v5/store/searchtest"
    25  	"github.com/mattermost/mattermost-server/v5/store/storetest"
    26  )
    27  
    28  type storeType struct {
    29  	Name        string
    30  	SqlSettings *model.SqlSettings
    31  	SqlStore    *SqlStore
    32  	Store       store.Store
    33  }
    34  
    35  var storeTypes []*storeType
    36  
    37  func newStoreType(name, driver string) *storeType {
    38  	return &storeType{
    39  		Name:        name,
    40  		SqlSettings: storetest.MakeSqlSettings(driver, false),
    41  	}
    42  }
    43  
    44  func StoreTest(t *testing.T, f func(*testing.T, store.Store)) {
    45  	defer func() {
    46  		if err := recover(); err != nil {
    47  			tearDownStores()
    48  			panic(err)
    49  		}
    50  	}()
    51  	for _, st := range storeTypes {
    52  		st := st
    53  		t.Run(st.Name, func(t *testing.T) {
    54  			if testing.Short() {
    55  				t.SkipNow()
    56  			}
    57  			f(t, st.Store)
    58  		})
    59  	}
    60  }
    61  
    62  func StoreTestWithSearchTestEngine(t *testing.T, f func(*testing.T, store.Store, *searchtest.SearchTestEngine)) {
    63  	defer func() {
    64  		if err := recover(); err != nil {
    65  			tearDownStores()
    66  			panic(err)
    67  		}
    68  	}()
    69  
    70  	for _, st := range storeTypes {
    71  		st := st
    72  		searchTestEngine := &searchtest.SearchTestEngine{
    73  			Driver: *st.SqlSettings.DriverName,
    74  		}
    75  
    76  		t.Run(st.Name, func(t *testing.T) { f(t, st.Store, searchTestEngine) })
    77  	}
    78  }
    79  
    80  func StoreTestWithSqlStore(t *testing.T, f func(*testing.T, store.Store, storetest.SqlStore)) {
    81  	defer func() {
    82  		if err := recover(); err != nil {
    83  			tearDownStores()
    84  			panic(err)
    85  		}
    86  	}()
    87  	for _, st := range storeTypes {
    88  		st := st
    89  		t.Run(st.Name, func(t *testing.T) {
    90  			if testing.Short() {
    91  				t.SkipNow()
    92  			}
    93  			f(t, st.Store, st.SqlStore)
    94  		})
    95  	}
    96  }
    97  
    98  func initStores() {
    99  	if testing.Short() {
   100  		return
   101  	}
   102  	// In CI, we already run the entire test suite for both mysql and postgres in parallel.
   103  	// So we just run the tests for the current database set.
   104  	if os.Getenv("IS_CI") == "true" {
   105  		switch os.Getenv("MM_SQLSETTINGS_DRIVERNAME") {
   106  		case "mysql":
   107  			storeTypes = append(storeTypes, newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL))
   108  		case "postgres":
   109  			storeTypes = append(storeTypes, newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES))
   110  		}
   111  	} else {
   112  		storeTypes = append(storeTypes,
   113  			newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL),
   114  			newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES),
   115  		)
   116  	}
   117  
   118  	defer func() {
   119  		if err := recover(); err != nil {
   120  			tearDownStores()
   121  			panic(err)
   122  		}
   123  	}()
   124  	var wg sync.WaitGroup
   125  	for _, st := range storeTypes {
   126  		st := st
   127  		wg.Add(1)
   128  		go func() {
   129  			defer wg.Done()
   130  			st.SqlStore = New(*st.SqlSettings, nil)
   131  			st.Store = st.SqlStore
   132  			st.Store.DropAllTables()
   133  			st.Store.MarkSystemRanUnitTests()
   134  		}()
   135  	}
   136  	wg.Wait()
   137  }
   138  
   139  var tearDownStoresOnce sync.Once
   140  
   141  func tearDownStores() {
   142  	if testing.Short() {
   143  		return
   144  	}
   145  	tearDownStoresOnce.Do(func() {
   146  		var wg sync.WaitGroup
   147  		wg.Add(len(storeTypes))
   148  		for _, st := range storeTypes {
   149  			st := st
   150  			go func() {
   151  				if st.Store != nil {
   152  					st.Store.Close()
   153  				}
   154  				if st.SqlSettings != nil {
   155  					storetest.CleanupSqlSettings(st.SqlSettings)
   156  				}
   157  				wg.Done()
   158  			}()
   159  		}
   160  		wg.Wait()
   161  	})
   162  }
   163  
   164  // This test was used to consistently reproduce the race
   165  // before the fix in MM-28397.
   166  // Keeping it here to help avoiding future regressions.
   167  func TestStoreLicenseRace(t *testing.T) {
   168  	settings := makeSqlSettings(model.DATABASE_DRIVER_POSTGRES)
   169  	store := New(*settings, nil)
   170  	defer func() {
   171  		store.Close()
   172  		storetest.CleanupSqlSettings(settings)
   173  	}()
   174  
   175  	wg := sync.WaitGroup{}
   176  	wg.Add(3)
   177  
   178  	go func() {
   179  		store.UpdateLicense(&model.License{})
   180  		wg.Done()
   181  	}()
   182  
   183  	go func() {
   184  		store.GetReplica()
   185  		wg.Done()
   186  	}()
   187  
   188  	go func() {
   189  		store.GetSearchReplica()
   190  		wg.Done()
   191  	}()
   192  
   193  	wg.Wait()
   194  }
   195  
   196  func TestGetReplica(t *testing.T) {
   197  	t.Parallel()
   198  	testCases := []struct {
   199  		Description                string
   200  		DataSourceReplicaNum       int
   201  		DataSourceSearchReplicaNum int
   202  	}{
   203  		{
   204  			"no replicas",
   205  			0,
   206  			0,
   207  		},
   208  		{
   209  			"one source replica",
   210  			1,
   211  			0,
   212  		},
   213  		{
   214  			"multiple source replicas",
   215  			3,
   216  			0,
   217  		},
   218  		{
   219  			"one source search replica",
   220  			0,
   221  			1,
   222  		},
   223  		{
   224  			"multiple source search replicas",
   225  			0,
   226  			3,
   227  		},
   228  		{
   229  			"one source replica, one source search replica",
   230  			1,
   231  			1,
   232  		},
   233  		{
   234  			"one source replica, multiple source search replicas",
   235  			1,
   236  			3,
   237  		},
   238  		{
   239  			"multiple source replica, one source search replica",
   240  			3,
   241  			1,
   242  		},
   243  		{
   244  			"multiple source replica, multiple source search replicas",
   245  			3,
   246  			3,
   247  		},
   248  	}
   249  
   250  	for _, testCase := range testCases {
   251  		testCase := testCase
   252  		t.Run(testCase.Description+" with license", func(t *testing.T) {
   253  
   254  			settings := makeSqlSettings(model.DATABASE_DRIVER_POSTGRES)
   255  			dataSourceReplicas := []string{}
   256  			dataSourceSearchReplicas := []string{}
   257  			for i := 0; i < testCase.DataSourceReplicaNum; i++ {
   258  				dataSourceReplicas = append(dataSourceReplicas, *settings.DataSource)
   259  			}
   260  			for i := 0; i < testCase.DataSourceSearchReplicaNum; i++ {
   261  				dataSourceSearchReplicas = append(dataSourceSearchReplicas, *settings.DataSource)
   262  			}
   263  
   264  			settings.DataSourceReplicas = dataSourceReplicas
   265  			settings.DataSourceSearchReplicas = dataSourceSearchReplicas
   266  			store := New(*settings, nil)
   267  			defer func() {
   268  				store.Close()
   269  				storetest.CleanupSqlSettings(settings)
   270  			}()
   271  
   272  			store.UpdateLicense(&model.License{})
   273  
   274  			replicas := make(map[*gorp.DbMap]bool)
   275  			for i := 0; i < 5; i++ {
   276  				replicas[store.GetReplica()] = true
   277  			}
   278  
   279  			searchReplicas := make(map[*gorp.DbMap]bool)
   280  			for i := 0; i < 5; i++ {
   281  				searchReplicas[store.GetSearchReplica()] = true
   282  			}
   283  
   284  			if testCase.DataSourceReplicaNum > 0 {
   285  				// If replicas were defined, ensure none are the master.
   286  				assert.Len(t, replicas, testCase.DataSourceReplicaNum)
   287  
   288  				for replica := range replicas {
   289  					assert.NotSame(t, store.GetMaster(), replica)
   290  				}
   291  
   292  			} else if assert.Len(t, replicas, 1) {
   293  				// Otherwise ensure the replicas contains only the master.
   294  				for replica := range replicas {
   295  					assert.Same(t, store.GetMaster(), replica)
   296  				}
   297  			}
   298  
   299  			if testCase.DataSourceSearchReplicaNum > 0 {
   300  				// If search replicas were defined, ensure none are the master nor the replicas.
   301  				assert.Len(t, searchReplicas, testCase.DataSourceSearchReplicaNum)
   302  
   303  				for searchReplica := range searchReplicas {
   304  					assert.NotSame(t, store.GetMaster(), searchReplica)
   305  					for replica := range replicas {
   306  						assert.NotSame(t, searchReplica, replica)
   307  					}
   308  				}
   309  			} else if testCase.DataSourceReplicaNum > 0 {
   310  				assert.Equal(t, len(replicas), len(searchReplicas))
   311  				for k := range replicas {
   312  					assert.True(t, searchReplicas[k])
   313  				}
   314  			} else if testCase.DataSourceReplicaNum == 0 && assert.Len(t, searchReplicas, 1) {
   315  				// Otherwise ensure the search replicas contains the master.
   316  				for searchReplica := range searchReplicas {
   317  					assert.Same(t, store.GetMaster(), searchReplica)
   318  				}
   319  			}
   320  		})
   321  
   322  		t.Run(testCase.Description+" without license", func(t *testing.T) {
   323  
   324  			settings := makeSqlSettings(model.DATABASE_DRIVER_POSTGRES)
   325  			dataSourceReplicas := []string{}
   326  			dataSourceSearchReplicas := []string{}
   327  			for i := 0; i < testCase.DataSourceReplicaNum; i++ {
   328  				dataSourceReplicas = append(dataSourceReplicas, *settings.DataSource)
   329  			}
   330  			for i := 0; i < testCase.DataSourceSearchReplicaNum; i++ {
   331  				dataSourceSearchReplicas = append(dataSourceSearchReplicas, *settings.DataSource)
   332  			}
   333  
   334  			settings.DataSourceReplicas = dataSourceReplicas
   335  			settings.DataSourceSearchReplicas = dataSourceSearchReplicas
   336  			store := New(*settings, nil)
   337  			defer func() {
   338  				store.Close()
   339  				storetest.CleanupSqlSettings(settings)
   340  			}()
   341  
   342  			replicas := make(map[*gorp.DbMap]bool)
   343  			for i := 0; i < 5; i++ {
   344  				replicas[store.GetReplica()] = true
   345  			}
   346  
   347  			searchReplicas := make(map[*gorp.DbMap]bool)
   348  			for i := 0; i < 5; i++ {
   349  				searchReplicas[store.GetSearchReplica()] = true
   350  			}
   351  
   352  			if testCase.DataSourceReplicaNum > 0 {
   353  				// If replicas were defined, ensure none are the master.
   354  				assert.Len(t, replicas, 1)
   355  
   356  				for replica := range replicas {
   357  					assert.Same(t, store.GetMaster(), replica)
   358  				}
   359  
   360  			} else if assert.Len(t, replicas, 1) {
   361  				// Otherwise ensure the replicas contains only the master.
   362  				for replica := range replicas {
   363  					assert.Same(t, store.GetMaster(), replica)
   364  				}
   365  			}
   366  
   367  			if testCase.DataSourceSearchReplicaNum > 0 {
   368  				// If search replicas were defined, ensure none are the master nor the replicas.
   369  				assert.Len(t, searchReplicas, 1)
   370  
   371  				for searchReplica := range searchReplicas {
   372  					assert.Same(t, store.GetMaster(), searchReplica)
   373  				}
   374  
   375  			} else if testCase.DataSourceReplicaNum > 0 {
   376  				assert.Equal(t, len(replicas), len(searchReplicas))
   377  				for k := range replicas {
   378  					assert.True(t, searchReplicas[k])
   379  				}
   380  			} else if assert.Len(t, searchReplicas, 1) {
   381  				// Otherwise ensure the search replicas contains the master.
   382  				for searchReplica := range searchReplicas {
   383  					assert.Same(t, store.GetMaster(), searchReplica)
   384  				}
   385  			}
   386  		})
   387  	}
   388  }
   389  
   390  func TestGetDbVersion(t *testing.T) {
   391  	testDrivers := []string{
   392  		model.DATABASE_DRIVER_POSTGRES,
   393  		model.DATABASE_DRIVER_MYSQL,
   394  	}
   395  
   396  	for _, driver := range testDrivers {
   397  		t.Run("Should return db version for "+driver, func(t *testing.T) {
   398  			t.Parallel()
   399  			settings := makeSqlSettings(driver)
   400  			store := New(*settings, nil)
   401  
   402  			version, err := store.GetDbVersion(false)
   403  			require.NoError(t, err)
   404  			require.Regexp(t, regexp.MustCompile(`\d+\.\d+(\.\d+)?`), version)
   405  		})
   406  	}
   407  }
   408  
   409  func TestUpAndDownMigrations(t *testing.T) {
   410  	testDrivers := []string{
   411  		model.DATABASE_DRIVER_POSTGRES,
   412  		model.DATABASE_DRIVER_MYSQL,
   413  	}
   414  
   415  	for _, driver := range testDrivers {
   416  		t.Run("Should be reversible for "+driver, func(t *testing.T) {
   417  			t.Parallel()
   418  			settings := makeSqlSettings(driver)
   419  			store := New(*settings, nil)
   420  			defer store.Close()
   421  
   422  			err := store.migrate(migrationsDirectionDown)
   423  			assert.NoError(t, err, "downing migrations should not error")
   424  		})
   425  	}
   426  }
   427  
   428  func TestGetAllConns(t *testing.T) {
   429  	t.Parallel()
   430  	testCases := []struct {
   431  		Description                string
   432  		DataSourceReplicaNum       int
   433  		DataSourceSearchReplicaNum int
   434  		ExpectedNumConnections     int
   435  	}{
   436  		{
   437  			"no replicas",
   438  			0,
   439  			0,
   440  			1,
   441  		},
   442  		{
   443  			"one source replica",
   444  			1,
   445  			0,
   446  			2,
   447  		},
   448  		{
   449  			"multiple source replicas",
   450  			3,
   451  			0,
   452  			4,
   453  		},
   454  		{
   455  			"one source search replica",
   456  			0,
   457  			1,
   458  			1,
   459  		},
   460  		{
   461  			"multiple source search replicas",
   462  			0,
   463  			3,
   464  			1,
   465  		},
   466  		{
   467  			"one source replica, one source search replica",
   468  			1,
   469  			1,
   470  			2,
   471  		},
   472  		{
   473  			"one source replica, multiple source search replicas",
   474  			1,
   475  			3,
   476  			2,
   477  		},
   478  		{
   479  			"multiple source replica, one source search replica",
   480  			3,
   481  			1,
   482  			4,
   483  		},
   484  		{
   485  			"multiple source replica, multiple source search replicas",
   486  			3,
   487  			3,
   488  			4,
   489  		},
   490  	}
   491  
   492  	for _, testCase := range testCases {
   493  		testCase := testCase
   494  		t.Run(testCase.Description, func(t *testing.T) {
   495  			t.Parallel()
   496  			settings := makeSqlSettings(model.DATABASE_DRIVER_POSTGRES)
   497  			dataSourceReplicas := []string{}
   498  			dataSourceSearchReplicas := []string{}
   499  			for i := 0; i < testCase.DataSourceReplicaNum; i++ {
   500  				dataSourceReplicas = append(dataSourceReplicas, *settings.DataSource)
   501  			}
   502  			for i := 0; i < testCase.DataSourceSearchReplicaNum; i++ {
   503  				dataSourceSearchReplicas = append(dataSourceSearchReplicas, *settings.DataSource)
   504  			}
   505  
   506  			settings.DataSourceReplicas = dataSourceReplicas
   507  			settings.DataSourceSearchReplicas = dataSourceSearchReplicas
   508  			store := New(*settings, nil)
   509  			defer func() {
   510  				store.Close()
   511  				storetest.CleanupSqlSettings(settings)
   512  			}()
   513  
   514  			assert.Len(t, store.GetAllConns(), testCase.ExpectedNumConnections)
   515  		})
   516  	}
   517  }
   518  
   519  func TestIsDuplicate(t *testing.T) {
   520  	testErrors := map[error]bool{
   521  		&pq.Error{Code: "42P06"}:                          false,
   522  		&pq.Error{Code: PGDupTableErrorCode}:              true,
   523  		&mysql.MySQLError{Number: uint16(1000)}:           false,
   524  		&mysql.MySQLError{Number: MySQLDupTableErrorCode}: true,
   525  		errors.New("Random error"):                        false,
   526  	}
   527  
   528  	for err, expected := range testErrors {
   529  		t.Run(fmt.Sprintf("Should return %t for %s", expected, err.Error()), func(t *testing.T) {
   530  			t.Parallel()
   531  			assert.Equal(t, expected, IsDuplicate(err))
   532  		})
   533  	}
   534  }
   535  
   536  func TestVersionString(t *testing.T) {
   537  	versions := []struct {
   538  		input  int
   539  		output string
   540  	}{
   541  		{
   542  			input:  100000,
   543  			output: "10.0",
   544  		},
   545  		{
   546  			input:  90603,
   547  			output: "9.603",
   548  		},
   549  		{
   550  			input:  120005,
   551  			output: "12.5",
   552  		},
   553  	}
   554  
   555  	for _, v := range versions {
   556  		out := VersionString(v.input)
   557  		assert.Equal(t, v.output, out)
   558  	}
   559  }
   560  
   561  func TestReplicaLagQuery(t *testing.T) {
   562  	testDrivers := []string{
   563  		model.DATABASE_DRIVER_POSTGRES,
   564  		model.DATABASE_DRIVER_MYSQL,
   565  	}
   566  
   567  	for _, driver := range testDrivers {
   568  		settings := makeSqlSettings(driver)
   569  		var query string
   570  		var tableName string
   571  		// Just any random query which returns a row in (string, int) format.
   572  		switch driver {
   573  		case model.DATABASE_DRIVER_POSTGRES:
   574  			query = `SELECT relname, count(relname) FROM pg_class WHERE relname='posts' GROUP BY relname`
   575  			tableName = "posts"
   576  		case model.DATABASE_DRIVER_MYSQL:
   577  			query = `SELECT table_name, count(table_name) FROM information_schema.tables WHERE table_name='Posts' and table_schema=Database() GROUP BY table_name`
   578  			tableName = "Posts"
   579  		}
   580  
   581  		settings.ReplicaLagSettings = []*model.ReplicaLagSettings{{
   582  			DataSource:       model.NewString(*settings.DataSource),
   583  			QueryAbsoluteLag: model.NewString(query),
   584  			QueryTimeLag:     model.NewString(query),
   585  		}}
   586  
   587  		mockMetrics := &mocks.MetricsInterface{}
   588  		defer mockMetrics.AssertExpectations(t)
   589  		mockMetrics.On("SetReplicaLagAbsolute", tableName, float64(1))
   590  		mockMetrics.On("SetReplicaLagTime", tableName, float64(1))
   591  
   592  		store := &SqlStore{
   593  			rrCounter: 0,
   594  			srCounter: 0,
   595  			settings:  settings,
   596  			metrics:   mockMetrics,
   597  		}
   598  
   599  		store.initConnection()
   600  		store.stores.post = newSqlPostStore(store, mockMetrics)
   601  		err := store.GetMaster().CreateTablesIfNotExists()
   602  		require.NoError(t, err)
   603  
   604  		defer store.Close()
   605  
   606  		err = store.ReplicaLagAbs()
   607  		require.NoError(t, err)
   608  		err = store.ReplicaLagTime()
   609  		require.NoError(t, err)
   610  	}
   611  }
   612  
   613  func TestAppendMultipleStatementsFlagMysql(t *testing.T) {
   614  	testCases := []struct {
   615  		Scenario    string
   616  		DSN         string
   617  		ExpectedDSN string
   618  		Driver      string
   619  	}{
   620  		{
   621  			"Should append multiStatements param to the DSN path with existing params",
   622  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s",
   623  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?writeTimeout=30s&multiStatements=true",
   624  			model.DATABASE_DRIVER_MYSQL,
   625  		},
   626  		{
   627  			"Should append multiStatements param to the DSN path with no existing params",
   628  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
   629  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost?multiStatements=true",
   630  			model.DATABASE_DRIVER_MYSQL,
   631  		},
   632  		{
   633  			"Should not multiStatements param to the DSN when driver is not MySQL",
   634  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
   635  			"user:rand?&ompasswith@character@unix(/var/run/mysqld/mysqld.sock)/mattermost",
   636  			model.DATABASE_DRIVER_POSTGRES,
   637  		},
   638  	}
   639  
   640  	for _, tc := range testCases {
   641  		t.Run(tc.Scenario, func(t *testing.T) {
   642  			store := &SqlStore{settings: &model.SqlSettings{DriverName: &tc.Driver, DataSource: &tc.DSN}}
   643  			res, err := store.appendMultipleStatementsFlag(*store.settings.DataSource)
   644  			require.NoError(t, err)
   645  			assert.Equal(t, tc.ExpectedDSN, res)
   646  		})
   647  	}
   648  }
   649  
   650  func makeSqlSettings(driver string) *model.SqlSettings {
   651  	switch driver {
   652  	case model.DATABASE_DRIVER_POSTGRES:
   653  		return storetest.MakeSqlSettings(driver, false)
   654  	case model.DATABASE_DRIVER_MYSQL:
   655  		return storetest.MakeSqlSettings(driver, false)
   656  	}
   657  
   658  	return nil
   659  }
   660  
   661  func TestExecNoTimeout(t *testing.T) {
   662  	StoreTest(t, func(t *testing.T, ss store.Store) {
   663  		sqlStore := ss.(*SqlStore)
   664  		var query string
   665  		timeout := sqlStore.master.QueryTimeout
   666  		sqlStore.master.QueryTimeout = 1
   667  		defer func() {
   668  			sqlStore.master.QueryTimeout = timeout
   669  		}()
   670  		if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   671  			query = `SELECT SLEEP(2);`
   672  		} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   673  			query = `SELECT pg_sleep(2);`
   674  		}
   675  		_, err := sqlStore.GetMaster().ExecNoTimeout(query)
   676  		require.NoError(t, err)
   677  	})
   678  }
   679  
   680  func TestMySQLReadTimeout(t *testing.T) {
   681  	settings := makeSqlSettings(model.DATABASE_DRIVER_MYSQL)
   682  	dataSource := *settings.DataSource
   683  	config, err := mysql.ParseDSN(dataSource)
   684  	require.NoError(t, err)
   685  
   686  	config.ReadTimeout = 1 * time.Second
   687  	dataSource = config.FormatDSN()
   688  	settings.DataSource = &dataSource
   689  
   690  	store := &SqlStore{
   691  		settings: settings,
   692  	}
   693  	store.initConnection()
   694  	defer store.Close()
   695  
   696  	_, err = store.GetMaster().ExecNoTimeout(`SELECT SLEEP(3)`)
   697  	require.NoError(t, err)
   698  }
   699  
   700  func TestRemoveIndexIfExists(t *testing.T) {
   701  	StoreTest(t, func(t *testing.T, ss store.Store) {
   702  		sqlStore := ss.(*SqlStore)
   703  
   704  		_, err := sqlStore.GetMaster().ExecNoTimeout(`CREATE INDEX idx_posts_create_at ON Posts (CreateAt)`)
   705  		require.Error(t, err)
   706  
   707  		ok := sqlStore.RemoveIndexIfExists("idx_posts_create_at", "Posts")
   708  		require.True(t, ok)
   709  
   710  		ok = sqlStore.RemoveIndexIfExists("idx_posts_create_at", "Posts")
   711  		require.False(t, ok)
   712  
   713  		_, err = sqlStore.GetMaster().ExecNoTimeout(`CREATE INDEX idx_posts_create_at ON Posts (CreateAt)`)
   714  		require.NoError(t, err)
   715  
   716  		ok = sqlStore.RemoveIndexIfExists("idx_posts_create_at", "Posts")
   717  		require.True(t, ok)
   718  
   719  		ok = sqlStore.RemoveIndexIfExists("idx_posts_create_at", "Posts")
   720  		require.False(t, ok)
   721  	})
   722  }
   723  
   724  func TestAlterDefaultIfColumnExists(t *testing.T) {
   725  	StoreTest(t, func(t *testing.T, ss store.Store) {
   726  		var query string
   727  		def := new(string)
   728  		sqlStore := ss.(*SqlStore)
   729  
   730  		t.Run("non existent table", func(t *testing.T) {
   731  			ok := sqlStore.AlterDefaultIfColumnExists("NotExistent", "NotExistent", nil, nil)
   732  			require.False(t, ok)
   733  		})
   734  
   735  		t.Run("non existent column", func(t *testing.T) {
   736  			ok := sqlStore.AlterDefaultIfColumnExists("Posts", "NotExistent", nil, nil)
   737  			require.False(t, ok)
   738  		})
   739  
   740  		t.Run("empty string", func(t *testing.T) {
   741  			ok := sqlStore.AlterDefaultIfColumnExists("Posts", "Id", model.NewString(""), model.NewString(""))
   742  			require.True(t, ok)
   743  
   744  			if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   745  				query = `SELECT column_default
   746  			 FROM information_schema.columns
   747  			 WHERE table_schema = DATABASE()
   748  			 AND table_name = 'Posts'
   749  			 AND column_name = 'Id'`
   750  			} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   751  				query = `SELECT column_default
   752  			 FROM information_schema.columns
   753  			 WHERE table_name = 'posts'
   754  			 AND column_name = 'id'`
   755  			}
   756  
   757  			err := sqlStore.GetMaster().SelectOne(&def, query)
   758  			require.NoError(t, err)
   759  			require.NotNil(t, def)
   760  			if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   761  				require.Equal(t, "", *def)
   762  			} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   763  				require.Equal(t, "''::character varying", *def)
   764  			}
   765  		})
   766  
   767  		t.Run("nil input", func(t *testing.T) {
   768  			ok := sqlStore.AlterDefaultIfColumnExists("Posts", "Id", nil, nil)
   769  			require.True(t, ok)
   770  
   771  			err := sqlStore.GetMaster().SelectOne(&def, query)
   772  			require.NoError(t, err)
   773  			require.NotNil(t, def)
   774  			if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   775  				require.Equal(t, "", *def)
   776  			} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   777  				require.Equal(t, "''::character varying", *def)
   778  			}
   779  		})
   780  
   781  		t.Run("remove", func(t *testing.T) {
   782  			ok := sqlStore.RemoveDefaultIfColumnExists("Posts", "Id")
   783  			require.True(t, ok)
   784  
   785  			err := sqlStore.GetMaster().SelectOne(&def, query)
   786  			require.NoError(t, err)
   787  			require.Nil(t, def)
   788  		})
   789  
   790  		t.Run("string default", func(t *testing.T) {
   791  			ok := sqlStore.AlterDefaultIfColumnExists("Posts", "Id", model.NewString("'test'"), model.NewString("'test'"))
   792  			require.True(t, ok)
   793  
   794  			err := sqlStore.GetMaster().SelectOne(&def, query)
   795  			require.NoError(t, err)
   796  			require.NotNil(t, def)
   797  			if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   798  				require.Equal(t, "test", *def)
   799  			} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   800  				require.Equal(t, "'test'::character varying", *def)
   801  			}
   802  
   803  			ok = sqlStore.RemoveDefaultIfColumnExists("Posts", "Id")
   804  			require.True(t, ok)
   805  		})
   806  
   807  		t.Run("int default", func(t *testing.T) {
   808  			ok := sqlStore.AlterDefaultIfColumnExists("Posts", "UpdateAt", model.NewString("0"), model.NewString("0"))
   809  			require.True(t, ok)
   810  
   811  			if sqlStore.DriverName() == model.DATABASE_DRIVER_MYSQL {
   812  				query = `SELECT column_default
   813  			 FROM information_schema.columns
   814  			 WHERE table_schema = DATABASE()
   815  			 AND table_name = 'Posts'
   816  			 AND column_name = 'UpdateAt'`
   817  			} else if sqlStore.DriverName() == model.DATABASE_DRIVER_POSTGRES {
   818  				query = `SELECT column_default
   819  			 FROM information_schema.columns
   820  			 WHERE table_name = 'posts'
   821  			 AND column_name = 'updateat'`
   822  			}
   823  
   824  			err := sqlStore.GetMaster().SelectOne(&def, query)
   825  			require.NoError(t, err)
   826  			require.NotNil(t, def)
   827  			require.Equal(t, "0", *def)
   828  
   829  			ok = sqlStore.RemoveDefaultIfColumnExists("Posts", "UpdateAt")
   830  			require.True(t, ok)
   831  		})
   832  	})
   833  }