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