code.gitea.io/gitea@v1.22.3/models/migrations/base/db.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package base 5 6 import ( 7 "context" 8 "database/sql" 9 "errors" 10 "fmt" 11 "os" 12 "path" 13 "reflect" 14 "regexp" 15 "strings" 16 "time" 17 18 "code.gitea.io/gitea/models/db" 19 "code.gitea.io/gitea/models/unittest" 20 "code.gitea.io/gitea/modules/log" 21 "code.gitea.io/gitea/modules/setting" 22 "code.gitea.io/gitea/modules/util" 23 24 "xorm.io/xorm" 25 "xorm.io/xorm/schemas" 26 ) 27 28 // RecreateTables will recreate the tables for the provided beans using the newly provided bean definition and move all data to that new table 29 // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION 30 func RecreateTables(beans ...any) func(*xorm.Engine) error { 31 return func(x *xorm.Engine) error { 32 sess := x.NewSession() 33 defer sess.Close() 34 if err := sess.Begin(); err != nil { 35 return err 36 } 37 sess = sess.StoreEngine("InnoDB") 38 for _, bean := range beans { 39 log.Info("Recreating Table: %s for Bean: %s", x.TableName(bean), reflect.Indirect(reflect.ValueOf(bean)).Type().Name()) 40 if err := RecreateTable(sess, bean); err != nil { 41 return err 42 } 43 } 44 return sess.Commit() 45 } 46 } 47 48 // RecreateTable will recreate the table using the newly provided bean definition and move all data to that new table 49 // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION 50 // WARNING: YOU MUST COMMIT THE SESSION AT THE END 51 func RecreateTable(sess *xorm.Session, bean any) error { 52 // TODO: This will not work if there are foreign keys 53 54 tableName := sess.Engine().TableName(bean) 55 tempTableName := fmt.Sprintf("tmp_recreate__%s", tableName) 56 57 // We need to move the old table away and create a new one with the correct columns 58 // We will need to do this in stages to prevent data loss 59 // 60 // First create the temporary table 61 if err := sess.Table(tempTableName).CreateTable(bean); err != nil { 62 log.Error("Unable to create table %s. Error: %v", tempTableName, err) 63 return err 64 } 65 66 if err := sess.Table(tempTableName).CreateUniques(bean); err != nil { 67 log.Error("Unable to create uniques for table %s. Error: %v", tempTableName, err) 68 return err 69 } 70 71 if err := sess.Table(tempTableName).CreateIndexes(bean); err != nil { 72 log.Error("Unable to create indexes for table %s. Error: %v", tempTableName, err) 73 return err 74 } 75 76 // Work out the column names from the bean - these are the columns to select from the old table and install into the new table 77 table, err := sess.Engine().TableInfo(bean) 78 if err != nil { 79 log.Error("Unable to get table info. Error: %v", err) 80 81 return err 82 } 83 newTableColumns := table.Columns() 84 if len(newTableColumns) == 0 { 85 return fmt.Errorf("no columns in new table") 86 } 87 hasID := false 88 for _, column := range newTableColumns { 89 hasID = hasID || (column.IsPrimaryKey && column.IsAutoIncrement) 90 } 91 92 if hasID && setting.Database.Type.IsMSSQL() { 93 if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` ON", tempTableName)); err != nil { 94 log.Error("Unable to set identity insert for table %s. Error: %v", tempTableName, err) 95 return err 96 } 97 } 98 99 sqlStringBuilder := &strings.Builder{} 100 _, _ = sqlStringBuilder.WriteString("INSERT INTO `") 101 _, _ = sqlStringBuilder.WriteString(tempTableName) 102 _, _ = sqlStringBuilder.WriteString("` (`") 103 _, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name) 104 _, _ = sqlStringBuilder.WriteString("`") 105 for _, column := range newTableColumns[1:] { 106 _, _ = sqlStringBuilder.WriteString(", `") 107 _, _ = sqlStringBuilder.WriteString(column.Name) 108 _, _ = sqlStringBuilder.WriteString("`") 109 } 110 _, _ = sqlStringBuilder.WriteString(")") 111 _, _ = sqlStringBuilder.WriteString(" SELECT ") 112 if newTableColumns[0].Default != "" { 113 _, _ = sqlStringBuilder.WriteString("COALESCE(`") 114 _, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name) 115 _, _ = sqlStringBuilder.WriteString("`, ") 116 _, _ = sqlStringBuilder.WriteString(newTableColumns[0].Default) 117 _, _ = sqlStringBuilder.WriteString(")") 118 } else { 119 _, _ = sqlStringBuilder.WriteString("`") 120 _, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name) 121 _, _ = sqlStringBuilder.WriteString("`") 122 } 123 124 for _, column := range newTableColumns[1:] { 125 if column.Default != "" { 126 _, _ = sqlStringBuilder.WriteString(", COALESCE(`") 127 _, _ = sqlStringBuilder.WriteString(column.Name) 128 _, _ = sqlStringBuilder.WriteString("`, ") 129 _, _ = sqlStringBuilder.WriteString(column.Default) 130 _, _ = sqlStringBuilder.WriteString(")") 131 } else { 132 _, _ = sqlStringBuilder.WriteString(", `") 133 _, _ = sqlStringBuilder.WriteString(column.Name) 134 _, _ = sqlStringBuilder.WriteString("`") 135 } 136 } 137 _, _ = sqlStringBuilder.WriteString(" FROM `") 138 _, _ = sqlStringBuilder.WriteString(tableName) 139 _, _ = sqlStringBuilder.WriteString("`") 140 141 if _, err := sess.Exec(sqlStringBuilder.String()); err != nil { 142 log.Error("Unable to set copy data in to temp table %s. Error: %v", tempTableName, err) 143 return err 144 } 145 146 if hasID && setting.Database.Type.IsMSSQL() { 147 if _, err := sess.Exec(fmt.Sprintf("SET IDENTITY_INSERT `%s` OFF", tempTableName)); err != nil { 148 log.Error("Unable to switch off identity insert for table %s. Error: %v", tempTableName, err) 149 return err 150 } 151 } 152 153 switch { 154 case setting.Database.Type.IsSQLite3(): 155 // SQLite will drop all the constraints on the old table 156 if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { 157 log.Error("Unable to drop old table %s. Error: %v", tableName, err) 158 return err 159 } 160 161 if err := sess.Table(tempTableName).DropIndexes(bean); err != nil { 162 log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err) 163 return err 164 } 165 166 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil { 167 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 168 return err 169 } 170 171 if err := sess.Table(tableName).CreateIndexes(bean); err != nil { 172 log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err) 173 return err 174 } 175 176 if err := sess.Table(tableName).CreateUniques(bean); err != nil { 177 log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) 178 return err 179 } 180 case setting.Database.Type.IsMySQL(): 181 // MySQL will drop all the constraints on the old table 182 if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { 183 log.Error("Unable to drop old table %s. Error: %v", tableName, err) 184 return err 185 } 186 187 if err := sess.Table(tempTableName).DropIndexes(bean); err != nil { 188 log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err) 189 return err 190 } 191 192 // SQLite and MySQL will move all the constraints from the temporary table to the new table 193 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil { 194 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 195 return err 196 } 197 198 if err := sess.Table(tableName).CreateIndexes(bean); err != nil { 199 log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err) 200 return err 201 } 202 203 if err := sess.Table(tableName).CreateUniques(bean); err != nil { 204 log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) 205 return err 206 } 207 case setting.Database.Type.IsPostgreSQL(): 208 var originalSequences []string 209 type sequenceData struct { 210 LastValue int `xorm:"'last_value'"` 211 IsCalled bool `xorm:"'is_called'"` 212 } 213 sequenceMap := map[string]sequenceData{} 214 215 schema := sess.Engine().Dialect().URI().Schema 216 sess.Engine().SetSchema("") 217 if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&originalSequences); err != nil { 218 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 219 return err 220 } 221 sess.Engine().SetSchema(schema) 222 223 for _, sequence := range originalSequences { 224 sequenceData := sequenceData{} 225 if _, err := sess.Table(sequence).Cols("last_value", "is_called").Get(&sequenceData); err != nil { 226 log.Error("Unable to get last_value and is_called from %s. Error: %v", sequence, err) 227 return err 228 } 229 sequenceMap[sequence] = sequenceData 230 } 231 232 // CASCADE causes postgres to drop all the constraints on the old table 233 if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s` CASCADE", tableName)); err != nil { 234 log.Error("Unable to drop old table %s. Error: %v", tableName, err) 235 return err 236 } 237 238 // CASCADE causes postgres to move all the constraints from the temporary table to the new table 239 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil { 240 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 241 return err 242 } 243 244 var indices []string 245 sess.Engine().SetSchema("") 246 if err := sess.Table("pg_indexes").Cols("indexname").Where("tablename = ? ", tableName).Find(&indices); err != nil { 247 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 248 return err 249 } 250 sess.Engine().SetSchema(schema) 251 252 for _, index := range indices { 253 newIndexName := strings.Replace(index, "tmp_recreate__", "", 1) 254 if _, err := sess.Exec(fmt.Sprintf("ALTER INDEX `%s` RENAME TO `%s`", index, newIndexName)); err != nil { 255 log.Error("Unable to rename %s to %s. Error: %v", index, newIndexName, err) 256 return err 257 } 258 } 259 260 var sequences []string 261 sess.Engine().SetSchema("") 262 if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__' || ? || '_%' AND sequence_catalog = ?", tableName, setting.Database.Name).Find(&sequences); err != nil { 263 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 264 return err 265 } 266 sess.Engine().SetSchema(schema) 267 268 for _, sequence := range sequences { 269 newSequenceName := strings.Replace(sequence, "tmp_recreate__", "", 1) 270 if _, err := sess.Exec(fmt.Sprintf("ALTER SEQUENCE `%s` RENAME TO `%s`", sequence, newSequenceName)); err != nil { 271 log.Error("Unable to rename %s sequence to %s. Error: %v", sequence, newSequenceName, err) 272 return err 273 } 274 val, ok := sequenceMap[newSequenceName] 275 if newSequenceName == tableName+"_id_seq" { 276 if ok && val.LastValue != 0 { 277 if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', %d, %t)", newSequenceName, val.LastValue, val.IsCalled)); err != nil { 278 log.Error("Unable to reset %s to %d. Error: %v", newSequenceName, val, err) 279 return err 280 } 281 } else { 282 // We're going to try to guess this 283 if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM `%s`), 1), false)", newSequenceName, tableName)); err != nil { 284 log.Error("Unable to reset %s. Error: %v", newSequenceName, err) 285 return err 286 } 287 } 288 } else if ok { 289 if _, err := sess.Exec(fmt.Sprintf("SELECT setval('%s', %d, %t)", newSequenceName, val.LastValue, val.IsCalled)); err != nil { 290 log.Error("Unable to reset %s to %d. Error: %v", newSequenceName, val, err) 291 return err 292 } 293 } 294 } 295 case setting.Database.Type.IsMSSQL(): 296 // MSSQL will drop all the constraints on the old table 297 if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { 298 log.Error("Unable to drop old table %s. Error: %v", tableName, err) 299 return err 300 } 301 302 // MSSQL sp_rename will move all the constraints from the temporary table to the new table 303 if _, err := sess.Exec(fmt.Sprintf("sp_rename `%s`,`%s`", tempTableName, tableName)); err != nil { 304 log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) 305 return err 306 } 307 default: 308 log.Fatal("Unrecognized DB") 309 } 310 return nil 311 } 312 313 // WARNING: YOU MUST COMMIT THE SESSION AT THE END 314 func DropTableColumns(sess *xorm.Session, tableName string, columnNames ...string) (err error) { 315 if tableName == "" || len(columnNames) == 0 { 316 return nil 317 } 318 // TODO: This will not work if there are foreign keys 319 320 switch { 321 case setting.Database.Type.IsSQLite3(): 322 // First drop the indexes on the columns 323 res, errIndex := sess.Query(fmt.Sprintf("PRAGMA index_list(`%s`)", tableName)) 324 if errIndex != nil { 325 return errIndex 326 } 327 for _, row := range res { 328 indexName := row["name"] 329 indexRes, err := sess.Query(fmt.Sprintf("PRAGMA index_info(`%s`)", indexName)) 330 if err != nil { 331 return err 332 } 333 if len(indexRes) != 1 { 334 continue 335 } 336 indexColumn := string(indexRes[0]["name"]) 337 for _, name := range columnNames { 338 if name == indexColumn { 339 _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s`", indexName)) 340 if err != nil { 341 return err 342 } 343 } 344 } 345 } 346 347 // Here we need to get the columns from the original table 348 sql := fmt.Sprintf("SELECT sql FROM sqlite_master WHERE tbl_name='%s' and type='table'", tableName) 349 res, err := sess.Query(sql) 350 if err != nil { 351 return err 352 } 353 tableSQL := string(res[0]["sql"]) 354 355 // Get the string offset for column definitions: `CREATE TABLE ( column-definitions... )` 356 columnDefinitionsIndex := strings.Index(tableSQL, "(") 357 if columnDefinitionsIndex < 0 { 358 return errors.New("couldn't find column definitions") 359 } 360 361 // Separate out the column definitions 362 tableSQL = tableSQL[columnDefinitionsIndex:] 363 364 // Remove the required columnNames 365 for _, name := range columnNames { 366 tableSQL = regexp.MustCompile(regexp.QuoteMeta("`"+name+"`")+"[^`,)]*?[,)]").ReplaceAllString(tableSQL, "") 367 } 368 369 // Ensure the query is ended properly 370 tableSQL = strings.TrimSpace(tableSQL) 371 if tableSQL[len(tableSQL)-1] != ')' { 372 if tableSQL[len(tableSQL)-1] == ',' { 373 tableSQL = tableSQL[:len(tableSQL)-1] 374 } 375 tableSQL += ")" 376 } 377 378 // Find all the columns in the table 379 columns := regexp.MustCompile("`([^`]*)`").FindAllString(tableSQL, -1) 380 381 tableSQL = fmt.Sprintf("CREATE TABLE `new_%s_new` ", tableName) + tableSQL 382 if _, err := sess.Exec(tableSQL); err != nil { 383 return err 384 } 385 386 // Now restore the data 387 columnsSeparated := strings.Join(columns, ",") 388 insertSQL := fmt.Sprintf("INSERT INTO `new_%s_new` (%s) SELECT %s FROM %s", tableName, columnsSeparated, columnsSeparated, tableName) 389 if _, err := sess.Exec(insertSQL); err != nil { 390 return err 391 } 392 393 // Now drop the old table 394 if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { 395 return err 396 } 397 398 // Rename the table 399 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `new_%s_new` RENAME TO `%s`", tableName, tableName)); err != nil { 400 return err 401 } 402 403 case setting.Database.Type.IsPostgreSQL(): 404 cols := "" 405 for _, col := range columnNames { 406 if cols != "" { 407 cols += ", " 408 } 409 cols += "DROP COLUMN `" + col + "` CASCADE" 410 } 411 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { 412 return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) 413 } 414 case setting.Database.Type.IsMySQL(): 415 // Drop indexes on columns first 416 sql := fmt.Sprintf("SHOW INDEX FROM %s WHERE column_name IN ('%s')", tableName, strings.Join(columnNames, "','")) 417 res, err := sess.Query(sql) 418 if err != nil { 419 return err 420 } 421 for _, index := range res { 422 indexName := index["column_name"] 423 if len(indexName) > 0 { 424 _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName)) 425 if err != nil { 426 return err 427 } 428 } 429 } 430 431 // Now drop the columns 432 cols := "" 433 for _, col := range columnNames { 434 if cols != "" { 435 cols += ", " 436 } 437 cols += "DROP COLUMN `" + col + "`" 438 } 439 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` %s", tableName, cols)); err != nil { 440 return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) 441 } 442 case setting.Database.Type.IsMSSQL(): 443 cols := "" 444 for _, col := range columnNames { 445 if cols != "" { 446 cols += ", " 447 } 448 cols += "`" + strings.ToLower(col) + "`" 449 } 450 sql := fmt.Sprintf("SELECT Name FROM sys.default_constraints WHERE parent_object_id = OBJECT_ID('%[1]s') AND parent_column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", 451 tableName, strings.ReplaceAll(cols, "`", "'")) 452 constraints := make([]string, 0) 453 if err := sess.SQL(sql).Find(&constraints); err != nil { 454 return fmt.Errorf("Find constraints: %v", err) 455 } 456 for _, constraint := range constraints { 457 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP CONSTRAINT `%s`", tableName, constraint)); err != nil { 458 return fmt.Errorf("Drop table `%s` default constraint `%s`: %v", tableName, constraint, err) 459 } 460 } 461 sql = fmt.Sprintf("SELECT DISTINCT Name FROM sys.indexes INNER JOIN sys.index_columns ON indexes.index_id = index_columns.index_id AND indexes.object_id = index_columns.object_id WHERE indexes.object_id = OBJECT_ID('%[1]s') AND index_columns.column_id IN (SELECT column_id FROM sys.columns WHERE LOWER(name) IN (%[2]s) AND object_id = OBJECT_ID('%[1]s'))", 462 tableName, strings.ReplaceAll(cols, "`", "'")) 463 constraints = make([]string, 0) 464 if err := sess.SQL(sql).Find(&constraints); err != nil { 465 return fmt.Errorf("Find constraints: %v", err) 466 } 467 for _, constraint := range constraints { 468 if _, err := sess.Exec(fmt.Sprintf("DROP INDEX `%[2]s` ON `%[1]s`", tableName, constraint)); err != nil { 469 return fmt.Errorf("Drop index `%[2]s` on `%[1]s`: %v", tableName, constraint, err) 470 } 471 } 472 473 if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` DROP COLUMN %s", tableName, cols)); err != nil { 474 return fmt.Errorf("Drop table `%s` columns %v: %v", tableName, columnNames, err) 475 } 476 default: 477 log.Fatal("Unrecognized DB") 478 } 479 480 return nil 481 } 482 483 // ModifyColumn will modify column's type or other property. SQLITE is not supported 484 func ModifyColumn(x *xorm.Engine, tableName string, col *schemas.Column) error { 485 var indexes map[string]*schemas.Index 486 var err error 487 // MSSQL have to remove index at first, otherwise alter column will fail 488 // ref. https://sqlzealots.com/2018/05/09/error-message-the-index-is-dependent-on-column-alter-table-alter-column-failed-because-one-or-more-objects-access-this-column/ 489 if x.Dialect().URI().DBType == schemas.MSSQL { 490 indexes, err = x.Dialect().GetIndexes(x.DB(), context.Background(), tableName) 491 if err != nil { 492 return err 493 } 494 495 for _, index := range indexes { 496 _, err = x.Exec(x.Dialect().DropIndexSQL(tableName, index)) 497 if err != nil { 498 return err 499 } 500 } 501 } 502 503 defer func() { 504 for _, index := range indexes { 505 _, err = x.Exec(x.Dialect().CreateIndexSQL(tableName, index)) 506 if err != nil { 507 log.Error("Create index %s on table %s failed: %v", index.Name, tableName, err) 508 } 509 } 510 }() 511 512 alterSQL := x.Dialect().ModifyColumnSQL(tableName, col) 513 if _, err := x.Exec(alterSQL); err != nil { 514 return err 515 } 516 return nil 517 } 518 519 func removeAllWithRetry(dir string) error { 520 var err error 521 for i := 0; i < 20; i++ { 522 err = os.RemoveAll(dir) 523 if err == nil { 524 break 525 } 526 time.Sleep(100 * time.Millisecond) 527 } 528 return err 529 } 530 531 func newXORMEngine() (*xorm.Engine, error) { 532 if err := db.InitEngine(context.Background()); err != nil { 533 return nil, err 534 } 535 x := unittest.GetXORMEngine() 536 return x, nil 537 } 538 539 func deleteDB() error { 540 switch { 541 case setting.Database.Type.IsSQLite3(): 542 if err := util.Remove(setting.Database.Path); err != nil { 543 return err 544 } 545 return os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm) 546 547 case setting.Database.Type.IsMySQL(): 548 db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/", 549 setting.Database.User, setting.Database.Passwd, setting.Database.Host)) 550 if err != nil { 551 return err 552 } 553 defer db.Close() 554 555 if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil { 556 return err 557 } 558 559 if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil { 560 return err 561 } 562 return nil 563 case setting.Database.Type.IsPostgreSQL(): 564 db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/?sslmode=%s", 565 setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.SSLMode)) 566 if err != nil { 567 return err 568 } 569 defer db.Close() 570 571 if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil { 572 return err 573 } 574 575 if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil { 576 return err 577 } 578 db.Close() 579 580 // Check if we need to setup a specific schema 581 if len(setting.Database.Schema) != 0 { 582 db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", 583 setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) 584 if err != nil { 585 return err 586 } 587 defer db.Close() 588 589 schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) 590 if err != nil { 591 return err 592 } 593 defer schrows.Close() 594 595 if !schrows.Next() { 596 // Create and setup a DB schema 597 _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema)) 598 if err != nil { 599 return err 600 } 601 } 602 603 // Make the user's default search path the created schema; this will affect new connections 604 _, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema)) 605 if err != nil { 606 return err 607 } 608 return nil 609 } 610 case setting.Database.Type.IsMSSQL(): 611 host, port := setting.ParseMSSQLHostPort(setting.Database.Host) 612 db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", 613 host, port, "master", setting.Database.User, setting.Database.Passwd)) 614 if err != nil { 615 return err 616 } 617 defer db.Close() 618 619 if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS [%s]", setting.Database.Name)); err != nil { 620 return err 621 } 622 if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE [%s]", setting.Database.Name)); err != nil { 623 return err 624 } 625 } 626 627 return nil 628 }