github.com/mattermost/mattermost-server/v5@v5.39.3/store/sqlstore/store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "context" 8 dbsql "database/sql" 9 "encoding/json" 10 "fmt" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "sync" 16 "sync/atomic" 17 "time" 18 19 sq "github.com/Masterminds/squirrel" 20 "github.com/dyatlov/go-opengraph/opengraph" 21 "github.com/go-sql-driver/mysql" 22 "github.com/golang-migrate/migrate/v4" 23 "github.com/golang-migrate/migrate/v4/database" 24 mysqlmigrate "github.com/golang-migrate/migrate/v4/database/mysql" 25 "github.com/golang-migrate/migrate/v4/database/postgres" 26 _ "github.com/golang-migrate/migrate/v4/source/file" 27 bindata "github.com/golang-migrate/migrate/v4/source/go_bindata" 28 "github.com/lib/pq" 29 _ "github.com/lib/pq" 30 "github.com/mattermost/gorp" 31 "github.com/pkg/errors" 32 33 "github.com/mattermost/mattermost-server/v5/db/migrations" 34 "github.com/mattermost/mattermost-server/v5/einterfaces" 35 "github.com/mattermost/mattermost-server/v5/model" 36 "github.com/mattermost/mattermost-server/v5/shared/i18n" 37 "github.com/mattermost/mattermost-server/v5/shared/mlog" 38 "github.com/mattermost/mattermost-server/v5/store" 39 ) 40 41 type migrationDirection string 42 43 const ( 44 IndexTypeFullText = "full_text" 45 IndexTypeFullTextFunc = "full_text_func" 46 IndexTypeDefault = "default" 47 PGDupTableErrorCode = "42P07" // see https://github.com/lib/pq/blob/master/error.go#L268 48 MySQLDupTableErrorCode = uint16(1050) // see https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html#error_er_table_exists_error 49 PGForeignKeyViolationErrorCode = "23503" 50 MySQLForeignKeyViolationErrorCode = 1452 51 PGDuplicateObjectErrorCode = "42710" 52 MySQLDuplicateObjectErrorCode = 1022 53 DBPingAttempts = 18 54 DBPingTimeoutSecs = 10 55 // This is a numerical version string by postgres. The format is 56 // 2 characters for major, minor, and patch version prior to 10. 57 // After 10, it's major and minor only. 58 // 10.1 would be 100001. 59 // 9.6.3 would be 90603. 60 MinimumRequiredPostgresVersion = 100000 61 62 migrationsDirectionUp migrationDirection = "up" 63 migrationsDirectionDown migrationDirection = "down" 64 65 replicaLagPrefix = "replica-lag" 66 ) 67 68 const ( 69 ExitGenericFailure = 1 70 ExitCreateTable = 100 71 ExitDBOpen = 101 72 ExitPing = 102 73 ExitNoDriver = 103 74 ExitTableExists = 104 75 ExitTableExistsMySQL = 105 76 ExitColumnExists = 106 77 ExitDoesColumnExistsPostgres = 107 78 ExitDoesColumnExistsMySQL = 108 79 ExitDoesColumnExistsMissing = 109 80 ExitCreateColumnPostgres = 110 81 ExitCreateColumnMySQL = 111 82 ExitCreateColumnMissing = 112 83 ExitRemoveColumn = 113 84 ExitRenameColumn = 114 85 ExitMaxColumn = 115 86 ExitAlterColumn = 116 87 ExitCreateIndexPostgres = 117 88 ExitCreateIndexMySQL = 118 89 ExitCreateIndexFullMySQL = 119 90 ExitCreateIndexMissing = 120 91 ExitRemoveIndexPostgres = 121 92 ExitRemoveIndexMySQL = 122 93 ExitRemoveIndexMissing = 123 94 ExitDoesIndexExists = 124 95 ExitDoesIndexExistsMySQL = 125 96 ExitRemoveTable = 134 97 ExitAlterPrimaryKey = 139 98 ) 99 100 type SqlStoreStores struct { 101 team store.TeamStore 102 channel store.ChannelStore 103 post store.PostStore 104 retentionPolicy store.RetentionPolicyStore 105 thread store.ThreadStore 106 user store.UserStore 107 bot store.BotStore 108 audit store.AuditStore 109 cluster store.ClusterDiscoveryStore 110 remoteCluster store.RemoteClusterStore 111 compliance store.ComplianceStore 112 session store.SessionStore 113 oauth store.OAuthStore 114 system store.SystemStore 115 webhook store.WebhookStore 116 command store.CommandStore 117 commandWebhook store.CommandWebhookStore 118 preference store.PreferenceStore 119 license store.LicenseStore 120 token store.TokenStore 121 emoji store.EmojiStore 122 status store.StatusStore 123 fileInfo store.FileInfoStore 124 uploadSession store.UploadSessionStore 125 reaction store.ReactionStore 126 job store.JobStore 127 userAccessToken store.UserAccessTokenStore 128 plugin store.PluginStore 129 channelMemberHistory store.ChannelMemberHistoryStore 130 role store.RoleStore 131 scheme store.SchemeStore 132 TermsOfService store.TermsOfServiceStore 133 productNotices store.ProductNoticesStore 134 group store.GroupStore 135 UserTermsOfService store.UserTermsOfServiceStore 136 linkMetadata store.LinkMetadataStore 137 sharedchannel store.SharedChannelStore 138 } 139 140 type SqlStore struct { 141 // rrCounter and srCounter should be kept first. 142 // See https://github.com/mattermost/mattermost-server/v5/pull/7281 143 rrCounter int64 144 srCounter int64 145 master *gorp.DbMap 146 Replicas []*gorp.DbMap 147 searchReplicas []*gorp.DbMap 148 replicaLagHandles []*dbsql.DB 149 stores SqlStoreStores 150 settings *model.SqlSettings 151 lockedToMaster bool 152 context context.Context 153 license *model.License 154 licenseMutex sync.RWMutex 155 metrics einterfaces.MetricsInterface 156 } 157 158 type TraceOnAdapter struct{} 159 160 // ColumnInfo holds information about a column. 161 type ColumnInfo struct { 162 DataType string 163 CharMaximumLength int 164 } 165 166 func (t *TraceOnAdapter) Printf(format string, v ...interface{}) { 167 originalString := fmt.Sprintf(format, v...) 168 newString := strings.ReplaceAll(originalString, "\n", " ") 169 newString = strings.ReplaceAll(newString, "\t", " ") 170 newString = strings.ReplaceAll(newString, "\"", "") 171 mlog.Debug(newString) 172 } 173 174 func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlStore { 175 store := &SqlStore{ 176 rrCounter: 0, 177 srCounter: 0, 178 settings: &settings, 179 metrics: metrics, 180 } 181 182 store.initConnection() 183 184 if *settings.DriverName == model.DATABASE_DRIVER_POSTGRES { 185 ver, err := store.GetDbVersion(true) 186 if err != nil { 187 mlog.Critical("Cannot get DB version.", mlog.Err(err)) 188 os.Exit(ExitGenericFailure) 189 } 190 intVer, err := strconv.Atoi(ver) 191 if err != nil { 192 mlog.Critical("Cannot parse DB version.", mlog.Err(err)) 193 os.Exit(ExitGenericFailure) 194 } 195 if intVer < MinimumRequiredPostgresVersion { 196 mlog.Critical("Minimum Postgres version requirements not met.", mlog.String("Found", VersionString(intVer)), mlog.String("Wanted", VersionString(MinimumRequiredPostgresVersion))) 197 os.Exit(ExitGenericFailure) 198 } 199 } 200 201 err := store.migrate(migrationsDirectionUp) 202 if err != nil { 203 mlog.Critical("Failed to apply database migrations.", mlog.Err(err)) 204 os.Exit(ExitGenericFailure) 205 } 206 207 store.stores.team = newSqlTeamStore(store) 208 store.stores.channel = newSqlChannelStore(store, metrics) 209 store.stores.post = newSqlPostStore(store, metrics) 210 store.stores.retentionPolicy = newSqlRetentionPolicyStore(store, metrics) 211 store.stores.user = newSqlUserStore(store, metrics) 212 store.stores.bot = newSqlBotStore(store, metrics) 213 store.stores.audit = newSqlAuditStore(store) 214 store.stores.cluster = newSqlClusterDiscoveryStore(store) 215 store.stores.remoteCluster = newSqlRemoteClusterStore(store) 216 store.stores.compliance = newSqlComplianceStore(store) 217 store.stores.session = newSqlSessionStore(store) 218 store.stores.oauth = newSqlOAuthStore(store) 219 store.stores.system = newSqlSystemStore(store) 220 store.stores.webhook = newSqlWebhookStore(store, metrics) 221 store.stores.command = newSqlCommandStore(store) 222 store.stores.commandWebhook = newSqlCommandWebhookStore(store) 223 store.stores.preference = newSqlPreferenceStore(store) 224 store.stores.license = newSqlLicenseStore(store) 225 store.stores.token = newSqlTokenStore(store) 226 store.stores.emoji = newSqlEmojiStore(store, metrics) 227 store.stores.status = newSqlStatusStore(store) 228 store.stores.fileInfo = newSqlFileInfoStore(store, metrics) 229 store.stores.uploadSession = newSqlUploadSessionStore(store) 230 store.stores.thread = newSqlThreadStore(store) 231 store.stores.job = newSqlJobStore(store) 232 store.stores.userAccessToken = newSqlUserAccessTokenStore(store) 233 store.stores.channelMemberHistory = newSqlChannelMemberHistoryStore(store) 234 store.stores.plugin = newSqlPluginStore(store) 235 store.stores.TermsOfService = newSqlTermsOfServiceStore(store, metrics) 236 store.stores.UserTermsOfService = newSqlUserTermsOfServiceStore(store) 237 store.stores.linkMetadata = newSqlLinkMetadataStore(store) 238 store.stores.sharedchannel = newSqlSharedChannelStore(store) 239 store.stores.reaction = newSqlReactionStore(store) 240 store.stores.role = newSqlRoleStore(store) 241 store.stores.scheme = newSqlSchemeStore(store) 242 store.stores.group = newSqlGroupStore(store) 243 store.stores.productNotices = newSqlProductNoticesStore(store) 244 err = store.GetMaster().CreateTablesIfNotExists() 245 246 if err != nil { 247 if IsDuplicate(err) { 248 mlog.Warn("Duplicate key error occurred; assuming table already created and proceeding.", mlog.Err(err)) 249 } else { 250 mlog.Critical("Error creating database tables.", mlog.Err(err)) 251 os.Exit(ExitCreateTable) 252 } 253 } 254 255 err = upgradeDatabase(store, model.CurrentVersion) 256 if err != nil { 257 mlog.Critical("Failed to upgrade database.", mlog.Err(err)) 258 time.Sleep(time.Second) 259 os.Exit(ExitGenericFailure) 260 } 261 262 store.stores.channel.(*SqlChannelStore).createIndexesIfNotExists() 263 store.stores.post.(*SqlPostStore).createIndexesIfNotExists() 264 store.stores.retentionPolicy.(*SqlRetentionPolicyStore).createIndexesIfNotExists() 265 store.stores.thread.(*SqlThreadStore).createIndexesIfNotExists() 266 store.stores.user.(*SqlUserStore).createIndexesIfNotExists() 267 store.stores.bot.(*SqlBotStore).createIndexesIfNotExists() 268 store.stores.audit.(*SqlAuditStore).createIndexesIfNotExists() 269 store.stores.compliance.(*SqlComplianceStore).createIndexesIfNotExists() 270 store.stores.session.(*SqlSessionStore).createIndexesIfNotExists() 271 store.stores.oauth.(*SqlOAuthStore).createIndexesIfNotExists() 272 store.stores.system.(*SqlSystemStore).createIndexesIfNotExists() 273 store.stores.webhook.(*SqlWebhookStore).createIndexesIfNotExists() 274 store.stores.command.(*SqlCommandStore).createIndexesIfNotExists() 275 store.stores.commandWebhook.(*SqlCommandWebhookStore).createIndexesIfNotExists() 276 store.stores.preference.(*SqlPreferenceStore).createIndexesIfNotExists() 277 store.stores.license.(*SqlLicenseStore).createIndexesIfNotExists() 278 store.stores.token.(*SqlTokenStore).createIndexesIfNotExists() 279 store.stores.emoji.(*SqlEmojiStore).createIndexesIfNotExists() 280 store.stores.status.(*SqlStatusStore).createIndexesIfNotExists() 281 store.stores.fileInfo.(*SqlFileInfoStore).createIndexesIfNotExists() 282 store.stores.uploadSession.(*SqlUploadSessionStore).createIndexesIfNotExists() 283 store.stores.job.(*SqlJobStore).createIndexesIfNotExists() 284 store.stores.userAccessToken.(*SqlUserAccessTokenStore).createIndexesIfNotExists() 285 store.stores.plugin.(*SqlPluginStore).createIndexesIfNotExists() 286 store.stores.TermsOfService.(SqlTermsOfServiceStore).createIndexesIfNotExists() 287 store.stores.productNotices.(SqlProductNoticesStore).createIndexesIfNotExists() 288 store.stores.UserTermsOfService.(SqlUserTermsOfServiceStore).createIndexesIfNotExists() 289 store.stores.linkMetadata.(*SqlLinkMetadataStore).createIndexesIfNotExists() 290 store.stores.sharedchannel.(*SqlSharedChannelStore).createIndexesIfNotExists() 291 store.stores.group.(*SqlGroupStore).createIndexesIfNotExists() 292 store.stores.scheme.(*SqlSchemeStore).createIndexesIfNotExists() 293 store.stores.remoteCluster.(*sqlRemoteClusterStore).createIndexesIfNotExists() 294 store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures() 295 296 return store 297 } 298 299 func setupConnection(connType string, dataSource string, settings *model.SqlSettings) *gorp.DbMap { 300 db, err := dbsql.Open(*settings.DriverName, dataSource) 301 if err != nil { 302 mlog.Critical("Failed to open SQL connection to err.", mlog.Err(err)) 303 time.Sleep(time.Second) 304 os.Exit(ExitDBOpen) 305 } 306 307 for i := 0; i < DBPingAttempts; i++ { 308 mlog.Info("Pinging SQL", mlog.String("database", connType)) 309 ctx, cancel := context.WithTimeout(context.Background(), DBPingTimeoutSecs*time.Second) 310 defer cancel() 311 err = db.PingContext(ctx) 312 if err == nil { 313 break 314 } else { 315 if i == DBPingAttempts-1 { 316 mlog.Critical("Failed to ping DB, server will exit.", mlog.Err(err)) 317 time.Sleep(time.Second) 318 os.Exit(ExitPing) 319 } else { 320 mlog.Error("Failed to ping DB", mlog.Err(err), mlog.Int("retrying in seconds", DBPingTimeoutSecs)) 321 time.Sleep(DBPingTimeoutSecs * time.Second) 322 } 323 } 324 } 325 326 if strings.HasPrefix(connType, replicaLagPrefix) { 327 // If this is a replica lag connection, we just open one connection. 328 // 329 // Arguably, if the query doesn't require a special credential, it does take up 330 // one extra connection from the replica DB. But falling back to the replica 331 // data source when the replica lag data source is null implies an ordering constraint 332 // which makes things brittle and is not a good design. 333 // If connections are an overhead, it is advised to use a connection pool. 334 db.SetMaxOpenConns(1) 335 db.SetMaxIdleConns(1) 336 } else { 337 db.SetMaxIdleConns(*settings.MaxIdleConns) 338 db.SetMaxOpenConns(*settings.MaxOpenConns) 339 } 340 db.SetConnMaxLifetime(time.Duration(*settings.ConnMaxLifetimeMilliseconds) * time.Millisecond) 341 db.SetConnMaxIdleTime(time.Duration(*settings.ConnMaxIdleTimeMilliseconds) * time.Millisecond) 342 343 dbMap := getDBMap(settings, db) 344 345 return dbMap 346 } 347 348 func getDBMap(settings *model.SqlSettings, db *dbsql.DB) *gorp.DbMap { 349 connectionTimeout := time.Duration(*settings.QueryTimeout) * time.Second 350 var dbMap *gorp.DbMap 351 switch *settings.DriverName { 352 case model.DATABASE_DRIVER_MYSQL: 353 dbMap = &gorp.DbMap{ 354 Db: db, 355 TypeConverter: mattermConverter{}, 356 Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}, 357 QueryTimeout: connectionTimeout, 358 } 359 case model.DATABASE_DRIVER_POSTGRES: 360 dbMap = &gorp.DbMap{ 361 Db: db, 362 TypeConverter: mattermConverter{}, 363 Dialect: gorp.PostgresDialect{}, 364 QueryTimeout: connectionTimeout, 365 } 366 default: 367 mlog.Critical("Failed to create dialect specific driver") 368 time.Sleep(time.Second) 369 os.Exit(ExitNoDriver) 370 return nil 371 } 372 if settings.Trace != nil && *settings.Trace { 373 dbMap.TraceOn("sql-trace:", &TraceOnAdapter{}) 374 } 375 return dbMap 376 } 377 378 func (ss *SqlStore) SetContext(context context.Context) { 379 ss.context = context 380 } 381 382 func (ss *SqlStore) Context() context.Context { 383 return ss.context 384 } 385 386 func (ss *SqlStore) initConnection() { 387 dataSource := *ss.settings.DataSource 388 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 389 // TODO: We ignore the readTimeout datasource parameter for MySQL since QueryTimeout 390 // covers that already. Ideally we'd like to do this only for the upgrade 391 // step. To be reviewed in MM-35789. 392 var err error 393 dataSource, err = resetReadTimeout(dataSource) 394 if err != nil { 395 mlog.Critical("Failed to reset read timeout from datasource.", mlog.Err(err)) 396 os.Exit(ExitGenericFailure) 397 } 398 } 399 400 ss.master = setupConnection("master", dataSource, ss.settings) 401 402 if len(ss.settings.DataSourceReplicas) > 0 { 403 ss.Replicas = make([]*gorp.DbMap, len(ss.settings.DataSourceReplicas)) 404 for i, replica := range ss.settings.DataSourceReplicas { 405 ss.Replicas[i] = setupConnection(fmt.Sprintf("replica-%v", i), replica, ss.settings) 406 } 407 } 408 409 if len(ss.settings.DataSourceSearchReplicas) > 0 { 410 ss.searchReplicas = make([]*gorp.DbMap, len(ss.settings.DataSourceSearchReplicas)) 411 for i, replica := range ss.settings.DataSourceSearchReplicas { 412 ss.searchReplicas[i] = setupConnection(fmt.Sprintf("search-replica-%v", i), replica, ss.settings) 413 } 414 } 415 416 if len(ss.settings.ReplicaLagSettings) > 0 { 417 ss.replicaLagHandles = make([]*dbsql.DB, len(ss.settings.ReplicaLagSettings)) 418 for i, src := range ss.settings.ReplicaLagSettings { 419 if src.DataSource == nil { 420 continue 421 } 422 gorpConn := setupConnection(fmt.Sprintf(replicaLagPrefix+"-%d", i), *src.DataSource, ss.settings) 423 ss.replicaLagHandles[i] = gorpConn.Db 424 } 425 } 426 } 427 428 func (ss *SqlStore) DriverName() string { 429 return *ss.settings.DriverName 430 } 431 432 func (ss *SqlStore) GetCurrentSchemaVersion() string { 433 version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'") 434 return version 435 } 436 437 // GetDbVersion returns the version of the database being used. 438 // If numerical is set to true, it attempts to return a numerical version string 439 // that can be parsed by callers. 440 func (ss *SqlStore) GetDbVersion(numerical bool) (string, error) { 441 var sqlVersion string 442 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 443 if numerical { 444 sqlVersion = `SHOW server_version_num` 445 } else { 446 sqlVersion = `SHOW server_version` 447 } 448 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 449 sqlVersion = `SELECT version()` 450 } else { 451 return "", errors.New("Not supported driver") 452 } 453 454 version, err := ss.GetReplica().SelectStr(sqlVersion) 455 if err != nil { 456 return "", err 457 } 458 459 return version, nil 460 461 } 462 463 func (ss *SqlStore) GetMaster() *gorp.DbMap { 464 return ss.master 465 } 466 467 func (ss *SqlStore) GetSearchReplica() *gorp.DbMap { 468 ss.licenseMutex.RLock() 469 license := ss.license 470 ss.licenseMutex.RUnlock() 471 if license == nil { 472 return ss.GetMaster() 473 } 474 475 if len(ss.settings.DataSourceSearchReplicas) == 0 { 476 return ss.GetReplica() 477 } 478 479 rrNum := atomic.AddInt64(&ss.srCounter, 1) % int64(len(ss.searchReplicas)) 480 return ss.searchReplicas[rrNum] 481 } 482 483 func (ss *SqlStore) GetReplica() *gorp.DbMap { 484 ss.licenseMutex.RLock() 485 license := ss.license 486 ss.licenseMutex.RUnlock() 487 if len(ss.settings.DataSourceReplicas) == 0 || ss.lockedToMaster || license == nil { 488 return ss.GetMaster() 489 } 490 491 rrNum := atomic.AddInt64(&ss.rrCounter, 1) % int64(len(ss.Replicas)) 492 return ss.Replicas[rrNum] 493 } 494 495 func (ss *SqlStore) TotalMasterDbConnections() int { 496 return ss.GetMaster().Db.Stats().OpenConnections 497 } 498 499 // ReplicaLagAbs queries all the replica databases to get the absolute replica lag value 500 // and updates the Prometheus metric with it. 501 func (ss *SqlStore) ReplicaLagAbs() error { 502 for i, item := range ss.settings.ReplicaLagSettings { 503 if item.QueryAbsoluteLag == nil || *item.QueryAbsoluteLag == "" { 504 continue 505 } 506 var binDiff float64 507 var node string 508 err := ss.replicaLagHandles[i].QueryRow(*item.QueryAbsoluteLag).Scan(&node, &binDiff) 509 if err != nil { 510 return err 511 } 512 // There is no nil check needed here because it's called from the metrics store. 513 ss.metrics.SetReplicaLagAbsolute(node, binDiff) 514 } 515 return nil 516 } 517 518 // ReplicaLagAbs queries all the replica databases to get the time-based replica lag value 519 // and updates the Prometheus metric with it. 520 func (ss *SqlStore) ReplicaLagTime() error { 521 for i, item := range ss.settings.ReplicaLagSettings { 522 if item.QueryTimeLag == nil || *item.QueryTimeLag == "" { 523 continue 524 } 525 var timeDiff float64 526 var node string 527 err := ss.replicaLagHandles[i].QueryRow(*item.QueryTimeLag).Scan(&node, &timeDiff) 528 if err != nil { 529 return err 530 } 531 // There is no nil check needed here because it's called from the metrics store. 532 ss.metrics.SetReplicaLagTime(node, timeDiff) 533 } 534 return nil 535 } 536 537 func (ss *SqlStore) TotalReadDbConnections() int { 538 if len(ss.settings.DataSourceReplicas) == 0 { 539 return 0 540 } 541 542 count := 0 543 for _, db := range ss.Replicas { 544 count = count + db.Db.Stats().OpenConnections 545 } 546 547 return count 548 } 549 550 func (ss *SqlStore) TotalSearchDbConnections() int { 551 if len(ss.settings.DataSourceSearchReplicas) == 0 { 552 return 0 553 } 554 555 count := 0 556 for _, db := range ss.searchReplicas { 557 count = count + db.Db.Stats().OpenConnections 558 } 559 560 return count 561 } 562 563 func (ss *SqlStore) MarkSystemRanUnitTests() { 564 props, err := ss.System().Get() 565 if err != nil { 566 return 567 } 568 569 unitTests := props[model.SYSTEM_RAN_UNIT_TESTS] 570 if unitTests == "" { 571 systemTests := &model.System{Name: model.SYSTEM_RAN_UNIT_TESTS, Value: "1"} 572 ss.System().Save(systemTests) 573 } 574 } 575 576 func (ss *SqlStore) DoesTableExist(tableName string) bool { 577 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 578 count, err := ss.GetMaster().SelectInt( 579 `SELECT count(relname) FROM pg_class WHERE relname=$1`, 580 strings.ToLower(tableName), 581 ) 582 583 if err != nil { 584 mlog.Critical("Failed to check if table exists", mlog.Err(err)) 585 time.Sleep(time.Second) 586 os.Exit(ExitTableExists) 587 } 588 589 return count > 0 590 591 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 592 593 count, err := ss.GetMaster().SelectInt( 594 `SELECT 595 COUNT(0) AS table_exists 596 FROM 597 information_schema.TABLES 598 WHERE 599 TABLE_SCHEMA = DATABASE() 600 AND TABLE_NAME = ? 601 `, 602 tableName, 603 ) 604 605 if err != nil { 606 mlog.Critical("Failed to check if table exists", mlog.Err(err)) 607 time.Sleep(time.Second) 608 os.Exit(ExitTableExistsMySQL) 609 } 610 611 return count > 0 612 613 } else { 614 mlog.Critical("Failed to check if column exists because of missing driver") 615 time.Sleep(time.Second) 616 os.Exit(ExitColumnExists) 617 return false 618 } 619 } 620 621 func (ss *SqlStore) DoesColumnExist(tableName string, columnName string) bool { 622 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 623 count, err := ss.GetMaster().SelectInt( 624 `SELECT COUNT(0) 625 FROM pg_attribute 626 WHERE attrelid = $1::regclass 627 AND attname = $2 628 AND NOT attisdropped`, 629 strings.ToLower(tableName), 630 strings.ToLower(columnName), 631 ) 632 633 if err != nil { 634 if err.Error() == "pq: relation \""+strings.ToLower(tableName)+"\" does not exist" { 635 return false 636 } 637 638 mlog.Critical("Failed to check if column exists", mlog.Err(err)) 639 time.Sleep(time.Second) 640 os.Exit(ExitDoesColumnExistsPostgres) 641 } 642 643 return count > 0 644 645 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 646 647 count, err := ss.GetMaster().SelectInt( 648 `SELECT 649 COUNT(0) AS column_exists 650 FROM 651 information_schema.COLUMNS 652 WHERE 653 TABLE_SCHEMA = DATABASE() 654 AND TABLE_NAME = ? 655 AND COLUMN_NAME = ?`, 656 tableName, 657 columnName, 658 ) 659 660 if err != nil { 661 mlog.Critical("Failed to check if column exists", mlog.Err(err)) 662 time.Sleep(time.Second) 663 os.Exit(ExitDoesColumnExistsMySQL) 664 } 665 666 return count > 0 667 668 } else { 669 mlog.Critical("Failed to check if column exists because of missing driver") 670 time.Sleep(time.Second) 671 os.Exit(ExitDoesColumnExistsMissing) 672 return false 673 } 674 } 675 676 func (ss *SqlStore) DoesIndexExist(indexName string, tableName string) bool { 677 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 678 _, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName) 679 // It should fail if the index does not exist 680 return err == nil 681 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 682 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) 683 if err != nil { 684 mlog.Critical("Failed to check index", mlog.Err(err)) 685 time.Sleep(time.Second) 686 os.Exit(ExitDoesIndexExistsMySQL) 687 } 688 689 if count <= 0 { 690 return false 691 } 692 } else { 693 mlog.Critical("Failed to check if index exists because of missing driver") 694 time.Sleep(time.Second) 695 os.Exit(ExitDoesIndexExists) 696 } 697 698 return true 699 } 700 701 // GetColumnInfo returns data type information about the given column. 702 func (ss *SqlStore) GetColumnInfo(tableName, columnName string) (*ColumnInfo, error) { 703 var columnInfo ColumnInfo 704 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 705 err := ss.GetMaster().SelectOne(&columnInfo, 706 `SELECT data_type as DataType, 707 COALESCE(character_maximum_length, 0) as CharMaximumLength 708 FROM information_schema.columns 709 WHERE lower(table_name) = lower($1) 710 AND lower(column_name) = lower($2)`, 711 tableName, columnName) 712 if err != nil { 713 return nil, err 714 } 715 return &columnInfo, nil 716 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 717 err := ss.GetMaster().SelectOne(&columnInfo, 718 `SELECT data_type as DataType, 719 COALESCE(character_maximum_length, 0) as CharMaximumLength 720 FROM information_schema.columns 721 WHERE table_schema = DATABASE() 722 AND lower(table_name) = lower(?) 723 AND lower(column_name) = lower(?)`, 724 tableName, columnName) 725 if err != nil { 726 return nil, err 727 } 728 return &columnInfo, nil 729 } 730 return nil, errors.New("Driver not supported for this method") 731 } 732 733 // IsVarchar returns true if the column type matches one of the varchar types 734 // either in MySQL or PostgreSQL. 735 func (ss *SqlStore) IsVarchar(columnType string) bool { 736 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES && columnType == "character varying" { 737 return true 738 } 739 740 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL && columnType == "varchar" { 741 return true 742 } 743 744 return false 745 } 746 747 func (ss *SqlStore) DoesTriggerExist(triggerName string) bool { 748 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 749 count, err := ss.GetMaster().SelectInt(` 750 SELECT 751 COUNT(0) 752 FROM 753 pg_trigger 754 WHERE 755 tgname = $1 756 `, triggerName) 757 758 if err != nil { 759 mlog.Critical("Failed to check if trigger exists", mlog.Err(err)) 760 time.Sleep(time.Second) 761 os.Exit(ExitGenericFailure) 762 } 763 764 return count > 0 765 766 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 767 count, err := ss.GetMaster().SelectInt(` 768 SELECT 769 COUNT(0) 770 FROM 771 information_schema.triggers 772 WHERE 773 trigger_schema = DATABASE() 774 AND trigger_name = ? 775 `, triggerName) 776 777 if err != nil { 778 mlog.Critical("Failed to check if trigger exists", mlog.Err(err)) 779 time.Sleep(time.Second) 780 os.Exit(ExitGenericFailure) 781 } 782 783 return count > 0 784 785 } else { 786 mlog.Critical("Failed to check if column exists because of missing driver") 787 time.Sleep(time.Second) 788 os.Exit(ExitGenericFailure) 789 return false 790 } 791 } 792 793 func (ss *SqlStore) CreateColumnIfNotExists(tableName string, columnName string, mySqlColType string, postgresColType string, defaultValue string) bool { 794 795 if ss.DoesColumnExist(tableName, columnName) { 796 return false 797 } 798 799 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 800 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'") 801 if err != nil { 802 mlog.Critical("Failed to create column", mlog.Err(err)) 803 time.Sleep(time.Second) 804 os.Exit(ExitCreateColumnPostgres) 805 } 806 807 return true 808 809 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 810 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'") 811 if err != nil { 812 mlog.Critical("Failed to create column", mlog.Err(err)) 813 time.Sleep(time.Second) 814 os.Exit(ExitCreateColumnMySQL) 815 } 816 817 return true 818 819 } else { 820 mlog.Critical("Failed to create column because of missing driver") 821 time.Sleep(time.Second) 822 os.Exit(ExitCreateColumnMissing) 823 return false 824 } 825 } 826 827 func (ss *SqlStore) CreateColumnIfNotExistsNoDefault(tableName string, columnName string, mySqlColType string, postgresColType string) bool { 828 829 if ss.DoesColumnExist(tableName, columnName) { 830 return false 831 } 832 833 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 834 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType) 835 if err != nil { 836 mlog.Critical("Failed to create column", mlog.Err(err)) 837 time.Sleep(time.Second) 838 os.Exit(ExitCreateColumnPostgres) 839 } 840 841 return true 842 843 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 844 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType) 845 if err != nil { 846 mlog.Critical("Failed to create column", mlog.Err(err)) 847 time.Sleep(time.Second) 848 os.Exit(ExitCreateColumnMySQL) 849 } 850 851 return true 852 853 } else { 854 mlog.Critical("Failed to create column because of missing driver") 855 time.Sleep(time.Second) 856 os.Exit(ExitCreateColumnMissing) 857 return false 858 } 859 } 860 861 func (ss *SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool { 862 863 if !ss.DoesColumnExist(tableName, columnName) { 864 return false 865 } 866 867 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " DROP COLUMN " + columnName) 868 if err != nil { 869 mlog.Critical("Failed to drop column", mlog.Err(err)) 870 time.Sleep(time.Second) 871 os.Exit(ExitRemoveColumn) 872 } 873 874 return true 875 } 876 877 func (ss *SqlStore) RemoveTableIfExists(tableName string) bool { 878 if !ss.DoesTableExist(tableName) { 879 return false 880 } 881 882 _, err := ss.GetMaster().ExecNoTimeout("DROP TABLE " + tableName) 883 if err != nil { 884 mlog.Critical("Failed to drop table", mlog.Err(err)) 885 time.Sleep(time.Second) 886 os.Exit(ExitRemoveTable) 887 } 888 889 return true 890 } 891 892 func (ss *SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool { 893 if !ss.DoesColumnExist(tableName, oldColumnName) { 894 return false 895 } 896 897 var err error 898 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 899 _, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType) 900 } else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 901 _, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName) 902 } 903 904 if err != nil { 905 mlog.Critical("Failed to rename column", mlog.Err(err)) 906 time.Sleep(time.Second) 907 os.Exit(ExitRenameColumn) 908 } 909 910 return true 911 } 912 913 func (ss *SqlStore) GetMaxLengthOfColumnIfExists(tableName string, columnName string) string { 914 if !ss.DoesColumnExist(tableName, columnName) { 915 return "" 916 } 917 918 var result string 919 var err error 920 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 921 result, err = ss.GetMaster().SelectStr("SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE table_name = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "'") 922 } else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 923 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) + "'") 924 } 925 926 if err != nil { 927 mlog.Critical("Failed to get max length of column", mlog.Err(err)) 928 time.Sleep(time.Second) 929 os.Exit(ExitMaxColumn) 930 } 931 932 return result 933 } 934 935 func (ss *SqlStore) AlterColumnTypeIfExists(tableName string, columnName string, mySqlColType string, postgresColType string) bool { 936 if !ss.DoesColumnExist(tableName, columnName) { 937 return false 938 } 939 940 var err error 941 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 942 _, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " MODIFY " + columnName + " " + mySqlColType) 943 } else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 944 _, err = ss.GetMaster().ExecNoTimeout("ALTER TABLE " + strings.ToLower(tableName) + " ALTER COLUMN " + strings.ToLower(columnName) + " TYPE " + postgresColType) 945 } 946 947 if err != nil { 948 mlog.Critical("Failed to alter column type", mlog.Err(err)) 949 time.Sleep(time.Second) 950 os.Exit(ExitAlterColumn) 951 } 952 953 return true 954 } 955 956 func (ss *SqlStore) RemoveDefaultIfColumnExists(tableName, columnName string) bool { 957 if !ss.DoesColumnExist(tableName, columnName) { 958 return false 959 } 960 961 _, err := ss.GetMaster().ExecNoTimeout("ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " DROP DEFAULT") 962 if err != nil { 963 mlog.Critical("Failed to drop column default", mlog.String("table", tableName), mlog.String("column", columnName), mlog.Err(err)) 964 time.Sleep(time.Second) 965 os.Exit(ExitGenericFailure) 966 return false 967 } 968 969 return true 970 } 971 972 func (ss *SqlStore) AlterDefaultIfColumnExists(tableName string, columnName string, mySqlColDefault *string, postgresColDefault *string) bool { 973 if !ss.DoesColumnExist(tableName, columnName) { 974 return false 975 } 976 977 var defaultValue string 978 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 979 // Some column types in MySQL cannot have defaults, so don't try to configure anything. 980 if mySqlColDefault == nil { 981 return true 982 } 983 984 defaultValue = *mySqlColDefault 985 } else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 986 // Postgres doesn't have the same limitation, but preserve the interface. 987 if postgresColDefault == nil { 988 return true 989 } 990 991 tableName = strings.ToLower(tableName) 992 columnName = strings.ToLower(columnName) 993 defaultValue = *postgresColDefault 994 } else { 995 mlog.Critical("Failed to alter column default because of missing driver") 996 time.Sleep(time.Second) 997 os.Exit(ExitGenericFailure) 998 return false 999 } 1000 1001 if defaultValue == "" { 1002 defaultValue = "''" 1003 } 1004 1005 query := "ALTER TABLE " + tableName + " ALTER COLUMN " + columnName + " SET DEFAULT " + defaultValue 1006 _, err := ss.GetMaster().ExecNoTimeout(query) 1007 if err != nil { 1008 mlog.Critical("Failed to alter column default", mlog.String("table", tableName), mlog.String("column", columnName), mlog.String("default value", defaultValue), mlog.Err(err)) 1009 time.Sleep(time.Second) 1010 os.Exit(ExitGenericFailure) 1011 return false 1012 } 1013 1014 return true 1015 } 1016 1017 func (ss *SqlStore) AlterPrimaryKey(tableName string, columnNames []string) bool { 1018 var currentPrimaryKey string 1019 var err error 1020 // get the current primary key as a comma separated list of columns 1021 switch ss.DriverName() { 1022 case model.DATABASE_DRIVER_MYSQL: 1023 query := ` 1024 SELECT GROUP_CONCAT(column_name ORDER BY seq_in_index) AS PK 1025 FROM 1026 information_schema.statistics 1027 WHERE 1028 table_schema = DATABASE() 1029 AND table_name = ? 1030 AND index_name = 'PRIMARY' 1031 GROUP BY 1032 index_name` 1033 currentPrimaryKey, err = ss.GetMaster().SelectStr(query, tableName) 1034 case model.DATABASE_DRIVER_POSTGRES: 1035 query := ` 1036 SELECT string_agg(a.attname, ',') AS pk 1037 FROM 1038 pg_constraint AS c 1039 CROSS JOIN 1040 (SELECT unnest(conkey) FROM pg_constraint WHERE conrelid='` + strings.ToLower(tableName) + `'::REGCLASS AND contype='p') AS cols(colnum) 1041 INNER JOIN 1042 pg_attribute AS a ON a.attrelid = c.conrelid 1043 AND cols.colnum = a.attnum 1044 WHERE 1045 c.contype = 'p' 1046 AND c.conrelid = '` + strings.ToLower(tableName) + `'::REGCLASS` 1047 currentPrimaryKey, err = ss.GetMaster().SelectStr(query) 1048 } 1049 if err != nil { 1050 mlog.Critical("Failed to get current primary key", mlog.String("table", tableName), mlog.Err(err)) 1051 time.Sleep(time.Second) 1052 os.Exit(ExitAlterPrimaryKey) 1053 } 1054 1055 primaryKey := strings.Join(columnNames, ",") 1056 if strings.EqualFold(currentPrimaryKey, primaryKey) { 1057 return false 1058 } 1059 // alter primary key 1060 var alterQuery string 1061 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 1062 alterQuery = "ALTER TABLE " + tableName + " DROP PRIMARY KEY, ADD PRIMARY KEY (" + primaryKey + ")" 1063 } else if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1064 alterQuery = "ALTER TABLE " + tableName + " DROP CONSTRAINT " + strings.ToLower(tableName) + "_pkey, ADD PRIMARY KEY (" + strings.ToLower(primaryKey) + ")" 1065 } 1066 _, err = ss.GetMaster().ExecNoTimeout(alterQuery) 1067 if err != nil { 1068 mlog.Critical("Failed to alter primary key", mlog.String("table", tableName), mlog.Err(err)) 1069 time.Sleep(time.Second) 1070 os.Exit(ExitAlterPrimaryKey) 1071 } 1072 return true 1073 } 1074 1075 func (ss *SqlStore) CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool { 1076 return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, IndexTypeDefault, true) 1077 } 1078 1079 func (ss *SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) bool { 1080 return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, IndexTypeDefault, false) 1081 } 1082 1083 func (ss *SqlStore) CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool { 1084 return ss.createIndexIfNotExists(indexName, tableName, columnNames, IndexTypeDefault, false) 1085 } 1086 1087 func (ss *SqlStore) CreateUniqueCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool { 1088 return ss.createIndexIfNotExists(indexName, tableName, columnNames, IndexTypeDefault, true) 1089 } 1090 1091 func (ss *SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool { 1092 return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, IndexTypeFullText, false) 1093 } 1094 1095 func (ss *SqlStore) CreateFullTextFuncIndexIfNotExists(indexName string, tableName string, function string) bool { 1096 return ss.createIndexIfNotExists(indexName, tableName, []string{function}, IndexTypeFullTextFunc, false) 1097 } 1098 1099 func (ss *SqlStore) createIndexIfNotExists(indexName string, tableName string, columnNames []string, indexType string, unique bool) bool { 1100 1101 uniqueStr := "" 1102 if unique { 1103 uniqueStr = "UNIQUE " 1104 } 1105 1106 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1107 _, errExists := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName) 1108 // It should fail if the index does not exist 1109 if errExists == nil { 1110 return false 1111 } 1112 1113 query := "" 1114 if indexType == IndexTypeFullText { 1115 if len(columnNames) != 1 { 1116 mlog.Critical("Unable to create multi column full text index") 1117 os.Exit(ExitCreateIndexPostgres) 1118 } 1119 columnName := columnNames[0] 1120 postgresColumnNames := convertMySQLFullTextColumnsToPostgres(columnName) 1121 query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + postgresColumnNames + "))" 1122 } else if indexType == IndexTypeFullTextFunc { 1123 if len(columnNames) != 1 { 1124 mlog.Critical("Unable to create multi column full text index") 1125 os.Exit(ExitCreateIndexPostgres) 1126 } 1127 columnName := columnNames[0] 1128 query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + columnName + "))" 1129 } else { 1130 query = "CREATE " + uniqueStr + "INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")" 1131 } 1132 1133 _, err := ss.GetMaster().ExecNoTimeout(query) 1134 if err != nil { 1135 mlog.Critical("Failed to create index", mlog.Err(errExists), mlog.Err(err)) 1136 time.Sleep(time.Second) 1137 os.Exit(ExitCreateIndexPostgres) 1138 } 1139 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 1140 1141 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) 1142 if err != nil { 1143 mlog.Critical("Failed to check index", mlog.Err(err)) 1144 time.Sleep(time.Second) 1145 os.Exit(ExitCreateIndexMySQL) 1146 } 1147 1148 if count > 0 { 1149 return false 1150 } 1151 1152 fullTextIndex := "" 1153 if indexType == IndexTypeFullText { 1154 fullTextIndex = " FULLTEXT " 1155 } 1156 1157 _, err = ss.GetMaster().ExecNoTimeout("CREATE " + uniqueStr + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + strings.Join(columnNames, ", ") + ")") 1158 if err != nil { 1159 mlog.Critical("Failed to create index", mlog.String("table", tableName), mlog.String("index_name", indexName), mlog.Err(err)) 1160 time.Sleep(time.Second) 1161 os.Exit(ExitCreateIndexFullMySQL) 1162 } 1163 } else { 1164 mlog.Critical("Failed to create index because of missing driver") 1165 time.Sleep(time.Second) 1166 os.Exit(ExitCreateIndexMissing) 1167 } 1168 1169 return true 1170 } 1171 1172 func (ss *SqlStore) CreateForeignKeyIfNotExists( 1173 tableName, columnName, refTableName, refColumnName string, 1174 onDeleteCascade bool, 1175 ) (err error) { 1176 deleteClause := "" 1177 if onDeleteCascade { 1178 deleteClause = "ON DELETE CASCADE" 1179 } 1180 constraintName := "FK_" + tableName + "_" + refTableName 1181 sQuery := ` 1182 ALTER TABLE ` + tableName + ` 1183 ADD CONSTRAINT ` + constraintName + ` 1184 FOREIGN KEY (` + columnName + `) REFERENCES ` + refTableName + ` (` + refColumnName + `) 1185 ` + deleteClause + `;` 1186 _, err = ss.GetMaster().ExecNoTimeout(sQuery) 1187 if IsConstraintAlreadyExistsError(err) { 1188 err = nil 1189 } 1190 if err != nil { 1191 mlog.Warn("Could not create foreign key: " + err.Error()) 1192 } 1193 return 1194 } 1195 1196 func (ss *SqlStore) RemoveIndexIfExists(indexName string, tableName string) bool { 1197 1198 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1199 _, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName) 1200 // It should fail if the index does not exist 1201 if err != nil { 1202 return false 1203 } 1204 1205 _, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName) 1206 if err != nil { 1207 mlog.Critical("Failed to remove index", mlog.Err(err)) 1208 time.Sleep(time.Second) 1209 os.Exit(ExitRemoveIndexPostgres) 1210 } 1211 1212 return true 1213 } else if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 1214 1215 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) 1216 if err != nil { 1217 mlog.Critical("Failed to check index", mlog.Err(err)) 1218 time.Sleep(time.Second) 1219 os.Exit(ExitRemoveIndexMySQL) 1220 } 1221 1222 if count <= 0 { 1223 return false 1224 } 1225 1226 _, err = ss.GetMaster().ExecNoTimeout("DROP INDEX " + indexName + " ON " + tableName) 1227 if err != nil { 1228 mlog.Critical("Failed to remove index", mlog.Err(err)) 1229 time.Sleep(time.Second) 1230 os.Exit(ExitRemoveIndexMySQL) 1231 } 1232 } else { 1233 mlog.Critical("Failed to create index because of missing driver") 1234 time.Sleep(time.Second) 1235 os.Exit(ExitRemoveIndexMissing) 1236 } 1237 1238 return true 1239 } 1240 1241 func IsConstraintAlreadyExistsError(err error) bool { 1242 switch dbErr := err.(type) { 1243 case *pq.Error: 1244 if dbErr.Code == PGDuplicateObjectErrorCode { 1245 return true 1246 } 1247 case *mysql.MySQLError: 1248 if dbErr.Number == MySQLDuplicateObjectErrorCode { 1249 return true 1250 } 1251 } 1252 return false 1253 } 1254 1255 func IsUniqueConstraintError(err error, indexName []string) bool { 1256 unique := false 1257 if pqErr, ok := err.(*pq.Error); ok && pqErr.Code == "23505" { 1258 unique = true 1259 } 1260 1261 if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1062 { 1262 unique = true 1263 } 1264 1265 field := false 1266 for _, contain := range indexName { 1267 if strings.Contains(err.Error(), contain) { 1268 field = true 1269 break 1270 } 1271 } 1272 1273 return unique && field 1274 } 1275 1276 func (ss *SqlStore) GetAllConns() []*gorp.DbMap { 1277 all := make([]*gorp.DbMap, len(ss.Replicas)+1) 1278 copy(all, ss.Replicas) 1279 all[len(ss.Replicas)] = ss.master 1280 return all 1281 } 1282 1283 // RecycleDBConnections closes active connections by setting the max conn lifetime 1284 // to d, and then resets them back to their original duration. 1285 func (ss *SqlStore) RecycleDBConnections(d time.Duration) { 1286 // Get old time. 1287 originalDuration := time.Duration(*ss.settings.ConnMaxLifetimeMilliseconds) * time.Millisecond 1288 // Set the max lifetimes for all connections. 1289 for _, conn := range ss.GetAllConns() { 1290 conn.Db.SetConnMaxLifetime(d) 1291 } 1292 // Wait for that period with an additional 2 seconds of scheduling delay. 1293 time.Sleep(d + 2*time.Second) 1294 // Reset max lifetime back to original value. 1295 for _, conn := range ss.GetAllConns() { 1296 conn.Db.SetConnMaxLifetime(originalDuration) 1297 } 1298 } 1299 1300 func (ss *SqlStore) Close() { 1301 ss.master.Db.Close() 1302 for _, replica := range ss.Replicas { 1303 replica.Db.Close() 1304 } 1305 1306 for _, replica := range ss.searchReplicas { 1307 replica.Db.Close() 1308 } 1309 } 1310 1311 func (ss *SqlStore) LockToMaster() { 1312 ss.lockedToMaster = true 1313 } 1314 1315 func (ss *SqlStore) UnlockFromMaster() { 1316 ss.lockedToMaster = false 1317 } 1318 1319 func (ss *SqlStore) Team() store.TeamStore { 1320 return ss.stores.team 1321 } 1322 1323 func (ss *SqlStore) Channel() store.ChannelStore { 1324 return ss.stores.channel 1325 } 1326 1327 func (ss *SqlStore) Post() store.PostStore { 1328 return ss.stores.post 1329 } 1330 1331 func (ss *SqlStore) RetentionPolicy() store.RetentionPolicyStore { 1332 return ss.stores.retentionPolicy 1333 } 1334 1335 func (ss *SqlStore) User() store.UserStore { 1336 return ss.stores.user 1337 } 1338 1339 func (ss *SqlStore) Bot() store.BotStore { 1340 return ss.stores.bot 1341 } 1342 1343 func (ss *SqlStore) Session() store.SessionStore { 1344 return ss.stores.session 1345 } 1346 1347 func (ss *SqlStore) Audit() store.AuditStore { 1348 return ss.stores.audit 1349 } 1350 1351 func (ss *SqlStore) ClusterDiscovery() store.ClusterDiscoveryStore { 1352 return ss.stores.cluster 1353 } 1354 1355 func (ss *SqlStore) RemoteCluster() store.RemoteClusterStore { 1356 return ss.stores.remoteCluster 1357 } 1358 1359 func (ss *SqlStore) Compliance() store.ComplianceStore { 1360 return ss.stores.compliance 1361 } 1362 1363 func (ss *SqlStore) OAuth() store.OAuthStore { 1364 return ss.stores.oauth 1365 } 1366 1367 func (ss *SqlStore) System() store.SystemStore { 1368 return ss.stores.system 1369 } 1370 1371 func (ss *SqlStore) Webhook() store.WebhookStore { 1372 return ss.stores.webhook 1373 } 1374 1375 func (ss *SqlStore) Command() store.CommandStore { 1376 return ss.stores.command 1377 } 1378 1379 func (ss *SqlStore) CommandWebhook() store.CommandWebhookStore { 1380 return ss.stores.commandWebhook 1381 } 1382 1383 func (ss *SqlStore) Preference() store.PreferenceStore { 1384 return ss.stores.preference 1385 } 1386 1387 func (ss *SqlStore) License() store.LicenseStore { 1388 return ss.stores.license 1389 } 1390 1391 func (ss *SqlStore) Token() store.TokenStore { 1392 return ss.stores.token 1393 } 1394 1395 func (ss *SqlStore) Emoji() store.EmojiStore { 1396 return ss.stores.emoji 1397 } 1398 1399 func (ss *SqlStore) Status() store.StatusStore { 1400 return ss.stores.status 1401 } 1402 1403 func (ss *SqlStore) FileInfo() store.FileInfoStore { 1404 return ss.stores.fileInfo 1405 } 1406 1407 func (ss *SqlStore) UploadSession() store.UploadSessionStore { 1408 return ss.stores.uploadSession 1409 } 1410 1411 func (ss *SqlStore) Reaction() store.ReactionStore { 1412 return ss.stores.reaction 1413 } 1414 1415 func (ss *SqlStore) Job() store.JobStore { 1416 return ss.stores.job 1417 } 1418 1419 func (ss *SqlStore) UserAccessToken() store.UserAccessTokenStore { 1420 return ss.stores.userAccessToken 1421 } 1422 1423 func (ss *SqlStore) ChannelMemberHistory() store.ChannelMemberHistoryStore { 1424 return ss.stores.channelMemberHistory 1425 } 1426 1427 func (ss *SqlStore) Plugin() store.PluginStore { 1428 return ss.stores.plugin 1429 } 1430 1431 func (ss *SqlStore) Thread() store.ThreadStore { 1432 return ss.stores.thread 1433 } 1434 1435 func (ss *SqlStore) Role() store.RoleStore { 1436 return ss.stores.role 1437 } 1438 1439 func (ss *SqlStore) TermsOfService() store.TermsOfServiceStore { 1440 return ss.stores.TermsOfService 1441 } 1442 1443 func (ss *SqlStore) ProductNotices() store.ProductNoticesStore { 1444 return ss.stores.productNotices 1445 } 1446 1447 func (ss *SqlStore) UserTermsOfService() store.UserTermsOfServiceStore { 1448 return ss.stores.UserTermsOfService 1449 } 1450 1451 func (ss *SqlStore) Scheme() store.SchemeStore { 1452 return ss.stores.scheme 1453 } 1454 1455 func (ss *SqlStore) Group() store.GroupStore { 1456 return ss.stores.group 1457 } 1458 1459 func (ss *SqlStore) LinkMetadata() store.LinkMetadataStore { 1460 return ss.stores.linkMetadata 1461 } 1462 1463 func (ss *SqlStore) SharedChannel() store.SharedChannelStore { 1464 return ss.stores.sharedchannel 1465 } 1466 1467 func (ss *SqlStore) DropAllTables() { 1468 ss.master.TruncateTables() 1469 } 1470 1471 func (ss *SqlStore) getQueryBuilder() sq.StatementBuilderType { 1472 builder := sq.StatementBuilder.PlaceholderFormat(sq.Question) 1473 if ss.DriverName() == model.DATABASE_DRIVER_POSTGRES { 1474 builder = builder.PlaceholderFormat(sq.Dollar) 1475 } 1476 return builder 1477 } 1478 1479 func (ss *SqlStore) CheckIntegrity() <-chan model.IntegrityCheckResult { 1480 results := make(chan model.IntegrityCheckResult) 1481 go CheckRelationalIntegrity(ss, results) 1482 return results 1483 } 1484 1485 func (ss *SqlStore) UpdateLicense(license *model.License) { 1486 ss.licenseMutex.Lock() 1487 defer ss.licenseMutex.Unlock() 1488 ss.license = license 1489 } 1490 1491 func (ss *SqlStore) GetLicense() *model.License { 1492 return ss.license 1493 } 1494 1495 func (ss *SqlStore) migrate(direction migrationDirection) error { 1496 var driver database.Driver 1497 var err error 1498 1499 // When WithInstance is used in golang-migrate, the underlying driver connections are not tracked. 1500 // So we will have to open a fresh connection for migrations and explicitly close it when all is done. 1501 dataSource, err := ss.appendMultipleStatementsFlag(*ss.settings.DataSource) 1502 if err != nil { 1503 return err 1504 } 1505 conn := setupConnection("migrations", dataSource, ss.settings) 1506 defer conn.Db.Close() 1507 1508 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 1509 driver, err = mysqlmigrate.WithInstance(conn.Db, &mysqlmigrate.Config{}) 1510 if err != nil { 1511 return err 1512 } 1513 } else { 1514 driver, err = postgres.WithInstance(conn.Db, &postgres.Config{}) 1515 if err != nil { 1516 return err 1517 } 1518 } 1519 1520 var assetNamesForDriver []string 1521 for _, assetName := range migrations.AssetNames() { 1522 if strings.HasPrefix(assetName, ss.DriverName()) { 1523 assetNamesForDriver = append(assetNamesForDriver, filepath.Base(assetName)) 1524 } 1525 } 1526 1527 source := bindata.Resource(assetNamesForDriver, func(name string) ([]byte, error) { 1528 return migrations.Asset(filepath.Join(ss.DriverName(), name)) 1529 }) 1530 1531 sourceDriver, err := bindata.WithInstance(source) 1532 if err != nil { 1533 return err 1534 } 1535 1536 migrations, err := migrate.NewWithInstance("go-bindata", 1537 sourceDriver, 1538 ss.DriverName(), 1539 driver) 1540 1541 if err != nil { 1542 return err 1543 } 1544 defer migrations.Close() 1545 1546 switch direction { 1547 case migrationsDirectionUp: 1548 err = migrations.Up() 1549 case migrationsDirectionDown: 1550 err = migrations.Down() 1551 default: 1552 return errors.New(fmt.Sprintf("unsupported migration direction %s", direction)) 1553 } 1554 1555 if err != nil && err != migrate.ErrNoChange && !errors.Is(err, os.ErrNotExist) { 1556 return err 1557 } 1558 1559 return nil 1560 } 1561 1562 func (ss *SqlStore) appendMultipleStatementsFlag(dataSource string) (string, error) { 1563 // We need to tell the MySQL driver that we want to use multiStatements 1564 // in order to make migrations work. 1565 if ss.DriverName() == model.DATABASE_DRIVER_MYSQL { 1566 config, err := mysql.ParseDSN(dataSource) 1567 if err != nil { 1568 return "", err 1569 } 1570 1571 if config.Params == nil { 1572 config.Params = map[string]string{} 1573 } 1574 1575 config.Params["multiStatements"] = "true" 1576 return config.FormatDSN(), nil 1577 } 1578 1579 return dataSource, nil 1580 } 1581 1582 func resetReadTimeout(dataSource string) (string, error) { 1583 config, err := mysql.ParseDSN(dataSource) 1584 if err != nil { 1585 return "", err 1586 } 1587 config.ReadTimeout = 0 1588 return config.FormatDSN(), nil 1589 } 1590 1591 type mattermConverter struct{} 1592 1593 func (me mattermConverter) ToDb(val interface{}) (interface{}, error) { 1594 1595 switch t := val.(type) { 1596 case model.StringMap: 1597 return model.MapToJson(t), nil 1598 case map[string]string: 1599 return model.MapToJson(model.StringMap(t)), nil 1600 case model.StringArray: 1601 return model.ArrayToJson(t), nil 1602 case model.StringInterface: 1603 return model.StringInterfaceToJson(t), nil 1604 case map[string]interface{}: 1605 return model.StringInterfaceToJson(model.StringInterface(t)), nil 1606 case JSONSerializable: 1607 return t.ToJson(), nil 1608 case *opengraph.OpenGraph: 1609 return json.Marshal(t) 1610 } 1611 1612 return val, nil 1613 } 1614 1615 func (me mattermConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { 1616 switch target.(type) { 1617 case *model.StringMap: 1618 binder := func(holder, target interface{}) error { 1619 s, ok := holder.(*string) 1620 if !ok { 1621 return errors.New(i18n.T("store.sql.convert_string_map")) 1622 } 1623 b := []byte(*s) 1624 return json.Unmarshal(b, target) 1625 } 1626 return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true 1627 case *map[string]string: 1628 binder := func(holder, target interface{}) error { 1629 s, ok := holder.(*string) 1630 if !ok { 1631 return errors.New(i18n.T("store.sql.convert_string_map")) 1632 } 1633 b := []byte(*s) 1634 return json.Unmarshal(b, target) 1635 } 1636 return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true 1637 case *model.StringArray: 1638 binder := func(holder, target interface{}) error { 1639 s, ok := holder.(*string) 1640 if !ok { 1641 return errors.New(i18n.T("store.sql.convert_string_array")) 1642 } 1643 b := []byte(*s) 1644 return json.Unmarshal(b, target) 1645 } 1646 return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true 1647 case *model.StringInterface: 1648 binder := func(holder, target interface{}) error { 1649 s, ok := holder.(*string) 1650 if !ok { 1651 return errors.New(i18n.T("store.sql.convert_string_interface")) 1652 } 1653 b := []byte(*s) 1654 return json.Unmarshal(b, target) 1655 } 1656 return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true 1657 case *map[string]interface{}: 1658 binder := func(holder, target interface{}) error { 1659 s, ok := holder.(*string) 1660 if !ok { 1661 return errors.New(i18n.T("store.sql.convert_string_interface")) 1662 } 1663 b := []byte(*s) 1664 return json.Unmarshal(b, target) 1665 } 1666 return gorp.CustomScanner{Holder: new(string), Target: target, Binder: binder}, true 1667 } 1668 1669 return gorp.CustomScanner{}, false 1670 } 1671 1672 type JSONSerializable interface { 1673 ToJson() string 1674 } 1675 1676 func convertMySQLFullTextColumnsToPostgres(columnNames string) string { 1677 columns := strings.Split(columnNames, ", ") 1678 concatenatedColumnNames := "" 1679 for i, c := range columns { 1680 concatenatedColumnNames += c 1681 if i < len(columns)-1 { 1682 concatenatedColumnNames += " || ' ' || " 1683 } 1684 } 1685 1686 return concatenatedColumnNames 1687 } 1688 1689 // IsDuplicate checks whether an error is a duplicate key error, which comes when processes are competing on creating the same 1690 // tables in the database. 1691 func IsDuplicate(err error) bool { 1692 var pqErr *pq.Error 1693 var mysqlErr *mysql.MySQLError 1694 switch { 1695 case errors.As(errors.Cause(err), &pqErr): 1696 if pqErr.Code == PGDupTableErrorCode { 1697 return true 1698 } 1699 case errors.As(errors.Cause(err), &mysqlErr): 1700 if mysqlErr.Number == MySQLDupTableErrorCode { 1701 return true 1702 } 1703 } 1704 1705 return false 1706 } 1707 1708 // VersionString converts an integer representation of a DB version 1709 // to a pretty-printed string. 1710 // Postgres doesn't follow three-part version numbers from 10.0 onwards: 1711 // https://www.postgresql.org/docs/13/libpq-status.html#LIBPQ-PQSERVERVERSION. 1712 func VersionString(v int) string { 1713 minor := v % 10000 1714 major := v / 10000 1715 return strconv.Itoa(major) + "." + strconv.Itoa(minor) 1716 }