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