using System; using System.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Initial; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Services; namespace Umbraco.Core.Persistence { public class DatabaseSchemaHelper { private readonly Database _db; private readonly ILogger _logger; private readonly ISqlSyntaxProvider _syntaxProvider; private readonly BaseDataCreation _baseDataCreation; public DatabaseSchemaHelper(Database db, ILogger logger, ISqlSyntaxProvider syntaxProvider) { _db = db; _logger = logger; _syntaxProvider = syntaxProvider; _baseDataCreation = new BaseDataCreation(db, logger); } public bool TableExist(string tableName) { return _syntaxProvider.DoesTableExist(_db, tableName); } internal void UninstallDatabaseSchema() { var creation = new DatabaseSchemaCreation(_db, _logger, _syntaxProvider); creation.UninstallDatabaseSchema(); } /// /// Creates the Umbraco db schema in the Database of the current Database. /// Safe method that is only able to create the schema in non-configured /// umbraco instances. /// public void CreateDatabaseSchema(ApplicationContext applicationContext) { if (applicationContext == null) throw new ArgumentNullException("applicationContext"); CreateDatabaseSchema(true, applicationContext); } /// /// Creates the Umbraco db schema in the Database of the current Database /// with the option to guard the db from having the schema created /// multiple times. /// /// /// public void CreateDatabaseSchema(bool guardConfiguration, ApplicationContext applicationContext) { if (applicationContext == null) throw new ArgumentNullException("applicationContext"); if (guardConfiguration && applicationContext.IsConfigured) throw new Exception("Umbraco is already configured!"); CreateDatabaseSchemaDo(applicationContext.Services.MigrationEntryService); } internal void CreateDatabaseSchemaDo(bool guardConfiguration, ApplicationContext applicationContext) { if (guardConfiguration && applicationContext.IsConfigured) throw new Exception("Umbraco is already configured!"); CreateDatabaseSchemaDo(applicationContext.Services.MigrationEntryService); } internal void CreateDatabaseSchemaDo(IMigrationEntryService migrationEntryService) { _logger.Info("Initializing database schema creation"); var creation = new DatabaseSchemaCreation(_db, _logger, _syntaxProvider); creation.InitializeDatabaseSchema(); //Now ensure to cretae the tag in the db for the current migration version migrationEntryService.CreateEntry(GlobalSettings.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion()); _logger.Info("Finalized database schema creation"); } public void CreateTable(bool overwrite) where T : new() { var tableType = typeof(T); CreateTable(overwrite, tableType); } public void CreateTable() where T : new() { var tableType = typeof(T); CreateTable(false, tableType); } public void CreateTable(bool overwrite, Type modelType) { var tableDefinition = DefinitionFactory.GetTableDefinition(modelType); var tableName = tableDefinition.Name; string createSql = _syntaxProvider.Format(tableDefinition); string createPrimaryKeySql = _syntaxProvider.FormatPrimaryKey(tableDefinition); var foreignSql = _syntaxProvider.Format(tableDefinition.ForeignKeys); var indexSql = _syntaxProvider.Format(tableDefinition.Indexes); var tableExist = _db.TableExist(tableName); if (overwrite && tableExist) { _db.DropTable(tableName); tableExist = false; } if (tableExist == false) { using (var transaction = _db.GetTransaction()) { //Execute the Create Table sql int created = _db.Execute(new Sql(createSql)); _logger.Info(string.Format("Create Table sql {0}:\n {1}", created, createSql)); //If any statements exists for the primary key execute them here if (!string.IsNullOrEmpty(createPrimaryKeySql)) { int createdPk = _db.Execute(new Sql(createPrimaryKeySql)); _logger.Info(string.Format("Primary Key sql {0}:\n {1}", createdPk, createPrimaryKeySql)); } //Turn on identity insert if db provider is not mysql if (_syntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) _db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", _syntaxProvider.GetQuotedTableName(tableName)))); //Call the NewTable-event to trigger the insert of base/default data //OnNewTable(tableName, _db, e, _logger); _baseDataCreation.InitializeBaseData(tableName); //Turn off identity insert if db provider is not mysql if (_syntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity)) _db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", _syntaxProvider.GetQuotedTableName(tableName)))); //Special case for MySql if (_syntaxProvider is MySqlSyntaxProvider && tableName.Equals("umbracoUser")) { _db.Update("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" }); } //Loop through index statements and execute sql foreach (var sql in indexSql) { int createdIndex = _db.Execute(new Sql(sql)); _logger.Info(string.Format("Create Index sql {0}:\n {1}", createdIndex, sql)); } //Loop through foreignkey statements and execute sql foreach (var sql in foreignSql) { int createdFk = _db.Execute(new Sql(sql)); _logger.Info(string.Format("Create Foreign Key sql {0}:\n {1}", createdFk, sql)); } transaction.Complete(); } } _logger.Info(string.Format("New table '{0}' was created", tableName)); } public void DropTable() where T : new() { Type type = typeof(T); var tableNameAttribute = type.FirstAttribute(); if (tableNameAttribute == null) throw new Exception( string.Format( "The Type '{0}' does not contain a TableNameAttribute, which is used to find the name of the table to drop. The operation could not be completed.", type.Name)); string tableName = tableNameAttribute.Value; DropTable(tableName); } public void DropTable(string tableName) { var sql = new Sql(string.Format( _syntaxProvider.DropTable, _syntaxProvider.GetQuotedTableName(tableName))); _db.Execute(sql); } } }