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 }