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