2012-10-29 09:49:31 -01:00
using System ;
using System.Configuration ;
2012-11-26 17:58:42 -01:00
using System.Data.SqlServerCe ;
2012-11-27 10:05:26 -01:00
using System.IO ;
2012-11-26 15:57:03 -01:00
using System.Linq ;
2014-03-14 13:04:20 +11:00
using System.Web ;
2012-11-26 15:57:03 -01:00
using System.Web.Configuration ;
using System.Xml.Linq ;
2016-04-12 15:11:07 +02:00
using NPoco ;
2015-06-24 14:17:24 +02:00
using Semver ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Configuration ;
2013-10-04 10:53:22 +10:00
using Umbraco.Core.IO ;
2012-11-27 15:07:53 -01:00
using Umbraco.Core.Logging ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Persistence ;
2012-11-30 18:48:20 -01:00
using Umbraco.Core.Persistence.Migrations ;
2013-01-25 15:05:42 -01:00
using Umbraco.Core.Persistence.Migrations.Initial ;
2016-05-02 15:38:45 +02:00
using Umbraco.Core.Persistence.Querying ;
2012-10-29 09:49:31 -01:00
using Umbraco.Core.Persistence.SqlSyntax ;
2015-06-22 18:59:44 +02:00
using Umbraco.Core.Services ;
2012-10-29 09:49:31 -01:00
namespace Umbraco.Core
{
/// <summary>
2016-04-12 19:55:50 +02:00
/// Represents the Umbraco database.
2012-10-29 09:49:31 -01:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <remarks>One per AppDomain. Ensures that the database is available.</remarks>
2012-10-29 09:49:31 -01:00
public class DatabaseContext
{
2013-01-29 11:30:20 -01:00
private readonly IDatabaseFactory _factory ;
2015-01-09 13:00:26 +11:00
private readonly ILogger _logger ;
2016-04-12 19:55:50 +02:00
private DatabaseSchemaResult _databaseSchemaValidationResult ;
2012-12-14 08:06:32 +05:00
2015-01-13 18:19:52 +11:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Initializes a new instance of the <see cref="DatabaseContext"/> class with a database factory and a logger.
2015-01-13 18:19:52 +11:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <param name="factory">A database factory.</param>
/// <param name="logger">A logger.</param>
/// <remarks>The database factory will try to configure itself but may fail eg if the default
/// Umbraco connection string is not available because we are installing. In which case this
/// database context must sort things out and configure the database factory before it can be
/// used.</remarks>
public DatabaseContext ( IDatabaseFactory factory , ILogger logger )
2015-01-13 18:19:52 +11:00
{
2016-04-12 19:55:50 +02:00
if ( factory = = null ) throw new ArgumentNullException ( nameof ( factory ) ) ;
if ( logger = = null ) throw new ArgumentNullException ( nameof ( logger ) ) ;
2015-01-13 18:19:52 +11:00
2015-01-09 13:35:32 +11:00
_factory = factory ;
_logger = logger ;
}
2016-05-02 15:38:45 +02:00
/// <summary>
/// Gets the QueryFactory
/// </summary>
public IQueryFactory QueryFactory = > _factory . QueryFactory ;
2015-01-13 18:19:52 +11:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Gets the database sql syntax.
2015-01-13 18:19:52 +11:00
/// </summary>
2016-05-02 15:38:45 +02:00
public ISqlSyntaxProvider SqlSyntax = > _factory . QueryFactory . SqlSyntax ;
2015-01-09 13:00:26 +11:00
2012-10-29 09:49:31 -01:00
/// <summary>
/// Gets the <see cref="Database"/> object for doing CRUD operations
/// against custom tables that resides in the Umbraco database.
/// </summary>
/// <remarks>
/// This should not be used for CRUD operations or queries against the
2012-11-27 15:07:53 -01:00
/// standard Umbraco tables! Use the Public services for that.
2012-10-29 09:49:31 -01:00
/// </remarks>
2016-04-14 18:17:18 +02:00
public UmbracoDatabase Database = > _factory . GetDatabase ( ) ;
2012-10-29 09:49:31 -01:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Gets a value indicating whether the database is configured, ie whether it exists
/// and can be reached. It does not necessarily mean that Umbraco is installed nor
/// up-to-date.
2012-10-29 09:49:31 -01:00
/// </summary>
2016-04-12 19:55:50 +02:00
public bool IsDatabaseConfigured = > _factory . Configured ;
2012-10-29 09:49:31 -01:00
2014-11-12 16:30:01 +11:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Gets a value indicating whether it is possible to connect to the database.
2014-11-12 16:30:01 +11:00
/// </summary>
2016-04-14 18:17:18 +02:00
public bool CanConnect
2014-11-12 16:30:01 +11:00
{
get
{
if ( IsDatabaseConfigured = = false ) return false ;
2016-04-12 19:55:50 +02:00
var canConnect = _factory . CanConnect ;
2015-07-09 14:31:59 +02:00
LogHelper . Info < DatabaseContext > ( "CanConnect = " + canConnect ) ;
return canConnect ;
2014-11-12 16:30:01 +11:00
}
}
2016-04-12 19:55:50 +02:00
#region Configure Connection String
2012-11-26 17:58:42 -01:00
2016-04-12 19:55:50 +02:00
private const string EmbeddedDatabaseConnectionString = @"Data Source=|DataDirectory|\Umbraco.sdf;Flush Interval=1;" ;
2012-11-26 17:58:42 -01:00
2012-11-26 15:57:03 -01:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Configures a connection string for the embedded database.
2012-11-26 15:57:03 -01:00
/// </summary>
2013-01-29 11:30:20 -01:00
public void ConfigureEmbeddedDatabaseConnection ( )
2012-11-26 15:57:03 -01:00
{
2016-04-22 12:03:30 +02:00
ConfigureEmbeddedDatabaseConnection ( _factory , _logger ) ;
}
private static void ConfigureEmbeddedDatabaseConnection ( IDatabaseFactory factory , ILogger logger )
{
SaveConnectionString ( EmbeddedDatabaseConnectionString , Constants . DbProviderNames . SqlCe , logger ) ;
2012-11-26 17:58:42 -01:00
2012-11-27 15:07:53 -01:00
var path = Path . Combine ( GlobalSettings . FullpathToRoot , "App_Data" , "Umbraco.sdf" ) ;
2012-11-27 10:05:26 -01:00
if ( File . Exists ( path ) = = false )
{
2016-04-12 19:55:50 +02:00
// this should probably be in a "using (new SqlCeEngine)" clause but not sure
// of the side effects and it's been like this for quite some time now
2014-03-04 16:40:23 +11:00
2016-04-12 19:55:50 +02:00
var engine = new SqlCeEngine ( EmbeddedDatabaseConnectionString ) ;
engine . CreateDatabase ( ) ;
2012-11-27 10:05:26 -01:00
}
2012-11-26 17:58:42 -01:00
2016-04-22 12:03:30 +02:00
factory . Configure ( EmbeddedDatabaseConnectionString , Constants . DbProviderNames . SqlCe ) ;
2014-02-26 16:01:31 +01:00
}
2012-11-26 15:57:03 -01:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Configures a connection string that has been entered manually.
2012-11-26 15:57:03 -01:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <param name="connectionString">A connection string.</param>
/// <remarks>Has to be either SQL Server or MySql</remarks>
2012-11-26 15:57:03 -01:00
public void ConfigureDatabaseConnection ( string connectionString )
{
2016-04-12 19:55:50 +02:00
var provider = DbConnectionExtensions . DetectProviderNameFromConnectionString ( connectionString ) ;
var providerName = provider . ToString ( ) . ToLower ( ) . Contains ( "mysql" )
? Constants . DbProviderNames . MySql
: Constants . DbProviderNames . SqlServer ;
2016-04-22 12:03:30 +02:00
SaveConnectionString ( connectionString , providerName , _logger ) ;
2016-04-12 19:55:50 +02:00
_factory . Configure ( connectionString , providerName ) ;
2012-11-26 15:57:03 -01:00
}
/// <summary>
2016-04-12 19:55:50 +02:00
/// Configures a connection string from the installer.
2012-11-26 15:57:03 -01:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
2012-11-26 15:57:03 -01:00
public void ConfigureDatabaseConnection ( string server , string databaseName , string user , string password , string databaseProvider )
2016-04-12 19:55:50 +02:00
{
2014-02-26 16:01:31 +01:00
string providerName ;
var connectionString = GetDatabaseConnectionString ( server , databaseName , user , password , databaseProvider , out providerName ) ;
2016-04-22 12:03:30 +02:00
SaveConnectionString ( connectionString , providerName , _logger ) ;
2016-04-12 19:55:50 +02:00
_factory . Configure ( connectionString , providerName ) ;
2014-02-26 16:01:31 +01:00
}
2016-04-12 19:55:50 +02:00
/// <summary>
/// Gets a connection string from the installer.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <param name="databaseProvider">The name the provider (Sql, Sql Azure, Sql Ce, MySql).</param>
/// <param name="providerName"></param>
/// <returns>A connection string.</returns>
2014-02-26 16:01:31 +01:00
public string GetDatabaseConnectionString ( string server , string databaseName , string user , string password , string databaseProvider , out string providerName )
2012-11-26 15:57:03 -01:00
{
2016-04-12 19:55:50 +02:00
providerName = Constants . DbProviderNames . SqlServer ;
var test = databaseProvider . ToLower ( ) ;
if ( test . Contains ( "mysql" ) )
2012-11-26 15:57:03 -01:00
{
2016-04-12 19:55:50 +02:00
providerName = Constants . DbProviderNames . MySql ;
return $"Server={server}; Database={databaseName};Uid={user};Pwd={password}" ;
2012-11-26 15:57:03 -01:00
}
2016-04-12 19:55:50 +02:00
if ( test . Contains ( "azure" ) )
2012-11-26 15:57:03 -01:00
{
2016-04-12 19:55:50 +02:00
return GetAzureConnectionString ( server , databaseName , user , password ) ;
2012-11-26 15:57:03 -01:00
}
2016-04-12 19:55:50 +02:00
return $"server={server};database={databaseName};user id={user};password={password}" ;
2012-11-26 15:57:03 -01:00
}
2013-07-19 04:26:50 -07:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Configures a connection string using Microsoft SQL Server integrated security.
2013-07-19 04:26:50 -07:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
2013-07-19 04:26:50 -07:00
public void ConfigureIntegratedSecurityDatabaseConnection ( string server , string databaseName )
{
2014-02-26 16:01:31 +01:00
var connectionString = GetIntegratedSecurityDatabaseConnectionString ( server , databaseName ) ;
2016-04-22 12:03:30 +02:00
SaveConnectionString ( connectionString , Constants . DbProviderNames . SqlServer , _logger ) ;
2016-04-12 19:55:50 +02:00
_factory . Configure ( connectionString , Constants . DbProviderNames . SqlServer ) ;
2013-07-19 04:26:50 -07:00
}
2016-04-12 19:55:50 +02:00
/// <summary>
/// Gets a connection string using Microsoft SQL Server integrated security.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database</param>
/// <returns>A connection string.</returns>
2014-02-26 16:01:31 +01:00
public string GetIntegratedSecurityDatabaseConnectionString ( string server , string databaseName )
{
2016-04-12 19:55:50 +02:00
return $"Server={server};Database={databaseName};Integrated Security=true" ;
2014-02-26 16:01:31 +01:00
}
2016-04-12 19:55:50 +02:00
/// <summary>
/// Gets an Azure connection string.
/// </summary>
/// <param name="server">The name or address of the database server.</param>
/// <param name="databaseName">The name of the database.</param>
/// <param name="user">The user name.</param>
/// <param name="password">The user password.</param>
/// <returns>A connection string.</returns>
public string GetAzureConnectionString ( string server , string databaseName , string user , string password )
2013-02-20 14:34:18 -01:00
{
if ( server . Contains ( "." ) & & ServerStartsWithTcp ( server ) = = false )
2016-04-12 19:55:50 +02:00
server = $"tcp:{server}" ;
2013-02-20 14:34:18 -01:00
if ( server . Contains ( "." ) = = false & & ServerStartsWithTcp ( server ) )
{
2016-04-12 19:55:50 +02:00
string serverName = server . Contains ( "," )
? server . Substring ( 0 , server . IndexOf ( "," , StringComparison . Ordinal ) )
2013-02-20 14:34:18 -01:00
: server ;
var portAddition = string . Empty ;
if ( server . Contains ( "," ) )
portAddition = server . Substring ( server . IndexOf ( "," , StringComparison . Ordinal ) ) ;
2016-04-12 19:55:50 +02:00
server = $"{serverName}.database.windows.net{portAddition}" ;
2013-02-20 14:34:18 -01:00
}
2016-04-12 19:55:50 +02:00
2013-02-20 14:34:18 -01:00
if ( ServerStartsWithTcp ( server ) = = false )
2016-04-12 19:55:50 +02:00
server = $"tcp:{server}.database.windows.net" ;
2013-02-20 14:34:18 -01:00
if ( server . Contains ( "," ) = = false )
2016-04-12 19:55:50 +02:00
server = $"{server},1433" ;
2013-02-20 14:34:18 -01:00
if ( user . Contains ( "@" ) = = false )
{
var userDomain = server ;
if ( ServerStartsWithTcp ( server ) )
userDomain = userDomain . Substring ( userDomain . IndexOf ( ":" , StringComparison . Ordinal ) + 1 ) ;
if ( userDomain . Contains ( "." ) )
userDomain = userDomain . Substring ( 0 , userDomain . IndexOf ( "." , StringComparison . Ordinal ) ) ;
2016-04-12 19:55:50 +02:00
user = $"{user}@{userDomain}" ;
2013-02-20 14:34:18 -01:00
}
2016-04-12 19:55:50 +02:00
return $"Server={server};Database={databaseName};User ID={user};Password={password}" ;
2013-02-20 14:34:18 -01:00
}
private static bool ServerStartsWithTcp ( string server )
{
return server . ToLower ( ) . StartsWith ( "tcp:" . ToLower ( ) ) ;
}
2012-11-26 15:57:03 -01:00
/// <summary>
2016-04-12 19:55:50 +02:00
/// Saves the connection string as a proper .net connection string in web.config.
2012-11-26 15:57:03 -01:00
/// </summary>
2016-04-12 19:55:50 +02:00
/// <remarks>Saves the ConnectionString in the very nasty 'medium trust'-supportive way.</remarks>
/// <param name="connectionString">The connection string.</param>
/// <param name="providerName">The provider name.</param>
2016-04-22 12:03:30 +02:00
/// <param name="logger">A logger.</param>
private static void SaveConnectionString ( string connectionString , string providerName , ILogger logger )
2012-11-26 15:57:03 -01:00
{
2016-04-12 19:55:50 +02:00
if ( string . IsNullOrWhiteSpace ( connectionString ) ) throw new ArgumentException ( "Value cannot be null nor empty." , nameof ( connectionString ) ) ;
if ( string . IsNullOrWhiteSpace ( providerName ) ) throw new ArgumentException ( "Value cannot be null nor empty." , nameof ( providerName ) ) ;
// set the connection string for the new datalayer
var connectionStringSettings = new ConnectionStringSettings ( GlobalSettings . UmbracoConnectionName , connectionString , providerName ) ;
var fileName = IOHelper . MapPath ( $"{SystemDirectories.Root}/web.config" ) ;
2012-12-11 16:23:15 -01:00
var xml = XDocument . Load ( fileName , LoadOptions . PreserveWhitespace ) ;
2016-04-12 19:55:50 +02:00
if ( xml . Root = = null ) throw new Exception ( "Invalid web.config file." ) ;
var connectionStrings = xml . Root . DescendantsAndSelf ( "connectionStrings" ) . FirstOrDefault ( ) ;
if ( connectionStrings = = null ) throw new Exception ( "Invalid web.config file." ) ;
2012-11-26 15:57:03 -01:00
2016-04-12 19:55:50 +02:00
// update connectionString if it exists, or else create a new connectionString
var setting = connectionStrings . Descendants ( "add" ) . FirstOrDefault ( s = > s . Attribute ( "name" ) . Value = = GlobalSettings . UmbracoConnectionName ) ;
2012-11-26 15:57:03 -01:00
if ( setting = = null )
2016-04-12 19:55:50 +02:00
{
connectionStrings . Add ( new XElement ( "add" ,
2012-11-26 15:57:03 -01:00
new XAttribute ( "name" , GlobalSettings . UmbracoConnectionName ) ,
new XAttribute ( "connectionString" , connectionStringSettings ) ,
new XAttribute ( "providerName" , providerName ) ) ) ;
2016-04-12 19:55:50 +02:00
}
2012-11-26 15:57:03 -01:00
else
{
setting . Attribute ( "connectionString" ) . Value = connectionString ;
2013-01-02 07:21:08 -01:00
setting . Attribute ( "providerName" ) . Value = providerName ;
2012-11-26 15:57:03 -01:00
}
2012-12-11 16:23:15 -01:00
xml . Save ( fileName , SaveOptions . DisableFormatting ) ;
2016-04-22 12:03:30 +02:00
logger . Info < DatabaseContext > ( "Configured a new ConnectionString using the '" + providerName + "' provider." ) ;
2012-11-26 15:57:03 -01:00
}
2016-04-12 19:55:50 +02:00
#endregion
2013-01-29 11:30:20 -01:00
2016-04-12 19:55:50 +02:00
#region Utils
2012-11-02 09:11:23 -01:00
2016-04-22 12:03:30 +02:00
internal static void GiveLegacyAChance ( IDatabaseFactory factory , ILogger logger )
2016-04-12 19:55:50 +02:00
{
// look for the legacy appSettings key
var legacyConnString = ConfigurationManager . AppSettings [ GlobalSettings . UmbracoConnectionName ] ;
if ( string . IsNullOrWhiteSpace ( legacyConnString ) ) return ;
2013-08-02 16:01:54 +10:00
2016-04-12 19:55:50 +02:00
var test = legacyConnString . ToLowerInvariant ( ) ;
if ( test . Contains ( "sqlce4umbraco" ) )
{
// sql ce
2016-04-22 12:03:30 +02:00
ConfigureEmbeddedDatabaseConnection ( factory , logger ) ;
2012-10-29 09:49:31 -01:00
}
2016-04-12 19:55:50 +02:00
else if ( test . Contains ( "tcp:" ) )
2012-10-29 09:49:31 -01:00
{
2016-04-12 19:55:50 +02:00
// sql azure
2016-04-22 12:03:30 +02:00
SaveConnectionString ( legacyConnString , Constants . DbProviderNames . SqlServer , logger ) ;
factory . Configure ( legacyConnString , Constants . DbProviderNames . SqlServer ) ;
2012-10-29 09:49:31 -01:00
}
2016-04-12 19:55:50 +02:00
else if ( test . Contains ( "datalayer=mysql" ) )
{
// mysql
2015-01-13 18:19:52 +11:00
2016-04-12 19:55:50 +02:00
// strip the datalayer part off
var connectionStringWithoutDatalayer = string . Empty ;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach ( var variable in legacyConnString . Split ( ';' ) . Where ( x = > x . ToLowerInvariant ( ) . StartsWith ( "datalayer" ) = = false ) )
connectionStringWithoutDatalayer = $"{connectionStringWithoutDatalayer}{variable};" ;
2013-03-09 10:43:34 -01:00
2016-04-22 12:03:30 +02:00
SaveConnectionString ( connectionStringWithoutDatalayer , Constants . DbProviderNames . MySql , logger ) ;
factory . Configure ( connectionStringWithoutDatalayer , Constants . DbProviderNames . MySql ) ;
2012-11-27 11:38:09 -01:00
}
2016-04-12 19:55:50 +02:00
else
2012-11-27 11:38:09 -01:00
{
2016-04-12 19:55:50 +02:00
// sql server
2016-04-22 12:03:30 +02:00
SaveConnectionString ( legacyConnString , Constants . DbProviderNames . SqlServer , logger ) ;
factory . Configure ( legacyConnString , Constants . DbProviderNames . SqlServer ) ;
2013-03-09 10:43:34 -01:00
}
2012-11-29 12:40:48 -01:00
2016-04-12 19:55:50 +02:00
// remove the legacy connection string, so we don't end up in a loop if something goes wrong
GlobalSettings . RemoveSetting ( GlobalSettings . UmbracoConnectionName ) ;
2013-07-26 18:13:56 +10:00
}
2016-04-12 19:55:50 +02:00
#endregion
2015-01-09 13:00:26 +11:00
2016-04-12 19:55:50 +02:00
#region Database Schema
2013-08-02 16:01:54 +10:00
2013-01-25 15:05:42 -01:00
internal DatabaseSchemaResult ValidateDatabaseSchema ( )
{
2016-04-12 19:55:50 +02:00
if ( _factory . Configured = = false )
2015-02-22 21:36:02 +01:00
return new DatabaseSchemaResult ( SqlSyntax ) ;
2013-01-25 15:05:42 -01:00
2016-04-12 19:55:50 +02:00
if ( _databaseSchemaValidationResult ! = null )
return _databaseSchemaValidationResult ;
2014-03-14 13:04:20 +11:00
2016-04-12 19:55:50 +02:00
var database = _factory . GetDatabase ( ) ;
var dbSchema = new DatabaseSchemaCreation ( database , _logger ) ;
_databaseSchemaValidationResult = dbSchema . ValidateSchema ( ) ;
return _databaseSchemaValidationResult ;
2013-01-25 15:05:42 -01:00
}
2015-06-22 19:31:34 +02:00
internal Result CreateDatabaseSchemaAndData ( ApplicationContext applicationContext )
2016-04-12 19:55:50 +02:00
{
2012-11-29 12:40:48 -01:00
try
{
2014-03-14 13:04:20 +11:00
var readyForInstall = CheckReadyForInstall ( ) ;
if ( readyForInstall . Success = = false )
{
return readyForInstall . Result ;
}
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: Started" ) ;
2013-02-06 09:04:46 -01:00
2016-04-12 19:55:50 +02:00
var database = _factory . GetDatabase ( ) ;
2014-04-30 19:58:59 +10:00
// If MySQL, we're going to ensure that database calls are maintaining proper casing as to remove the necessity for checks
// for case insensitive queries. In an ideal situation (which is what we're striving for), all calls would be case sensitive.
2016-04-12 19:55:50 +02:00
/ *
2015-01-09 13:00:26 +11:00
var supportsCaseInsensitiveQueries = SqlSyntax . SupportsCaseInsensitiveQueries ( database ) ;
2013-02-25 14:42:07 -01:00
if ( supportsCaseInsensitiveQueries = = false )
2013-02-25 13:14:26 -01:00
{
message = "<p> </p><p>The database you're trying to use does not support case insensitive queries. <br />We currently do not support these types of databases.</p>" +
2013-07-23 15:38:26 +02:00
"<p>You can fix this by changing the following setting in your my.ini file in your MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
2013-02-25 13:14:26 -01:00
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
return new Result { Message = message , Success = false , Percentage = "15" } ;
}
2014-04-30 19:58:59 +10:00
* /
2013-12-13 12:34:57 +11:00
2016-04-12 19:55:50 +02:00
var message = GetResultMessageForMySql ( ) ;
2013-01-25 15:05:42 -01:00
var schemaResult = ValidateDatabaseSchema ( ) ;
2015-06-23 18:10:50 +02:00
var installedSchemaVersion = schemaResult . DetermineInstalledVersion ( ) ;
2016-04-12 19:55:50 +02:00
2013-01-25 15:05:42 -01:00
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
2015-06-23 18:10:50 +02:00
if ( string . IsNullOrEmpty ( GlobalSettings . ConfigurationStatus ) & & installedSchemaVersion . Equals ( new Version ( 0 , 0 , 0 ) ) )
2012-11-30 18:48:20 -01:00
{
2016-04-12 19:55:50 +02:00
var helper = new DatabaseSchemaHelper ( database , _logger ) ;
2015-06-22 19:31:34 +02:00
helper . CreateDatabaseSchema ( true , applicationContext ) ;
2013-02-25 14:42:07 -01:00
message = message + "<p>Installation completed!</p>" ;
2013-12-13 12:34:57 +11:00
//now that everything is done, we need to determine the version of SQL server that is executing
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: " + message ) ;
2013-12-13 12:34:57 +11:00
return new Result { Message = message , Success = true , Percentage = "100" } ;
2012-11-30 18:48:20 -01:00
}
2013-12-13 12:34:57 +11:00
//we need to do an upgrade so return a new status message and it will need to be done during the next step
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database requires upgrade" ) ;
2013-12-13 12:34:57 +11:00
message = "<p>Upgrading database, this may take some time...</p>" ;
return new Result
{
2016-04-12 19:55:50 +02:00
RequiresUpgrade = true ,
Message = message ,
Success = true ,
2013-12-13 12:34:57 +11:00
Percentage = "30"
} ;
}
catch ( Exception ex )
{
return HandleInstallException ( ex ) ;
}
}
/// <summary>
/// This assumes all of the previous checks are done!
/// </summary>
/// <returns></returns>
2015-12-18 13:29:12 +01:00
internal Result UpgradeSchemaAndData ( IMigrationEntryService migrationEntryService , IMigrationResolver migrationResolver )
2013-12-13 12:34:57 +11:00
{
try
{
2014-03-14 13:04:20 +11:00
var readyForInstall = CheckReadyForInstall ( ) ;
if ( readyForInstall . Success = = false )
{
return readyForInstall . Result ;
}
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database upgrade started" ) ;
2013-12-13 12:34:57 +11:00
2016-04-12 19:55:50 +02:00
var database = _factory . GetDatabase ( ) ;
//var supportsCaseInsensitiveQueries = SqlSyntax.SupportsCaseInsensitiveQueries(database);
2013-12-13 12:34:57 +11:00
2014-04-30 19:58:59 +10:00
var message = GetResultMessageForMySql ( ) ;
2013-12-13 12:34:57 +11:00
var schemaResult = ValidateDatabaseSchema ( ) ;
2015-06-23 18:10:50 +02:00
2015-06-24 14:17:24 +02:00
var installedSchemaVersion = new SemVersion ( schemaResult . DetermineInstalledVersion ( ) ) ;
2015-06-24 15:20:08 +02:00
var installedMigrationVersion = new SemVersion ( 0 ) ;
//we cannot check the migrations table if it doesn't exist, this will occur when upgrading to 7.3
if ( schemaResult . ValidTables . Any ( x = > x . InvariantEquals ( "umbracoMigration" ) ) )
{
2016-04-12 19:55:50 +02:00
installedMigrationVersion = schemaResult . DetermineInstalledVersionByMigrations ( migrationEntryService ) ;
2015-06-24 15:20:08 +02:00
}
2015-06-23 18:10:50 +02:00
var targetVersion = UmbracoVersion . Current ;
2016-04-12 19:55:50 +02:00
2015-06-23 18:10:50 +02:00
//In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
2016-04-12 19:55:50 +02:00
// return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
2015-06-23 18:10:50 +02:00
// migrations are run for the target version
2015-06-24 14:17:24 +02:00
if ( installedMigrationVersion = = new SemVersion ( new Version ( 0 , 0 , 0 ) ) & & installedSchemaVersion > new SemVersion ( new Version ( 0 , 0 , 0 ) ) )
2015-06-23 18:10:50 +02:00
{
//set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
2015-06-24 14:17:24 +02:00
installedMigrationVersion = new SemVersion ( targetVersion . SubtractRevision ( ) ) ;
2015-06-23 18:10:50 +02:00
}
2016-04-12 19:55:50 +02:00
2015-06-24 17:46:52 +02:00
//Figure out what our current installed version is. If the web.config doesn't have a version listed, then we'll use the minimum
2016-04-12 19:55:50 +02:00
// version detected between the schema installed and the migrations listed in the migration table.
2015-06-24 17:46:52 +02:00
// If there is a version in the web.config, we'll take the minimum between the listed migration in the db and what
// is declared in the web.config.
2016-04-12 19:55:50 +02:00
2015-06-24 17:46:52 +02:00
var currentInstalledVersion = string . IsNullOrEmpty ( GlobalSettings . ConfigurationStatus )
2015-06-23 18:10:50 +02:00
//Take the minimum version between the detected schema version and the installed migration version
? new [ ] { installedSchemaVersion , installedMigrationVersion } . Min ( )
//Take the minimum version between the installed migration version and the version specified in the config
2015-06-24 14:17:24 +02:00
: new [ ] { SemVersion . Parse ( GlobalSettings . ConfigurationStatus ) , installedMigrationVersion } . Min ( ) ;
2016-04-12 19:55:50 +02:00
//Ok, another edge case here. If the current version is a pre-release,
// then we want to ensure all migrations for the current release are executed.
2015-06-24 17:46:52 +02:00
if ( currentInstalledVersion . Prerelease . IsNullOrWhiteSpace ( ) = = false )
{
currentInstalledVersion = new SemVersion ( currentInstalledVersion . GetVersion ( ) . SubtractRevision ( ) ) ;
}
2013-12-13 12:34:57 +11:00
//DO the upgrade!
2015-12-18 13:29:12 +01:00
var runner = new MigrationRunner ( migrationResolver , migrationEntryService , _logger , currentInstalledVersion , UmbracoVersion . GetSemanticVersion ( ) , GlobalSettings . UmbracoMigrationName ) ;
2015-06-24 15:20:08 +02:00
2016-05-31 23:29:15 +02:00
var migrationContext = new MigrationContext ( database , _logger ) ;
var upgraded = runner . Execute ( migrationContext /*, true*/ ) ;
2016-04-12 19:55:50 +02:00
2015-06-24 15:20:08 +02:00
if ( upgraded = = false )
{
throw new ApplicationException ( "Upgrading failed, either an error occurred during the upgrade process or an event canceled the upgrade process, see log for full details" ) ;
}
2013-12-13 12:34:57 +11:00
message = message + "<p>Upgrade completed!</p>" ;
2013-01-29 11:30:20 -01:00
2013-08-02 16:01:54 +10:00
//now that everything is done, we need to determine the version of SQL server that is executing
2015-01-09 13:00:26 +11:00
_logger . Info < DatabaseContext > ( "Database configuration status: " + message ) ;
2013-02-06 09:04:46 -01:00
2013-01-28 14:01:38 -01:00
return new Result { Message = message , Success = true , Percentage = "100" } ;
2012-11-29 12:40:48 -01:00
}
catch ( Exception ex )
{
2013-12-13 12:34:57 +11:00
return HandleInstallException ( ex ) ;
}
}
2014-04-30 19:58:59 +10:00
private string GetResultMessageForMySql ( )
{
2015-01-09 13:00:26 +11:00
if ( SqlSyntax . GetType ( ) = = typeof ( MySqlSyntaxProvider ) )
2014-04-30 19:58:59 +10:00
{
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
return string . Empty ;
}
/ *
2013-12-13 12:34:57 +11:00
private string GetResultMessageForMySql ( bool? supportsCaseInsensitiveQueries )
{
if ( supportsCaseInsensitiveQueries = = null )
{
return "<p> </p><p>Warning! Could not check if your database type supports case insensitive queries. <br />We currently do not support these databases that do not support case insensitive queries.</p>" +
"<p>You can check this by looking for the following setting in your my.ini file in your MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
2015-01-09 13:00:26 +11:00
if ( SqlSyntax . GetType ( ) = = typeof ( MySqlSyntaxProvider ) )
2013-12-13 12:34:57 +11:00
{
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
"<pre>lower_case_table_names=1</pre><br />" +
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>" ;
}
return string . Empty ;
2014-04-30 19:58:59 +10:00
} * /
2013-01-28 14:01:38 -01:00
2013-12-13 12:34:57 +11:00
private Attempt < Result > CheckReadyForInstall ( )
{
2016-04-12 19:55:50 +02:00
if ( _factory . Configured = = false )
2013-12-13 12:34:57 +11:00
{
return Attempt . Fail ( new Result
2013-01-28 14:01:38 -01:00
{
2016-04-12 19:55:50 +02:00
Message = "Database configuration is invalid. Please check that the entered database exists and"
+ " that the provided username and password has write access to the database." ,
2013-12-13 12:34:57 +11:00
Success = false ,
Percentage = "10"
} ) ;
}
return Attempt < Result > . Succeed ( ) ;
}
2013-01-28 14:01:38 -01:00
2013-12-13 12:34:57 +11:00
private Result HandleInstallException ( Exception ex )
{
2015-01-09 13:00:26 +11:00
_logger . Error < DatabaseContext > ( "Database configuration failed" , ex ) ;
2013-12-13 12:34:57 +11:00
2016-04-12 19:55:50 +02:00
if ( _databaseSchemaValidationResult ! = null )
2013-12-13 12:34:57 +11:00
{
2016-04-12 19:55:50 +02:00
_logger . Info < DatabaseContext > ( "The database schema validation produced the following summary: \n" + _databaseSchemaValidationResult . GetSummary ( ) ) ;
2012-11-29 12:40:48 -01:00
}
2013-12-13 12:34:57 +11:00
return new Result
{
Message =
"The database configuration failed with the following message: " + ex . Message +
"\n Please check log file for additional information (can be found in '/App_Data/Logs/UmbracoTraceLog.txt')" ,
Success = false ,
Percentage = "90"
} ;
2012-11-29 12:40:48 -01:00
}
internal class Result
{
2013-12-13 12:34:57 +11:00
public bool RequiresUpgrade { get ; set ; }
2012-11-29 12:40:48 -01:00
public string Message { get ; set ; }
public bool Success { get ; set ; }
public string Percentage { get ; set ; }
}
2014-03-04 19:20:36 +11:00
2016-04-12 19:55:50 +02:00
#endregion
2014-03-04 19:20:36 +11:00
internal bool IsConnectionStringConfigured ( ConnectionStringSettings databaseSettings )
{
var dbIsSqlCe = false ;
2016-04-12 19:55:50 +02:00
if ( databaseSettings ? . ProviderName ! = null )
dbIsSqlCe = databaseSettings . ProviderName = = Constants . DbProviderNames . SqlCe ;
2014-03-04 19:20:36 +11:00
var sqlCeDatabaseExists = false ;
if ( dbIsSqlCe )
{
var parts = databaseSettings . ConnectionString . Split ( new [ ] { ';' } , StringSplitOptions . RemoveEmptyEntries ) ;
var dataSourcePart = parts . FirstOrDefault ( x = > x . InvariantStartsWith ( "Data Source=" ) ) ;
if ( dataSourcePart ! = null )
{
var datasource = dataSourcePart . Replace ( "|DataDirectory|" , AppDomain . CurrentDomain . GetData ( "DataDirectory" ) . ToString ( ) ) ;
var filePath = datasource . Replace ( "Data Source=" , string . Empty ) ;
2016-04-12 19:55:50 +02:00
sqlCeDatabaseExists = File . Exists ( filePath ) ;
2014-03-04 19:20:36 +11:00
}
}
// Either the connection details are not fully specified or it's a SQL CE database that doesn't exist yet
if ( databaseSettings = = null
| | string . IsNullOrWhiteSpace ( databaseSettings . ConnectionString ) | | string . IsNullOrWhiteSpace ( databaseSettings . ProviderName )
| | ( dbIsSqlCe & & sqlCeDatabaseExists = = false ) )
{
return false ;
}
return true ;
}
2016-04-12 15:11:07 +02:00
2016-04-12 19:55:50 +02:00
// fixme - kill!
2016-04-12 15:11:07 +02:00
public Sql < SqlContext > Sql ( )
{
2016-04-12 19:55:50 +02:00
return Database . Sql ( ) ;
2016-04-12 15:11:07 +02:00
}
2012-10-29 09:49:31 -01:00
}
}