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