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