diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 4fe812dbeb..0c7d51334e 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -32,9 +32,9 @@ - - - + + + diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt index fbe2cbe439..f7fbad09bd 100644 --- a/build/NuSpecs/tools/Web.config.install.xdt +++ b/build/NuSpecs/tools/Web.config.install.xdt @@ -22,10 +22,7 @@ - - - - + @@ -51,6 +48,8 @@ + + > diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index b31e79056f..7cb235f023 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.3.0 -beta3 \ No newline at end of file +RC \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index dbc0f2d41d..ae85cd82ea 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.3.0")] -[assembly: AssemblyInformationalVersion("7.3.0-beta3")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.3.0-RC")] \ No newline at end of file diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 7e306a6361..4aef011d05 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -2,6 +2,7 @@ using System.Configuration; using System.Threading; using System.Threading.Tasks; +using System.Web; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; @@ -259,22 +260,13 @@ namespace Umbraco.Core { get { - // if initialized, return - if (_umbracoApplicationUrl != null) return _umbracoApplicationUrl; - - // try settings - ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(this, ProfilingLogger.Logger, UmbracoConfig.For.UmbracoSettings()); - - // and return what we have, may be null + ApplicationUrlHelper.EnsureApplicationUrl(this); return _umbracoApplicationUrl; } - set - { - _umbracoApplicationUrl = value; - } } - internal string _umbracoApplicationUrl; // internal for tests + // ReSharper disable once InconsistentNaming + internal string _umbracoApplicationUrl; private Lazy _configured; internal MainDom MainDom { get; private set; } @@ -379,6 +371,11 @@ namespace Umbraco.Core internal set { _services = value; } } + internal ServerRole GetCurrentServerRole() + { + var registrar = ServerRegistrarResolver.Current.Registrar as IServerRegistrar2; + return registrar == null ? ServerRole.Unknown : registrar.GetCurrentServerRole(); + } private volatile bool _disposed; private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index ed5265446f..30f59875e8 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "beta3"; } } + public static string CurrentComment { get { return "RC"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 4f225aaccb..f737a68f02 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -2,13 +2,13 @@ namespace Umbraco.Core { - public static partial class Constants - { - /// - /// Defines the identifiers for Umbraco object types as constants for easy centralized access/management. - /// - public static class ObjectTypes - { + public static partial class Constants + { + /// + /// Defines the identifiers for Umbraco object types as constants for easy centralized access/management. + /// + public static class ObjectTypes + { /// /// Guid for a doc type container /// @@ -19,84 +19,89 @@ namespace Umbraco.Core /// public const string MediaTypeContainer = "42AEF799-B288-4744-9B10-BE144B73CDC4"; - /// - /// Guid for a Content Item object. - /// - public const string ContentItem = "10E2B09F-C28B-476D-B77A-AA686435E44A"; + /// + /// Guid for a Content Item object. + /// + public const string ContentItem = "10E2B09F-C28B-476D-B77A-AA686435E44A"; - /// - /// Guid for a Content Item Type object. - /// - public const string ContentItemType = "7A333C54-6F43-40A4-86A2-18688DC7E532"; + /// + /// Guid for a Content Item Type object. + /// + public const string ContentItemType = "7A333C54-6F43-40A4-86A2-18688DC7E532"; - /// - /// Guid for the Content Recycle Bin. - /// - public const string ContentRecycleBin = "01BB7FF2-24DC-4C0C-95A2-C24EF72BBAC8"; + /// + /// Guid for the Content Recycle Bin. + /// + public const string ContentRecycleBin = "01BB7FF2-24DC-4C0C-95A2-C24EF72BBAC8"; - /// - /// Guid for a DataType object. - /// - public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C"; + /// + /// Guid for a DataType object. + /// + public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C"; - /// - /// Guid for a Document object. - /// - public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972"; + /// + /// Guid for a Document object. + /// + public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972"; - /// - /// Guid for a Document Type object. - /// - public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB"; + /// + /// Guid for a Document Type object. + /// + public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB"; - /// - /// Guid for a Media object. - /// - public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C"; + /// + /// Guid for a Media object. + /// + public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C"; - /// - /// Guid for the Media Recycle Bin. - /// - public const string MediaRecycleBin = "CF3D8E34-1C1C-41e9-AE56-878B57B32113"; + /// + /// Guid for the Media Recycle Bin. + /// + public const string MediaRecycleBin = "CF3D8E34-1C1C-41e9-AE56-878B57B32113"; - /// - /// Guid for a Media Type object. - /// - public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E"; + /// + /// Guid for a Media Type object. + /// + public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E"; - /// - /// Guid for a Member object. - /// - public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560"; + /// + /// Guid for a Member object. + /// + public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560"; - /// - /// Guid for a Member Group object. - /// - public const string MemberGroup = "366E63B9-880F-4E13-A61C-98069B029728"; + /// + /// Guid for a Member Group object. + /// + public const string MemberGroup = "366E63B9-880F-4E13-A61C-98069B029728"; - /// - /// Guid for a Member Type object. - /// - public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43"; + /// + /// Guid for a Member Type object. + /// + public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43"; - /// - /// Guid for a Stylesheet object. - /// + /// + /// Guid for a Stylesheet object. + /// [Obsolete("This no longer exists in the database")] public const string Stylesheet = "9F68DA4F-A3A8-44C2-8226-DCBD125E4840"; [Obsolete("This no longer exists in the database")] internal const string StylesheetProperty = "5555da4f-a123-42b2-4488-dcdfb25e4111"; - /// - /// Guid for the System Root. - /// - public const string SystemRoot = "EA7D8624-4CFE-4578-A871-24AA946BF34D"; + /// + /// Guid for the System Root. + /// + public const string SystemRoot = "EA7D8624-4CFE-4578-A871-24AA946BF34D"; - /// - /// Guid for a Template object. - /// - public const string Template = "6FBDE604-4178-42CE-A10B-8A2600A2F07D"; - } - } + /// + /// Guid for a Template object. + /// + public const string Template = "6FBDE604-4178-42CE-A10B-8A2600A2F07D"; + + /// + /// Guid for a Lock object. + /// + public const string LockObject = "87A9F1FF-B1E4-4A25-BABB-465A4A47EC41"; + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index b72afa265f..82e3a1ff3f 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -26,6 +26,8 @@ public const int DefaultMediaListViewDataTypeId = -96; public const int DefaultMembersListViewDataTypeId = -97; + // identifiers for lock objects + public const int ServersLock = -331; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/DelegateExtensions.cs b/src/Umbraco.Core/DelegateExtensions.cs new file mode 100644 index 0000000000..78450448e2 --- /dev/null +++ b/src/Umbraco.Core/DelegateExtensions.cs @@ -0,0 +1,44 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Umbraco.Core +{ + public static class DelegateExtensions + { + public static Attempt RetryUntilSuccessOrTimeout(this Func> task, TimeSpan timeout, TimeSpan pause) + { + if (pause.TotalMilliseconds < 0) + { + throw new ArgumentException("pause must be >= 0 milliseconds"); + } + var stopwatch = Stopwatch.StartNew(); + do + { + var result = task(); + if (result) { return result; } + Thread.Sleep((int)pause.TotalMilliseconds); + } + while (stopwatch.Elapsed < timeout); + return Attempt.Fail(); + } + + public static Attempt RetryUntilSuccessOrMaxAttempts(this Func> task, int totalAttempts, TimeSpan pause) + { + if (pause.TotalMilliseconds < 0) + { + throw new ArgumentException("pause must be >= 0 milliseconds"); + } + int attempts = 0; + do + { + attempts++; + var result = task(attempts); + if (result) { return result; } + Thread.Sleep((int)pause.TotalMilliseconds); + } + while (attempts < totalAttempts); + return Attempt.Fail(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 2d87f634f4..286acf0285 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -152,12 +152,7 @@ namespace Umbraco.Core.IO /// A value indicating whether the filepath is valid. internal static bool VerifyEditPath(string filePath, string validDir) { - if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false) - filePath = MapPath(filePath); - if (validDir.StartsWith(MapPath(SystemDirectories.Root)) == false) - validDir = MapPath(validDir); - - return filePath.StartsWith(validDir); + return VerifyEditPath(filePath, new[] { validDir }); } /// @@ -182,15 +177,31 @@ namespace Umbraco.Core.IO /// A value indicating whether the filepath is valid. internal static bool VerifyEditPath(string filePath, IEnumerable validDirs) { + // this is called from ScriptRepository, PartialViewRepository, etc. + // filePath is the fullPath (rooted, filesystem path, can be trusted) + // validDirs are virtual paths (eg ~/Views) + // + // except that for templates, filePath actually is a virtual path + + //TODO + // what's below is dirty, there are too many ways to get the root dir, etc. + // not going to fix everything today + + var mappedRoot = MapPath(SystemDirectories.Root); + if (filePath.StartsWith(mappedRoot) == false) + filePath = MapPath(filePath); + + // yes we can (see above) + //// don't trust what we get, it may contain relative segments + //filePath = Path.GetFullPath(filePath); + foreach (var dir in validDirs) { var validDir = dir; - if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false) - filePath = MapPath(filePath); - if (validDir.StartsWith(MapPath(SystemDirectories.Root)) == false) + if (validDir.StartsWith(mappedRoot) == false) validDir = MapPath(validDir); - if (filePath.StartsWith(validDir)) + if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar)) return true; } @@ -219,11 +230,8 @@ namespace Umbraco.Core.IO /// A value indicating whether the filepath is valid. internal static bool VerifyFileExtension(string filePath, List validFileExtensions) { - if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false) - filePath = MapPath(filePath); - var f = new FileInfo(filePath); - - return validFileExtensions.Contains(f.Extension.Substring(1)); + var ext = Path.GetExtension(filePath); + return ext != null && validFileExtensions.Contains(ext.TrimStart('.')); } /// @@ -240,6 +248,16 @@ namespace Umbraco.Core.IO return true; } + public static bool PathStartsWith(string path, string root, char separator) + { + // either it is identical to root, + // or it is root + separator + anything + + if (path.StartsWith(root, StringComparison.OrdinalIgnoreCase) == false) return false; + if (path.Length == root.Length) return true; + if (path.Length < root.Length) return false; + return path[root.Length] == separator; + } /// /// Returns the path to the root of the application, by getting the path to where the assembly where this diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 13df315960..47daff932d 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Globalization; -using System.IO; +using System.IO; using System.Linq; using Umbraco.Core.Logging; @@ -9,7 +8,12 @@ namespace Umbraco.Core.IO { public class PhysicalFileSystem : IFileSystem { - internal string RootPath { get; private set; } + // the rooted, filesystem path, using directory separator chars, NOT ending with a separator + // eg "c:" or "c:\path\to\site" or "\\server\path" + private readonly string _rootPath; + + // the ??? url, using url separator chars, NOT ending with a separator + // eg "" (?) or "/Scripts" or ??? private readonly string _rootUrl; public PhysicalFileSystem(string virtualRoot) @@ -18,8 +22,13 @@ namespace Umbraco.Core.IO if (virtualRoot.StartsWith("~/") == false) throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'"); - RootPath = IOHelper.MapPath(virtualRoot); + _rootPath = IOHelper.MapPath(virtualRoot); + _rootPath = EnsureDirectorySeparatorChar(_rootPath); + _rootPath = _rootPath.TrimEnd(Path.DirectorySeparatorChar); + _rootUrl = IOHelper.ResolveUrl(virtualRoot); + _rootUrl = EnsureUrlSeparatorChar(_rootUrl); + _rootUrl = _rootUrl.TrimEnd('/'); } public PhysicalFileSystem(string rootPath, string rootUrl) @@ -33,18 +42,31 @@ namespace Umbraco.Core.IO if (rootPath.StartsWith("~/")) throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'"); - RootPath = rootPath; - _rootUrl = rootUrl; + // rootPath should be... rooted, as in, it's a root path! + // but the test suite App.config cannot really "root" anything so we'll have to do it here + + //var localRoot = AppDomain.CurrentDomain.BaseDirectory; + var localRoot = IOHelper.GetRootDirectorySafe(); + if (Path.IsPathRooted(rootPath) == false) + { + rootPath = Path.Combine(localRoot, rootPath); + } + + rootPath = EnsureDirectorySeparatorChar(rootPath); + rootUrl = EnsureUrlSeparatorChar(rootUrl); + + _rootPath = rootPath.TrimEnd(Path.DirectorySeparatorChar); + _rootUrl = rootUrl.TrimEnd('/'); } public IEnumerable GetDirectories(string path) { - path = EnsureTrailingSeparator(GetFullPath(path)); + var fullPath = GetFullPath(path); try { - if (Directory.Exists(path)) - return Directory.EnumerateDirectories(path).Select(GetRelativePath); + if (Directory.Exists(fullPath)) + return Directory.EnumerateDirectories(fullPath).Select(GetRelativePath); } catch (UnauthorizedAccessException ex) { @@ -65,12 +87,13 @@ namespace Umbraco.Core.IO public void DeleteDirectory(string path, bool recursive) { - if (DirectoryExists(path) == false) + var fullPath = GetFullPath(path); + if (Directory.Exists(fullPath) == false) return; try { - Directory.Delete(GetFullPath(path), recursive); + Directory.Delete(fullPath, recursive); } catch (DirectoryNotFoundException ex) { @@ -80,7 +103,8 @@ namespace Umbraco.Core.IO public bool DirectoryExists(string path) { - return Directory.Exists(GetFullPath(path)); + var fullPath = GetFullPath(path); + return Directory.Exists(fullPath); } public void AddFile(string path, Stream stream) @@ -90,17 +114,17 @@ namespace Umbraco.Core.IO public void AddFile(string path, Stream stream, bool overrideIfExists) { - var fsRelativePath = GetRelativePath(path); + var fullPath = GetFullPath(path); + var exists = File.Exists(fullPath); + if (exists && overrideIfExists == false) + throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path)); - var exists = FileExists(fsRelativePath); - if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path)); - - EnsureDirectory(Path.GetDirectoryName(fsRelativePath)); + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // ensure it exists if (stream.CanSeek) stream.Seek(0, 0); - using (var destination = (Stream)File.Create(GetFullPath(fsRelativePath))) + using (var destination = (Stream)File.Create(fullPath)) stream.CopyTo(destination); } @@ -111,9 +135,7 @@ namespace Umbraco.Core.IO public IEnumerable GetFiles(string path, string filter) { - var fsRelativePath = GetRelativePath(path); - - var fullPath = EnsureTrailingSeparator(GetFullPath(fsRelativePath)); + var fullPath = GetFullPath(path); try { @@ -140,12 +162,13 @@ namespace Umbraco.Core.IO public void DeleteFile(string path) { - if (!FileExists(path)) + var fullPath = GetFullPath(path); + if (File.Exists(fullPath) == false) return; try { - File.Delete(GetFullPath(path)); + File.Delete(fullPath); } catch (FileNotFoundException ex) { @@ -155,39 +178,86 @@ namespace Umbraco.Core.IO public bool FileExists(string path) { - return File.Exists(GetFullPath(path)); + var fullpath = GetFullPath(path); + return File.Exists(fullpath); } + // beware, many things depend on how the GetRelative/AbsolutePath methods work! + + /// + /// Gets the relative path. + /// + /// The full path or url. + /// The path, relative to this filesystem's root. + /// + /// The relative path is relative to this filesystem's root, not starting with any + /// directory separator. If input was recognized as a url (path), then output uses url (path) separator + /// chars. + /// public string GetRelativePath(string fullPathOrUrl) { - var relativePath = fullPathOrUrl - .TrimStart(_rootUrl) - .Replace('/', Path.DirectorySeparatorChar) - .TrimStart(RootPath) - .TrimStart(Path.DirectorySeparatorChar); + // test url + var path = fullPathOrUrl.Replace('\\', '/'); // ensure url separator char - return relativePath; + if (IOHelper.PathStartsWith(path, _rootUrl, '/')) // if it starts with the root url... + return path.Substring(_rootUrl.Length) // strip it + .TrimStart('/'); // it's relative + + // test path + path = EnsureDirectorySeparatorChar(fullPathOrUrl); + + if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) // if it starts with the root path + return path.Substring(_rootPath.Length) // strip it + .TrimStart(Path.DirectorySeparatorChar); // it's relative + + // unchanged - including separators + return fullPathOrUrl; } + /// + /// Gets the full path. + /// + /// The full or relative path. + /// The full path. + /// + /// On the physical filesystem, the full path is the rooted (ie non-relative), safe (ie within this + /// filesystem's root) path. All separators are converted to Path.DirectorySeparatorChar. + /// public string GetFullPath(string path) { - //if the path starts with a '/' then it's most likely not a FS relative path which is required so convert it - if (path.StartsWith("/")) - { - path = GetRelativePath(path); - } + // normalize + var opath = path; + path = EnsureDirectorySeparatorChar(path); - return !path.StartsWith(RootPath) - ? Path.Combine(RootPath, path) - : path; + // not sure what we are doing here - so if input starts with a (back) slash, + // we assume it's not a FS relative path and we try to convert it... but it + // really makes little sense? + if (path.StartsWith(Path.DirectorySeparatorChar.ToString())) + path = GetRelativePath(path); + + // if already a full path, return + if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) + return path; + + // else combine and sanitize, ie GetFullPath will take care of any relative + // segments in path, eg '../../foo.tmp' - it may throw a SecurityException + // if the combined path reaches illegal parts of the filesystem + var fpath = Path.Combine(_rootPath, path); + fpath = Path.GetFullPath(fpath); + + // at that point, path is within legal parts of the filesystem, ie we have + // permissions to reach that path, but it may nevertheless be outside of + // our root path, due to relative segments, so better check + if (IOHelper.PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar)) + return fpath; + + throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root."); } public string GetUrl(string path) { - return _rootUrl.TrimEnd("/") + "/" + path - .TrimStart(Path.DirectorySeparatorChar) - .Replace(Path.DirectorySeparatorChar, '/') - .TrimEnd("/"); + path = EnsureUrlSeparatorChar(path).Trim('/'); + return _rootUrl + "/" + path; } public DateTimeOffset GetLastModified(string path) @@ -214,9 +284,19 @@ namespace Umbraco.Core.IO protected string EnsureTrailingSeparator(string path) { - if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)) - path = path + Path.DirectorySeparatorChar; + return path.EnsureEndsWith(Path.DirectorySeparatorChar); + } + protected string EnsureDirectorySeparatorChar(string path) + { + path = path.Replace('/', Path.DirectorySeparatorChar); + path = path.Replace('\\', Path.DirectorySeparatorChar); + return path; + } + + protected string EnsureUrlSeparatorChar(string path) + { + path = path.Replace('\\', '/'); return path; } diff --git a/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs b/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs index 92c4f7589b..cf7efdb4c4 100644 --- a/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs +++ b/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs @@ -22,8 +22,8 @@ namespace Umbraco.Core.Logging private CancellationTokenSource _loggingCancelationTokenSource; private CancellationToken _loggingCancelationToken; private Task _loggingTask; - private Double _shutdownFlushTimeout = 5; - private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(5); + private Double _shutdownFlushTimeout = 1; + private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(1); private static readonly Type ThisType = typeof(ParallelForwardingAppender); private volatile bool _shutDownRequested; private int? _bufferSize = DefaultBufferSize; diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs index 571c916123..e30d188339 100644 --- a/src/Umbraco.Core/Manifest/ManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ManifestParser.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -140,11 +141,22 @@ namespace Umbraco.Core.Manifest { var result = new List(); foreach (var m in manifestFileContents) - { - if (m.IsNullOrWhiteSpace()) continue; + { + var manifestContent = m; + + if (manifestContent.IsNullOrWhiteSpace()) continue; + + // Strip byte object marker, JSON.NET does not like it + var preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); + + // Strangely StartsWith(preamble) would always return true + if (manifestContent.Substring(0, 1) == preamble) + manifestContent = manifestContent.Remove(0, preamble.Length); + + if (manifestContent.IsNullOrWhiteSpace()) continue; //remove any comments first - var replaced = CommentsSurround.Replace(m, match => " "); + var replaced = CommentsSurround.Replace(manifestContent, match => " "); replaced = CommentsLine.Replace(replaced, match => ""); JObject deserialized; @@ -154,7 +166,7 @@ namespace Umbraco.Core.Manifest } catch (Exception ex) { - LogHelper.Error("An error occurred parsing manifest with contents: " + m, ex); + LogHelper.Error("An error occurred parsing manifest with contents: " + manifestContent, ex); continue; } diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs index ae4aac6a1c..8ead6da5f8 100644 --- a/src/Umbraco.Core/Models/File.cs +++ b/src/Umbraco.Core/Models/File.cs @@ -2,6 +2,8 @@ using System.IO; using System.Reflection; using System.Runtime.Serialization; +using System.Text; +using Umbraco.Core.IO; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models @@ -15,12 +17,20 @@ namespace Umbraco.Core.Models { private string _path; private string _originalPath; - private string _content = string.Empty; //initialize to empty string, not null - protected File(string path) + // initialize to string.Empty so that it is possible to save a new file, + // should use the lazyContent ctor to set it to null when loading existing. + // cannot simply use HasIdentity as some classes (eg Script) override it + // in a weird way. + private string _content; + internal Func GetFileContent { get; set; } + + protected File(string path, Func getFileContent = null) { - _path = path; + _path = SanitizePath(path); _originalPath = _path; + GetFileContent = getFileContent; + _content = getFileContent != null ? null : string.Empty; } private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.Content); @@ -28,6 +38,14 @@ namespace Umbraco.Core.Models private string _alias; private string _name; + private static string SanitizePath(string path) + { + return path + .Replace('\\', System.IO.Path.DirectorySeparatorChar) + .Replace('/', System.IO.Path.DirectorySeparatorChar); + //.TrimStart(System.IO.Path.DirectorySeparatorChar); + } + /// /// Gets or sets the Name of the File including extension /// @@ -71,7 +89,7 @@ namespace Umbraco.Core.Models SetPropertyValueAndDetectChanges(o => { - _path = value; + _path = SanitizePath(value); return _path; }, _path, PathSelector); } @@ -96,15 +114,26 @@ namespace Umbraco.Core.Models /// /// Gets or sets the Content of a File /// + /// Marked as DoNotClone, because it should be lazy-reloaded from disk. [DataMember] + [DoNotClone] public virtual string Content { - get { return _content; } + get + { + if (_content != null) + return _content; + + // else, must lazy-load, and ensure it's not null + if (GetFileContent != null) + _content = GetFileContent(this); + return _content ?? (_content = string.Empty); + } set { SetPropertyValueAndDetectChanges(o => { - _content = value; + _content = value ?? string.Empty; // cannot set to null return _content; }, _content, ContentSelector); } @@ -121,17 +150,32 @@ namespace Umbraco.Core.Models return true; } + // this exists so that class that manage name and alias differently, eg Template, + // can implement their own cloning - (though really, not sure it's even needed) + protected virtual void DeepCloneNameAndAlias(File clone) + { + // set fields that have a lazy value, by forcing evaluation of the lazy + clone._name = Name; + clone._alias = Alias; + } + public override object DeepClone() { - var clone = (File)base.DeepClone(); - //turn off change tracking + var clone = (File) base.DeepClone(); + + // clear fields that were memberwise-cloned and that we don't want to clone + clone._content = null; + + // turn off change tracking clone.DisableChangeTracking(); - //need to manually assign since they are readonly properties - clone._alias = Alias; - clone._name = Name; - //this shouldn't really be needed since we're not tracking + + // ... + DeepCloneNameAndAlias(clone); + + // this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); - //re-enable tracking + + // re-enable tracking clone.EnableChangeTracking(); return clone; diff --git a/src/Umbraco.Core/Models/IPublishedContentWithKey.cs b/src/Umbraco.Core/Models/IPublishedContentWithKey.cs new file mode 100644 index 0000000000..b0e71221b2 --- /dev/null +++ b/src/Umbraco.Core/Models/IPublishedContentWithKey.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a cached content with a GUID key. + /// + /// This is temporary, because we cannot add the Key property to IPublishedContent without + /// breaking backward compatibility. With v8, it will be merged into IPublishedContent. + public interface IPublishedContentWithKey : IPublishedContent + { + Guid Key { get; } + } +} diff --git a/src/Umbraco.Core/Models/IServerRegistration.cs b/src/Umbraco.Core/Models/IServerRegistration.cs index 9eb6815bbe..8053ea0a6e 100644 --- a/src/Umbraco.Core/Models/IServerRegistration.cs +++ b/src/Umbraco.Core/Models/IServerRegistration.cs @@ -18,6 +18,11 @@ namespace Umbraco.Core.Models /// bool IsActive { get; set; } + /// + /// Gets or sets a value indicating whether the server is master. + /// + bool IsMaster { get; set; } + /// /// Gets the date and time the registration was created. /// diff --git a/src/Umbraco.Core/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs index d483de4176..75914820f0 100644 --- a/src/Umbraco.Core/Models/PartialView.cs +++ b/src/Umbraco.Core/Models/PartialView.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text.RegularExpressions; -using Umbraco.Core.IO; +using Umbraco.Core.Services; namespace Umbraco.Core.Models { @@ -15,12 +12,14 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class PartialView : File, IPartialView { - public PartialView(string path) - : base(path) - { - base.Path = path; - } + : this(path, null) + { } + internal PartialView(string path, Func getFileContent) + : base(path, getFileContent) + { } + + internal PartialViewType ViewType { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PartialViewType.cs b/src/Umbraco.Core/Models/PartialViewType.cs new file mode 100644 index 0000000000..2b45448271 --- /dev/null +++ b/src/Umbraco.Core/Models/PartialViewType.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Models +{ + internal enum PartialViewType : byte + { + Unknown = 0, // default + PartialView = 1, + PartialViewMacro = 2 + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs index 8c0eeef86f..fef066e0b1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs @@ -62,9 +62,20 @@ namespace Umbraco.Core.Models.PublishedContent // model and therefore returned the original content unchanged. var model = content.CreateModel(); - var extended = model == content // == means the factory did not create a model - ? new PublishedContentExtended(content) // so we have to extend - : model; // else we can use what the factory returned + IPublishedContent extended; + if (model == content) // == means the factory did not create a model + { + // so we have to extend + var contentWithKey = content as IPublishedContentWithKey; + extended = contentWithKey == null + ? new PublishedContentExtended(content) + : new PublishedContentWithKeyExtended(contentWithKey); + } + else + { + // else we can use what the factory returned + extended = model; + } // so extended should always implement IPublishedContentExtended, however if // by mistake the factory returned a different object that does not implement diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs new file mode 100644 index 0000000000..492fd79796 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + public class PublishedContentWithKeyExtended : PublishedContentExtended, IPublishedContentWithKey + { + // protected for models, internal for PublishedContentExtended static Extend method + protected internal PublishedContentWithKeyExtended(IPublishedContentWithKey content) + : base(content) + { } + + public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs new file mode 100644 index 0000000000..4761a52617 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + public abstract class PublishedContentWithKeyModel : PublishedContentModel, IPublishedContentWithKey + { + protected PublishedContentWithKeyModel(IPublishedContentWithKey content) + : base (content) + { } + + public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs new file mode 100644 index 0000000000..35d7dd6f1f --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs @@ -0,0 +1,17 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Provides an abstract base class for IPublishedContentWithKey implementations that + /// wrap and extend another IPublishedContentWithKey. + /// + public class PublishedContentWithKeyWrapped : PublishedContentWrapped, IPublishedContentWithKey + { + protected PublishedContentWithKeyWrapped(IPublishedContentWithKey content) + : base(content) + { } + + public virtual Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } } + } +} diff --git a/src/Umbraco.Core/Models/Rdbms/AccessDto.cs b/src/Umbraco.Core/Models/Rdbms/AccessDto.cs index ba2cb6d767..37b1dbddd8 100644 --- a/src/Umbraco.Core/Models/Rdbms/AccessDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/AccessDto.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Security.AccessControl; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -30,11 +31,11 @@ namespace Umbraco.Core.Models.Rdbms public int NoAccessNodeId { get; set; } [Column("createDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime CreateDate { get; set; } [Column("updateDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime UpdateDate { get; set; } [ResultColumn] diff --git a/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs b/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs index a98dfb9450..78e3444e56 100644 --- a/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -25,11 +26,11 @@ namespace Umbraco.Core.Models.Rdbms public string RuleType { get; set; } [Column("createDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime CreateDate { get; set; } [Column("updateDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime UpdateDate { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs index 9b1fa02dae..3bd85cccf6 100644 --- a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -23,7 +24,7 @@ namespace Umbraco.Core.Models.Rdbms public Guid VersionId { get; set; } [Column("VersionDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime VersionDate { get; set; } [ResultColumn] diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs index de087667b2..88e04087c9 100644 --- a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -38,7 +39,7 @@ namespace Umbraco.Core.Models.Rdbms public DateTime? ExpiresDate { get; set; } [Column("updateDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime UpdateDate { get; set; } [Column("templateId")] diff --git a/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs b/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs index 652c9df714..803c25fdfc 100644 --- a/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -27,7 +28,7 @@ namespace Umbraco.Core.Models.Rdbms public string ProviderKey { get; set; } [Column("createDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime CreateDate { get; set; } diff --git a/src/Umbraco.Core/Models/Rdbms/LogDto.cs b/src/Umbraco.Core/Models/Rdbms/LogDto.cs index 3c2a6a61a5..be67b5873a 100644 --- a/src/Umbraco.Core/Models/Rdbms/LogDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/LogDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -21,7 +22,7 @@ namespace Umbraco.Core.Models.Rdbms public int NodeId { get; set; } [Column("Datestamp")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime Datestamp { get; set; } [Column("logHeader")] diff --git a/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs index ffd000278d..5ab339fe01 100644 --- a/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -19,7 +20,7 @@ namespace Umbraco.Core.Models.Rdbms public string Name { get; set; } [Column("createDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime CreateDate { get; set; } [Column("version")] diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs index d7a859ef06..7003c58e77 100644 --- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -48,7 +49,7 @@ namespace Umbraco.Core.Models.Rdbms [Column("uniqueID")] [NullSetting(NullSetting = NullSettings.NotNull)] [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoNodeUniqueID")] - [Constraint(Default = "newid()")] + [Constraint(Default = SystemMethods.NewGuid)] public Guid UniqueId { get; set; } [Column("text")] @@ -61,7 +62,7 @@ namespace Umbraco.Core.Models.Rdbms public Guid? NodeObjectType { get; set; } [Column("createDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime CreateDate { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs index 621ace555b..74a6d34289 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -61,7 +62,7 @@ namespace Umbraco.Core.Models.Rdbms [Column("UniqueID")] [NullSetting(NullSetting = NullSettings.NotNull)] - [Constraint(Default = "newid()")] + [Constraint(Default = SystemMethods.NewGuid)] [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")] public Guid UniqueId { get; set; } } diff --git a/src/Umbraco.Core/Models/Rdbms/RelationDto.cs b/src/Umbraco.Core/Models/Rdbms/RelationDto.cs index e204e42040..368904a5cb 100644 --- a/src/Umbraco.Core/Models/Rdbms/RelationDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/RelationDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -26,7 +27,7 @@ namespace Umbraco.Core.Models.Rdbms public int RelationType { get; set; } [Column("datetime")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime Datetime { get; set; } [Column("comment")] diff --git a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs index b7bdf265ce..2a3751c083 100644 --- a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -23,7 +24,7 @@ namespace Umbraco.Core.Models.Rdbms public string ServerIdentity { get; set; } [Column("registeredDate")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime DateRegistered { get; set; } [Column("lastNotifiedDate")] @@ -33,6 +34,7 @@ namespace Umbraco.Core.Models.Rdbms [Index(IndexTypes.NonClustered)] public bool IsActive { get; set; } - + [Column("isMaster")] + public bool IsMaster { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/TaskDto.cs b/src/Umbraco.Core/Models/Rdbms/TaskDto.cs index 75e4d25cb7..e27f7c0a93 100644 --- a/src/Umbraco.Core/Models/Rdbms/TaskDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/TaskDto.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseAnnotations; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; namespace Umbraco.Core.Models.Rdbms { @@ -34,7 +35,7 @@ namespace Umbraco.Core.Models.Rdbms public int UserId { get; set; } [Column("DateTime")] - [Constraint(Default = "getdate()")] + [Constraint(Default = SystemMethods.CurrentDateTime)] public DateTime DateTime { get; set; } [Column("Comment")] diff --git a/src/Umbraco.Core/Models/Script.cs b/src/Umbraco.Core/Models/Script.cs index 71f0684447..325885d9ba 100644 --- a/src/Umbraco.Core/Models/Script.cs +++ b/src/Umbraco.Core/Models/Script.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using System.Runtime.Serialization; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -15,16 +13,17 @@ namespace Umbraco.Core.Models public class Script : File { public Script(string path) - : base(path) - { - - } + : this(path, (Func) null) + { } + + internal Script(string path, Func getFileContent) + : base(path, getFileContent) + { } [Obsolete("This is no longer used and will be removed from the codebase in future versions")] public Script(string path, IContentSection contentConfig) - : this(path) - { - } + : base(path) + { } /// /// Indicates whether the current entity has an identity, which in this case is a path/name. diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs index 9a871859b6..cee70893d0 100644 --- a/src/Umbraco.Core/Models/ServerRegistration.cs +++ b/src/Umbraco.Core/Models/ServerRegistration.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.Reflection; using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Sync; namespace Umbraco.Core.Models { @@ -14,10 +13,12 @@ namespace Umbraco.Core.Models private string _serverAddress; private string _serverIdentity; private bool _isActive; + private bool _isMaster; private static readonly PropertyInfo ServerAddressSelector = ExpressionHelper.GetPropertyInfo(x => x.ServerAddress); private static readonly PropertyInfo ServerIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.ServerIdentity); private static readonly PropertyInfo IsActiveSelector = ExpressionHelper.GetPropertyInfo(x => x.IsActive); + private static readonly PropertyInfo IsMasterSelector = ExpressionHelper.GetPropertyInfo(x => x.IsMaster); /// /// Initialiazes a new instance of the class. @@ -34,7 +35,8 @@ namespace Umbraco.Core.Models /// The date and time the registration was created. /// The date and time the registration was last accessed. /// A value indicating whether the registration is active. - public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive) + /// A value indicating whether the registration is master. + public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive, bool isMaster) { UpdateDate = accessed; CreateDate = registered; @@ -43,6 +45,7 @@ namespace Umbraco.Core.Models ServerAddress = serverAddress; ServerIdentity = serverIdentity; IsActive = isActive; + IsMaster = isMaster; } /// @@ -108,6 +111,22 @@ namespace Umbraco.Core.Models } } + /// + /// Gets or sets a value indicating whether the server is master. + /// + public bool IsMaster + { + get { return _isMaster; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _isMaster = value; + return _isMaster; + }, _isMaster, IsMasterSelector); + } + } + /// /// Gets the date and time the registration was created. /// @@ -124,7 +143,7 @@ namespace Umbraco.Core.Models /// public override string ToString() { - return string.Format("{{\"{0}\", \"{1}\", {2}active}}", ServerAddress, ServerIdentity, IsActive ? "" : "!"); + return string.Format("{{\"{0}\", \"{1}\", {2}active, {3}master}}", ServerAddress, ServerIdentity, IsActive ? "" : "!", IsMaster ? "" : "!"); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Stylesheet.cs b/src/Umbraco.Core/Models/Stylesheet.cs index ca203d835d..060246df54 100644 --- a/src/Umbraco.Core/Models/Stylesheet.cs +++ b/src/Umbraco.Core/Models/Stylesheet.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Data; using System.Linq; using System.Runtime.Serialization; -using System.Text; using Umbraco.Core.IO; using Umbraco.Core.Strings.Css; @@ -16,12 +16,16 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public class Stylesheet : File { - public Stylesheet(string path) - : base(path.EnsureEndsWith(".css")) - { + public Stylesheet(string path) + : this(path, null) + { } + + internal Stylesheet(string path, Func getFileContent) + : base(path.EnsureEndsWith(".css"), getFileContent) + { InitializeProperties(); } - + private Lazy> _properties; private void InitializeProperties() @@ -81,7 +85,7 @@ namespace Umbraco.Core.Models /// /// /// - void Property_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + void Property_PropertyChanged(object sender, PropertyChangedEventArgs e) { var prop = (StylesheetProperty) sender; diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs index 3a8cbf8794..4aca88f286 100644 --- a/src/Umbraco.Core/Models/Template.cs +++ b/src/Umbraco.Core/Models/Template.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Drawing; +using System.IO; using System.Reflection; using System.Runtime.Serialization; +using System.Text; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; @@ -29,12 +32,16 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); public Template(string name, string alias) - : base(string.Empty) + : this(name, alias, (Func) null) + { } + + internal Template(string name, string alias, Func getFileContent) + : base(string.Empty, getFileContent) { _name = name; _alias = alias.ToCleanString(CleanStringType.UnderscoreAlias); _masterTemplateId = new Lazy(() => -1); - } + } [Obsolete("This constructor should not be used, file path is determined by alias, setting the path here will have no affect")] public Template(string path, string name, string alias) @@ -123,7 +130,6 @@ namespace Umbraco.Core.Models Key = Guid.NewGuid(); } - public void SetMasterTemplate(ITemplate masterTemplate) { if (masterTemplate == null) @@ -139,27 +145,9 @@ namespace Umbraco.Core.Models } - public override object DeepClone() + protected override void DeepCloneNameAndAlias(File clone) { - - //We cannot call in to the base classes to clone because the base File class treats Alias, Name.. differently so we need to manually do the clone - - //Memberwise clone on Entity will work since it doesn't have any deep elements - // for any sub class this will work for standard properties as well that aren't complex object's themselves. - var clone = (Template)MemberwiseClone(); - //turn off change tracking - clone.DisableChangeTracking(); - //Automatically deep clone ref properties that are IDeepCloneable - DeepCloneHelper.DeepCloneRefProperties(this, clone); - - //this shouldn't really be needed since we're not tracking - clone.ResetDirtyProperties(false); - //re-enable tracking - clone.EnableChangeTracking(); - - return clone; + // do nothing - prevents File from doing its stuff } - - } } diff --git a/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs b/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs index 599f599f85..33c7616687 100644 --- a/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs +++ b/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs @@ -20,6 +20,6 @@ namespace Umbraco.Core.Persistence.DatabaseAnnotations /// /// Gets or sets the Default value /// - public string Default { get; set; } + public object Default { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs index b7cacf0899..24c2294b59 100644 --- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs +++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs @@ -3,8 +3,8 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions public enum SystemMethods { NewGuid, - NewSequentialId, CurrentDateTime, - CurrentUTCDateTime + //NewSequentialId, + //CurrentUTCDateTime } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs new file mode 100644 index 0000000000..3e6d245416 --- /dev/null +++ b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Runtime.CompilerServices; + +namespace Umbraco.Core.Persistence +{ + internal static class DatabaseNodeLockExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ValidateDatabase(UmbracoDatabase database) + { + if (database == null) + throw new ArgumentNullException("database"); + if (database.CurrentTransactionIsolationLevel < IsolationLevel.RepeatableRead) + throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required."); + } + + // updating a record within a repeatable-read transaction gets an exclusive lock on + // that record which will be kept until the transaction is ended, effectively locking + // out all other accesses to that record - thus obtaining an exclusive lock over the + // protected resources. + public static void AcquireLockNodeWriteLock(this UmbracoDatabase database, int nodeId) + { + ValidateDatabase(database); + + database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id", + new { @id = nodeId }); + } + + // reading a record within a repeatable-read transaction gets a shared lock on + // that record which will be kept until the transaction is ended, effectively preventing + // other write accesses to that record - thus obtaining a shared lock over the protected + // resources. + public static void AcquireLockNodeReadLock(this UmbracoDatabase database, int nodeId) + { + ValidateDatabase(database); + + database.ExecuteScalar("SELECT sortOrder FROM umbracoNode WHERE id=@id", + new { @id = nodeId }); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs index 9ecc02e213..9c315aef46 100644 --- a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Factories { public ServerRegistration BuildEntity(ServerRegistrationDto dto) { - var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive); + var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive, dto.IsMaster); //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 model.ResetDirtyProperties(false); @@ -21,6 +21,7 @@ namespace Umbraco.Core.Persistence.Factories ServerAddress = entity.ServerAddress, DateRegistered = entity.CreateDate, IsActive = entity.IsActive, + IsMaster = ((ServerRegistration) entity).IsMaster, DateAccessed = entity.UpdateDate, ServerIdentity = entity.ServerIdentity }; diff --git a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs index d1742dc030..60cde916b6 100644 --- a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs @@ -33,9 +33,9 @@ namespace Umbraco.Core.Persistence.Factories #region Implementation of IEntityFactory - public Template BuildEntity(TemplateDto dto, IEnumerable childDefinitions) + public Template BuildEntity(TemplateDto dto, IEnumerable childDefinitions, Func getFileContent) { - var template = new Template(dto.NodeDto.Text, dto.Alias) + var template = new Template(dto.NodeDto.Text, dto.Alias, getFileContent) { CreateDate = dto.NodeDto.CreateDate, Id = dto.NodeId, diff --git a/src/Umbraco.Core/Persistence/LockedRepository.cs b/src/Umbraco.Core/Persistence/LockedRepository.cs new file mode 100644 index 0000000000..b5d2d672f2 --- /dev/null +++ b/src/Umbraco.Core/Persistence/LockedRepository.cs @@ -0,0 +1,27 @@ +using System; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence +{ + internal class LockedRepository + where TRepository : IDisposable, IRepository + { + public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository) + { + Transaction = transaction; + UnitOfWork = unitOfWork; + Repository = repository; + } + + public Transaction Transaction { get; private set; } + public IDatabaseUnitOfWork UnitOfWork { get; private set; } + public TRepository Repository { get; private set; } + + public void Commit() + { + UnitOfWork.Commit(); + Transaction.Complete(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs new file mode 100644 index 0000000000..f513073e71 --- /dev/null +++ b/src/Umbraco.Core/Persistence/LockingRepository.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence +{ + internal class LockingRepository + where TRepository : IDisposable, IRepository + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly Func _repositoryFactory; + private readonly int[] _readLockIds, _writeLockIds; + + public LockingRepository(IDatabaseUnitOfWorkProvider uowProvider, Func repositoryFactory, + IEnumerable readLockIds, IEnumerable writeLockIds) + { + Mandate.ParameterNotNull(uowProvider, "uowProvider"); + Mandate.ParameterNotNull(repositoryFactory, "repositoryFactory"); + + _uowProvider = uowProvider; + _repositoryFactory = repositoryFactory; + _readLockIds = readLockIds == null ? new int[0] : readLockIds.ToArray(); + _writeLockIds = writeLockIds == null ? new int[0] : writeLockIds.ToArray(); + } + + public void WithReadLocked(Action> action, bool autoCommit = true) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + { + foreach (var lockId in _readLockIds) + uow.Database.AcquireLockNodeReadLock(lockId); + + using (var repository = _repositoryFactory(uow)) + { + action(new LockedRepository(transaction, uow, repository)); + if (autoCommit == false) return; + uow.Commit(); + transaction.Complete(); + } + } + } + + public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + { + foreach (var lockId in _readLockIds) + uow.Database.AcquireLockNodeReadLock(lockId); + + using (var repository = _repositoryFactory(uow)) + { + var ret = func(new LockedRepository(transaction, uow, repository)); + if (autoCommit == false) return ret; + uow.Commit(); + transaction.Complete(); + return ret; + } + } + } + + public void WithWriteLocked(Action> action, bool autoCommit = true) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + { + foreach (var lockId in _writeLockIds) + uow.Database.AcquireLockNodeWriteLock(lockId); + + using (var repository = _repositoryFactory(uow)) + { + action(new LockedRepository(transaction, uow, repository)); + if (autoCommit == false) return; + uow.Commit(); + transaction.Complete(); + } + } + } + + public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true) + { + var uow = _uowProvider.GetUnitOfWork(); + using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + { + foreach (var lockId in _writeLockIds) + uow.Database.AcquireLockNodeReadLock(lockId); + + using (var repository = _repositoryFactory(uow)) + { + var ret = func(new LockedRepository(transaction, uow, repository)); + if (autoCommit == false) return ret; + uow.Commit(); + transaction.Complete(); + return ret; + } + } + } + } +} diff --git a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs index e532db18aa..54d7e8cdc1 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs @@ -30,6 +30,7 @@ namespace Umbraco.Core.Persistence.Mappers { CacheMap(src => src.Id, dto => dto.Id); CacheMap(src => src.IsActive, dto => dto.IsActive); + CacheMap(src => src.IsMaster, dto => dto.IsMaster); CacheMap(src => src.ServerAddress, dto => dto.ServerAddress); CacheMap(src => src.CreateDate, dto => dto.DateRegistered); CacheMap(src => src.UpdateDate, dto => dto.DateAccessed); diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs index e37d001fea..a19aaa24ad 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs @@ -16,13 +16,7 @@ namespace Umbraco.Core.Persistence.Migrations { public ISqlSyntaxProvider SqlSyntax { get; private set; } public ILogger Logger { get; private set; } - - [Obsolete("Use the other constructor specifying all dependencies instead")] - protected MigrationBase() - : this(SqlSyntaxContext.SqlSyntaxProvider, LoggerResolver.Current.Logger) - { - } - + protected MigrationBase(ISqlSyntaxProvider sqlSyntax, ILogger logger) { SqlSyntax = sqlSyntax; @@ -48,7 +42,7 @@ namespace Umbraco.Core.Persistence.Migrations public IAlterSyntaxBuilder Alter { - get { return new AlterSyntaxBuilder(Context); } + get { return new AlterSyntaxBuilder(Context, SqlSyntax); } } public ICreateBuilder Create @@ -63,27 +57,27 @@ namespace Umbraco.Core.Persistence.Migrations public IExecuteBuilder Execute { - get { return new ExecuteBuilder(Context); } + get { return new ExecuteBuilder(Context, SqlSyntax); } } public IInsertBuilder Insert { - get { return new InsertBuilder(Context); } + get { return new InsertBuilder(Context, SqlSyntax); } } public IRenameBuilder Rename { - get { return new RenameBuilder(Context); } + get { return new RenameBuilder(Context, SqlSyntax); } } public IUpdateBuilder Update { - get { return new UpdateBuilder(Context); } + get { return new UpdateBuilder(Context, SqlSyntax); } } public IIfDatabaseBuilder IfDatabase(params DatabaseProviders[] databaseProviders) { - return new IfDatabaseBuilder(Context, databaseProviders); + return new IfDatabaseBuilder(Context, SqlSyntax, databaseProviders); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs index 7dde31d3e3..35d199d5f0 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs @@ -7,22 +7,22 @@ namespace Umbraco.Core.Persistence.Migrations { public abstract class MigrationExpressionBase : IMigrationExpression { - [Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] - protected MigrationExpressionBase() - : this(SqlSyntaxContext.SqlSyntaxProvider) - { - } + //[Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] + //protected MigrationExpressionBase() + // : this(SqlSyntaxContext.SqlSyntaxProvider) + //{ + //} - [Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] - protected MigrationExpressionBase(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : this(current, databaseProviders, SqlSyntaxContext.SqlSyntaxProvider) - { - } + //[Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] + //protected MigrationExpressionBase(DatabaseProviders current, DatabaseProviders[] databaseProviders) + // : this(current, databaseProviders, SqlSyntaxContext.SqlSyntaxProvider) + //{ + //} - protected MigrationExpressionBase(ISqlSyntaxProvider sqlSyntax) - { - SqlSyntax = sqlSyntax; - } + //protected MigrationExpressionBase(ISqlSyntaxProvider sqlSyntax) + //{ + // SqlSyntax = sqlSyntax; + //} protected MigrationExpressionBase(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) { @@ -76,8 +76,10 @@ namespace Umbraco.Core.Persistence.Migrations case TypeCode.UInt32: case TypeCode.UInt64: return val.ToString(); + case TypeCode.DateTime: + return SqlSyntax.GetQuotedValue(SqlSyntax.FormatDateTime((DateTime) val)); default: - return SqlSyntaxContext.SqlSyntaxProvider.GetQuotedValue(val.ToString()); + return SqlSyntax.GetQuotedValue(val.ToString()); } } } diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs index 539b00fe42..234cd67386 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using log4net; using Semver; using Umbraco.Core.Configuration; @@ -243,13 +245,43 @@ namespace Umbraco.Core.Persistence.Migrations //TODO: We should output all of these SQL calls to files in a migration folder in App_Data/TEMP // so if people want to executed them manually on another environment, they can. - _logger.Info("Executing sql statement " + i + ": " + sql); - database.Execute(sql); + //The following ensures the multiple statement sare executed one at a time, this is a requirement + // of SQLCE, it's unfortunate but necessary. + // http://stackoverflow.com/questions/13665491/sql-ce-inconsistent-with-multiple-statements + var sb = new StringBuilder(); + using (var reader = new StringReader(sql)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + line = line.Trim(); + if (line.Equals("GO", StringComparison.OrdinalIgnoreCase)) + { + //Execute the SQL up to the point of a GO statement + var exeSql = sb.ToString(); + _logger.Info("Executing sql statement " + i + ": " + exeSql); + database.Execute(exeSql); + + //restart the string builder + sb.Remove(0, sb.Length); + } + else + { + sb.AppendLine(line); + } + } + //execute anything remaining + if (sb.Length > 0) + { + var exeSql = sb.ToString(); + _logger.Info("Executing sql statement " + i + ": " + exeSql); + database.Execute(exeSql); + } + } + i++; } - - transaction.Complete(); //Now that this is all complete, we need to add an entry to the migrations table flagging that migrations diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs index 299550d83e..e253f8e607 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs @@ -1,30 +1,35 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter { public class AlterSyntaxBuilder : IAlterSyntaxBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; + private readonly DatabaseProviders[] _databaseProviders; - public AlterSyntaxBuilder(IMigrationContext context) + public AlterSyntaxBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; + _databaseProviders = databaseProviders; } public IAlterTableSyntax Table(string tableName) { - var expression = new AlterTableExpression { TableName = tableName }; + var expression = new AlterTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; //_context.Expressions.Add(expression); - return new AlterTableBuilder(expression, _context); + return new AlterTableBuilder(_context, _databaseProviders, expression); } public IAlterColumnSyntax Column(string columnName) { - var expression = new AlterColumnExpression { Column = { Name = columnName } }; + var expression = new AlterColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {Column = {Name = columnName}}; //_context.Expressions.Add(expression); - return new AlterColumnBuilder(expression, _context); + return new AlterColumnBuilder(_context, _databaseProviders, expression); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs index 3b77743b0d..a7077f2d35 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs @@ -2,6 +2,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column { @@ -11,11 +12,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column IAlterColumnOptionForeignKeyCascadeSyntax { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public AlterColumnBuilder(AlterColumnExpression expression, IMigrationContext context) + public AlterColumnBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, AlterColumnExpression expression) : base(expression) { _context = context; + _databaseProviders = databaseProviders; } public ForeignKeyDefinition CurrentForeignKey { get; set; } @@ -33,7 +36,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column public IAlterColumnOptionSyntax WithDefault(SystemMethods method) { - var dc = new AlterDefaultConstraintExpression + var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax) { TableName = Expression.TableName, SchemaName = Expression.SchemaName, @@ -51,7 +54,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column public IAlterColumnOptionSyntax WithDefaultValue(object value) { - var dc = new AlterDefaultConstraintExpression + var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax) { TableName = Expression.TableName, SchemaName = Expression.SchemaName, @@ -81,15 +84,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column { Expression.Column.IsIndexed = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -135,16 +135,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column { Expression.Column.IsUnique = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName, - IsUnique = true - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName, + IsUnique = true + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -172,17 +169,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column { Expression.Column.IsForeignKey = true; - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = primaryTableName, - PrimaryTableSchema = primaryTableSchema, - ForeignTable = Expression.TableName, - ForeignTableSchema = Expression.SchemaName - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = primaryTableName, + PrimaryTableSchema = primaryTableSchema, + ForeignTable = Expression.TableName, + ForeignTableSchema = Expression.SchemaName + }); fk.ForeignKey.PrimaryColumns.Add(primaryColumnName); fk.ForeignKey.ForeignColumns.Add(Expression.Column.Name); @@ -212,17 +206,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column public IAlterColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema, string foreignTableName, string foreignColumnName) { - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = Expression.TableName, - PrimaryTableSchema = Expression.SchemaName, - ForeignTable = foreignTableName, - ForeignTableSchema = foreignTableSchema - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = Expression.TableName, + PrimaryTableSchema = Expression.SchemaName, + ForeignTable = foreignTableName, + ForeignTableSchema = foreignTableSchema + }); fk.ForeignKey.PrimaryColumns.Add(Expression.Column.Name); fk.ForeignKey.ForeignColumns.Add(foreignColumnName); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs index 43be956440..a40e7be320 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs @@ -6,11 +6,11 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { public class AlterColumnExpression : MigrationExpressionBase { - public AlterColumnExpression(ISqlSyntaxProvider sqlSyntax) - : base(sqlSyntax) - { - Column = new ColumnDefinition() { ModificationType = ModificationType.Alter }; - } + //public AlterColumnExpression(ISqlSyntaxProvider sqlSyntax) + // : base(sqlSyntax) + //{ + // Column = new ColumnDefinition() { ModificationType = ModificationType.Alter }; + //} public AlterColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) : base(current, databaseProviders, sqlSyntax) @@ -18,17 +18,17 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions Column = new ColumnDefinition() { ModificationType = ModificationType.Alter }; } - [Obsolete("Use the constructor specifying an ISqlSyntaxProvider instead")] - public AlterColumnExpression() - : this(SqlSyntaxContext.SqlSyntaxProvider) - { - } + //[Obsolete("Use the constructor specifying an ISqlSyntaxProvider instead")] + //public AlterColumnExpression() + // : this(SqlSyntaxContext.SqlSyntaxProvider) + //{ + //} - [Obsolete("Use the constructor specifying an ISqlSyntaxProvider instead")] - public AlterColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : this(current, databaseProviders, SqlSyntaxContext.SqlSyntaxProvider) - { - } + //[Obsolete("Use the constructor specifying an ISqlSyntaxProvider instead")] + //public AlterColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) + // : this(current, databaseProviders, SqlSyntaxContext.SqlSyntaxProvider) + //{ + //} @@ -42,9 +42,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(Column.Name), // SqlSyntaxContext.SqlSyntaxProvider.Format(Column)); - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.AlterColumn, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), - SqlSyntaxContext.SqlSyntaxProvider.Format(Column)); + return string.Format(SqlSyntax.AlterColumn, + SqlSyntax.GetQuotedTableName(TableName), + SqlSyntax.Format(Column)); //return string.Format(SqlSyntaxContext.SqlSyntaxProvider.AlterColumn, // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs index cd99b6754f..cd83f55a35 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs @@ -4,11 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { public class AlterDefaultConstraintExpression : MigrationExpressionBase { - public AlterDefaultConstraintExpression() - { - } - - public AlterDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + public AlterDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -22,9 +19,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { //NOTE Should probably investigate if Deleting a Default Constraint is different from deleting a 'regular' constraint - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedName(ConstraintName)); + return string.Format(SqlSyntax.DeleteConstraint, + SqlSyntax.GetQuotedTableName(TableName), + SqlSyntax.GetQuotedName(ConstraintName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs index 360b699cd8..b5906ed324 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs @@ -1,12 +1,12 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions { public class AlterTableExpression : MigrationExpressionBase { - public AlterTableExpression() - { - } - public AlterTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + public AlterTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs index a50e393e96..7a6ab8d47a 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs @@ -2,6 +2,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table { @@ -10,11 +11,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table IAlterTableColumnOptionForeignKeyCascadeSyntax { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public AlterTableBuilder(AlterTableExpression expression, IMigrationContext context) + public AlterTableBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, AlterTableExpression expression) : base(expression) { _context = context; + _databaseProviders = databaseProviders; } public ColumnDefinition CurrentColumn { get; set; } @@ -36,7 +39,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table { if (CurrentColumn.ModificationType == ModificationType.Alter) { - var dc = new AlterDefaultConstraintExpression + var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax) { TableName = Expression.TableName, SchemaName = Expression.SchemaName, @@ -66,15 +69,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table { CurrentColumn.IsIndexed = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -120,16 +120,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table { CurrentColumn.IsUnique = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName, - IsUnique = true - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName, + IsUnique = true + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -157,17 +154,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table { CurrentColumn.IsForeignKey = true; - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = primaryTableName, - PrimaryTableSchema = primaryTableSchema, - ForeignTable = Expression.TableName, - ForeignTableSchema = Expression.SchemaName - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = primaryTableName, + PrimaryTableSchema = primaryTableSchema, + ForeignTable = Expression.TableName, + ForeignTableSchema = Expression.SchemaName + }); fk.ForeignKey.PrimaryColumns.Add(primaryColumnName); fk.ForeignKey.ForeignColumns.Add(CurrentColumn.Name); @@ -197,17 +191,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table public IAlterTableColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema, string foreignTableName, string foreignColumnName) { - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = Expression.TableName, - PrimaryTableSchema = Expression.SchemaName, - ForeignTable = foreignTableName, - ForeignTableSchema = foreignTableSchema - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = Expression.TableName, + PrimaryTableSchema = Expression.SchemaName, + ForeignTable = foreignTableName, + ForeignTableSchema = foreignTableSchema + }); fk.ForeignKey.PrimaryColumns.Add(CurrentColumn.Name); fk.ForeignKey.ForeignColumns.Add(foreignColumnName); @@ -220,7 +211,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table public IAlterTableColumnTypeSyntax AddColumn(string name) { var column = new ColumnDefinition { Name = name, ModificationType = ModificationType.Create }; - var createColumn = new CreateColumnExpression + var createColumn = new CreateColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax) { Column = column, SchemaName = Expression.SchemaName, @@ -236,12 +227,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table public IAlterTableColumnTypeSyntax AlterColumn(string name) { var column = new ColumnDefinition { Name = name, ModificationType = ModificationType.Alter }; - var alterColumn = new AlterColumnExpression - { - Column = column, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName - }; + var alterColumn = new AlterColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax) + { + Column = column, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName + }; CurrentColumn = column; diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs index 439733bc46..d004cd1176 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs @@ -1,6 +1,7 @@ using System.Data; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column { @@ -10,11 +11,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column ICreateColumnOptionForeignKeyCascadeSyntax { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public CreateColumnBuilder(CreateColumnExpression expression, IMigrationContext context) + public CreateColumnBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, CreateColumnExpression expression) : base(expression) { _context = context; + _databaseProviders = databaseProviders; } public ForeignKeyDefinition CurrentForeignKey { get; set; } @@ -56,15 +59,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column { Expression.Column.IsIndexed = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -110,16 +110,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column { Expression.Column.IsUnique = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName, - IsUnique = true - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName, + IsUnique = true + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -147,17 +144,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column { Expression.Column.IsForeignKey = true; - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = primaryTableName, - PrimaryTableSchema = primaryTableSchema, - ForeignTable = Expression.TableName, - ForeignTableSchema = Expression.SchemaName - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = primaryTableName, + PrimaryTableSchema = primaryTableSchema, + ForeignTable = Expression.TableName, + ForeignTableSchema = Expression.SchemaName + }); fk.ForeignKey.PrimaryColumns.Add(primaryColumnName); fk.ForeignKey.ForeignColumns.Add(Expression.Column.Name); @@ -187,17 +181,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column public ICreateColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema, string foreignTableName, string foreignColumnName) { - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = Expression.TableName, - PrimaryTableSchema = Expression.SchemaName, - ForeignTable = foreignTableName, - ForeignTableSchema = foreignTableSchema - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = Expression.TableName, + PrimaryTableSchema = Expression.SchemaName, + ForeignTable = foreignTableName, + ForeignTableSchema = foreignTableSchema + }); fk.ForeignKey.PrimaryColumns.Add(Expression.Column.Name); fk.ForeignKey.ForeignColumns.Add(foreignColumnName); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs index 4f2666d032..e3ad6f972d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs @@ -19,75 +19,66 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create public CreateBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { + if (context == null) throw new ArgumentNullException("context"); + if (sqlSyntax == null) throw new ArgumentNullException("sqlSyntax"); + _context = context; _sqlSyntax = sqlSyntax; _databaseProviders = databaseProviders; } - [Obsolete("Use alternate ctor specifying ISqlSyntaxProvider instead")] - public CreateBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) - :this(context, SqlSyntaxContext.SqlSyntaxProvider, databaseProviders) - { - } - public ICreateTableWithColumnSyntax Table(string tableName) { - var expression = new CreateTableExpression { TableName = tableName }; + var expression = new CreateTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; _context.Expressions.Add(expression); - return new CreateTableBuilder(expression, _context); + return new CreateTableBuilder(_context, _databaseProviders, expression); } public ICreateColumnOnTableSyntax Column(string columnName) { - var expression = _databaseProviders == null - ? new CreateColumnExpression { Column = { Name = columnName } } - : new CreateColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders) { Column = { Name = columnName } }; + var expression = new CreateColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Column = { Name = columnName } }; _context.Expressions.Add(expression); - return new CreateColumnBuilder(expression, _context); + return new CreateColumnBuilder(_context, _databaseProviders, expression); } public ICreateForeignKeyFromTableSyntax ForeignKey() { - var expression = _databaseProviders == null - ? new CreateForeignKeyExpression() - : new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders); + var expression = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); _context.Expressions.Add(expression); return new CreateForeignKeyBuilder(expression); } public ICreateForeignKeyFromTableSyntax ForeignKey(string foreignKeyName) { - var expression = _databaseProviders == null - ? new CreateForeignKeyExpression { ForeignKey = { Name = foreignKeyName } } - : new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders) { ForeignKey = { Name = foreignKeyName } }; + var expression = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { ForeignKey = { Name = foreignKeyName } }; _context.Expressions.Add(expression); return new CreateForeignKeyBuilder(expression); } public ICreateIndexForTableSyntax Index() { - var expression = new CreateIndexExpression(_sqlSyntax); + var expression = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); _context.Expressions.Add(expression); return new CreateIndexBuilder(expression); } public ICreateIndexForTableSyntax Index(string indexName) { - var expression = new CreateIndexExpression(_sqlSyntax) { Index = { Name = indexName } }; + var expression = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Index = { Name = indexName } }; _context.Expressions.Add(expression); return new CreateIndexBuilder(expression); } public ICreateConstraintOnTableSyntax PrimaryKey() { - var expression = new CreateConstraintExpression(ConstraintType.PrimaryKey); + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey); _context.Expressions.Add(expression); return new CreateConstraintBuilder(expression); } public ICreateConstraintOnTableSyntax PrimaryKey(string primaryKeyName) { - var expression = new CreateConstraintExpression(ConstraintType.PrimaryKey); + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey); expression.Constraint.ConstraintName = primaryKeyName; _context.Expressions.Add(expression); return new CreateConstraintBuilder(expression); @@ -95,14 +86,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create public ICreateConstraintOnTableSyntax UniqueConstraint() { - var expression = new CreateConstraintExpression(ConstraintType.Unique); + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique); _context.Expressions.Add(expression); return new CreateConstraintBuilder(expression); } public ICreateConstraintOnTableSyntax UniqueConstraint(string constraintName) { - var expression = new CreateConstraintExpression(ConstraintType.Unique); + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique); expression.Constraint.ConstraintName = constraintName; _context.Expressions.Add(expression); return new CreateConstraintBuilder(expression); @@ -110,7 +101,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create public ICreateConstraintOnTableSyntax Constraint(string constraintName) { - var expression = new CreateConstraintExpression(ConstraintType.NonUnique); + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.NonUnique); expression.Constraint.ConstraintName = constraintName; _context.Expressions.Add(expression); return new CreateConstraintBuilder(expression); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs index b575a89c49..a06775929b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs @@ -6,10 +6,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions { public class CreateConstraintExpression : MigrationExpressionBase { - public CreateConstraintExpression(ConstraintType type) + public CreateConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ConstraintType constraint) + : base(current, databaseProviders, sqlSyntax) { - Constraint = new ConstraintDefinition(type); + Constraint = new ConstraintDefinition(constraint); } + public ConstraintDefinition Constraint { get; private set; } @@ -17,7 +19,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions { var constraintType = (Constraint.IsPrimaryKeyConstraint) ? "PRIMARY KEY" : "UNIQUE"; - if (Constraint.IsPrimaryKeyConstraint && SqlSyntaxContext.SqlSyntaxProvider.SupportsClustered()) + if (Constraint.IsPrimaryKeyConstraint && SqlSyntax.SupportsClustered()) constraintType += " CLUSTERED"; if (Constraint.IsNonUniqueConstraint) @@ -27,12 +29,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions for (int i = 0; i < Constraint.Columns.Count; i++) { - columns[i] = SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(Constraint.Columns.ElementAt(i)); + columns[i] = SqlSyntax.GetQuotedColumnName(Constraint.Columns.ElementAt(i)); } - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.CreateConstraint, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedName(Constraint.ConstraintName), + return string.Format(SqlSyntax.CreateConstraint, + SqlSyntax.GetQuotedTableName(Constraint.TableName), + SqlSyntax.GetQuotedName(Constraint.ConstraintName), constraintType, string.Join(", ", columns)); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs index c048bda198..8cf37e74d2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs @@ -7,24 +7,24 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions { public class CreateTableExpression : MigrationExpressionBase { - [Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] - public CreateTableExpression() - { - Columns = new List(); - } + //[Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] + //public CreateTableExpression() + //{ + // Columns = new List(); + //} - [Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] - public CreateTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) - { - Columns = new List(); - } + //[Obsolete("Use the other constructors specifying an ISqlSyntaxProvider instead")] + //public CreateTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) + // : base(current, databaseProviders) + //{ + // Columns = new List(); + //} - public CreateTableExpression(ISqlSyntaxProvider sqlSyntax) - : base(sqlSyntax) - { - Columns = new List(); - } + //public CreateTableExpression(ISqlSyntaxProvider sqlSyntax) + // : base(sqlSyntax) + //{ + // Columns = new List(); + //} public CreateTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) : base(current, databaseProviders, sqlSyntax) diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs index 58eef672eb..86760b71bc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs @@ -2,6 +2,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table { @@ -11,11 +12,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table ICreateTableColumnOptionForeignKeyCascadeSyntax { private readonly IMigrationContext _context; + private readonly DatabaseProviders[] _databaseProviders; - public CreateTableBuilder(CreateTableExpression expression, IMigrationContext context) + public CreateTableBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, CreateTableExpression expression) : base(expression) { _context = context; + _databaseProviders = databaseProviders; } public ColumnDefinition CurrentColumn { get; set; } @@ -62,15 +65,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table { CurrentColumn.IsIndexed = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -86,15 +86,25 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table { CurrentColumn.IsPrimaryKey = true; - var expression = new CreateConstraintExpression(ConstraintType.PrimaryKey) + //For MySQL, the PK will be created WITH the create table expression, however for + // SQL Server, the PK get's created in a different Alter table expression afterwords. + // MySQL will choke if the same constraint is again added afterword + // TODO: This is a super hack, I'd rather not add another property like 'CreatesPkInCreateTableDefinition' to check + // for this, but I don't see another way around. MySQL doesn't support checking for a constraint before creating + // it... except in a very strange way but it doesn't actually provider error feedback if it doesn't work so we cannot use + // it. For now, this is what I'm doing + if (Expression.CurrentDatabaseProvider != DatabaseProviders.MySql) { - Constraint = + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, ConstraintType.PrimaryKey) + { + Constraint = { TableName = CurrentColumn.TableName, Columns = new[] { CurrentColumn.Name } } - }; - _context.Expressions.Add(expression); + }; + _context.Expressions.Add(expression); + } return this; } @@ -104,16 +114,27 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table CurrentColumn.IsPrimaryKey = true; CurrentColumn.PrimaryKeyName = primaryKeyName; - var expression = new CreateConstraintExpression(ConstraintType.PrimaryKey) + //For MySQL, the PK will be created WITH the create table expression, however for + // SQL Server, the PK get's created in a different Alter table expression afterwords. + // MySQL will choke if the same constraint is again added afterword + // TODO: This is a super hack, I'd rather not add another property like 'CreatesPkInCreateTableDefinition' to check + // for this, but I don't see another way around. MySQL doesn't support checking for a constraint before creating + // it... except in a very strange way but it doesn't actually provider error feedback if it doesn't work so we cannot use + // it. For now, this is what I'm doing + + if (Expression.CurrentDatabaseProvider != DatabaseProviders.MySql) { - Constraint = + var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, ConstraintType.PrimaryKey) + { + Constraint = { ConstraintName = primaryKeyName, TableName = CurrentColumn.TableName, Columns = new[] { CurrentColumn.Name } } - }; - _context.Expressions.Add(expression); + }; + _context.Expressions.Add(expression); + } return this; } @@ -139,16 +160,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table { CurrentColumn.IsUnique = true; - var index = new CreateIndexExpression - { - Index = new IndexDefinition - { - Name = indexName, - SchemaName = Expression.SchemaName, - TableName = Expression.TableName, - IsUnique = true - } - }; + var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition + { + Name = indexName, + SchemaName = Expression.SchemaName, + TableName = Expression.TableName, + IsUnique = true + }); index.Index.Columns.Add(new IndexColumnDefinition { @@ -176,17 +194,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table { CurrentColumn.IsForeignKey = true; - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = primaryTableName, - PrimaryTableSchema = primaryTableSchema, - ForeignTable = Expression.TableName, - ForeignTableSchema = Expression.SchemaName - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = primaryTableName, + PrimaryTableSchema = primaryTableSchema, + ForeignTable = Expression.TableName, + ForeignTableSchema = Expression.SchemaName + }); fk.ForeignKey.PrimaryColumns.Add(primaryColumnName); fk.ForeignKey.ForeignColumns.Add(CurrentColumn.Name); @@ -216,17 +231,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table public ICreateTableColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema, string foreignTableName, string foreignColumnName) { - var fk = new CreateForeignKeyExpression - { - ForeignKey = new ForeignKeyDefinition - { - Name = foreignKeyName, - PrimaryTable = Expression.TableName, - PrimaryTableSchema = Expression.SchemaName, - ForeignTable = foreignTableName, - ForeignTableSchema = foreignTableSchema - } - }; + var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition + { + Name = foreignKeyName, + PrimaryTable = Expression.TableName, + PrimaryTableSchema = Expression.SchemaName, + ForeignTable = foreignTableName, + ForeignTableSchema = foreignTableSchema + }); fk.ForeignKey.PrimaryColumns.Add(CurrentColumn.Name); fk.ForeignKey.ForeignColumns.Add(foreignColumnName); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs index f70de5b548..18ea700ed2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs @@ -23,69 +23,57 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete _databaseProviders = databaseProviders; } - [Obsolete("Use the other constructor specifying an ISqlSyntaxProvider instead")] - public DeleteBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) - : this(context, SqlSyntaxContext.SqlSyntaxProvider, databaseProviders) - { - } - public void Table(string tableName) { - var expression = new DeleteTableExpression { TableName = tableName }; + var expression = new DeleteTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; _context.Expressions.Add(expression); } public IDeleteColumnFromTableSyntax Column(string columnName) { - var expression = _databaseProviders == null - ? new DeleteColumnExpression { ColumnNames = { columnName } } - : new DeleteColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders) { ColumnNames = { columnName } }; + var expression = new DeleteColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {ColumnNames = {columnName}}; _context.Expressions.Add(expression); return new DeleteColumnBuilder(expression); } public IDeleteForeignKeyFromTableSyntax ForeignKey() { - var expression = _databaseProviders == null - ? new DeleteForeignKeyExpression(_sqlSyntax) - : new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); + var expression = new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); _context.Expressions.Add(expression); return new DeleteForeignKeyBuilder(expression); } public IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName) { - var expression = _databaseProviders == null - ? new DeleteForeignKeyExpression(_sqlSyntax) { ForeignKey = { Name = foreignKeyName } } - : new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { ForeignKey = { Name = foreignKeyName } }; + var expression = new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {ForeignKey = {Name = foreignKeyName}}; _context.Expressions.Add(expression); return new DeleteForeignKeyBuilder(expression); } public IDeleteDataSyntax FromTable(string tableName) { - var expression = new DeleteDataExpression { TableName = tableName }; + var expression = new DeleteDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; _context.Expressions.Add(expression); return new DeleteDataBuilder(expression); } public IDeleteIndexForTableSyntax Index() { - var expression = new DeleteIndexExpression(); + var expression = new DeleteIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); _context.Expressions.Add(expression); return new DeleteIndexBuilder(expression); } public IDeleteIndexForTableSyntax Index(string indexName) { - var expression = new DeleteIndexExpression { Index = { Name = indexName } }; + var expression = new DeleteIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Index = { Name = indexName } }; _context.Expressions.Add(expression); return new DeleteIndexBuilder(expression); } public IDeleteConstraintOnTableSyntax PrimaryKey(string primaryKeyName) { - var expression = new DeleteConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, ConstraintType.PrimaryKey) + var expression = new DeleteConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey) { Constraint = { ConstraintName = primaryKeyName } }; @@ -95,7 +83,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete public IDeleteConstraintOnTableSyntax UniqueConstraint(string constraintName) { - var expression = new DeleteConstraintExpression(ConstraintType.Unique) + var expression = new DeleteConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique) { Constraint = { ConstraintName = constraintName } }; @@ -105,9 +93,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete public IDeleteDefaultConstraintOnTableSyntax DefaultConstraint() { - var expression = _databaseProviders == null - ? new DeleteDefaultConstraintExpression() - : new DeleteDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders); + var expression = new DeleteDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax); _context.Expressions.Add(expression); return new DeleteDefaultConstraintBuilder(expression); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs index c9f8c8d04d..b02bca6f12 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs @@ -7,13 +7,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteColumnExpression : MigrationExpressionBase { - public DeleteColumnExpression() - { - ColumnNames = new List(); - } + //public DeleteColumnExpression() + //{ + // ColumnNames = new List(); + //} - public DeleteColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) + public DeleteColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { ColumnNames = new List(); } @@ -31,9 +31,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions foreach (string columnName in ColumnNames) { if (ColumnNames.First() != columnName) sb.AppendLine(";"); - sb.AppendFormat(SqlSyntaxContext.SqlSyntaxProvider.DropColumn, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(columnName)); + sb.AppendFormat(SqlSyntax.DropColumn, + SqlSyntax.GetQuotedTableName(TableName), + SqlSyntax.GetQuotedColumnName(columnName)); } return sb.ToString(); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs index 39f250532d..21ef6bb517 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs @@ -5,13 +5,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteConstraintExpression : MigrationExpressionBase { - public DeleteConstraintExpression(ConstraintType type) - { - Constraint = new ConstraintDefinition(type); - } - - public DeleteConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ConstraintType type) - : base(current, databaseProviders) + + public DeleteConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ConstraintType type) + : base(current, databaseProviders, sqlSyntax) { Constraint = new ConstraintDefinition(type); } @@ -25,24 +21,24 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { if (Constraint.IsPrimaryKeyConstraint) { - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), + return string.Format(SqlSyntax.DeleteConstraint, + SqlSyntax.GetQuotedTableName(Constraint.TableName), "PRIMARY KEY", ""); } else { - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), + return string.Format(SqlSyntax.DeleteConstraint, + SqlSyntax.GetQuotedTableName(Constraint.TableName), "FOREIGN KEY", ""); } } else { - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteConstraint, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Constraint.TableName), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedName(Constraint.ConstraintName)); + return string.Format(SqlSyntax.DeleteConstraint, + SqlSyntax.GetQuotedTableName(Constraint.TableName), + SqlSyntax.GetQuotedName(Constraint.ConstraintName)); } } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs index 73966f6e93..97c601a2a1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs @@ -8,12 +8,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public class DeleteDataExpression : MigrationExpressionBase { private readonly List _rows = new List(); - - public DeleteDataExpression() - { - } - - public DeleteDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + + public DeleteDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -32,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions if (IsAllRows) { - deleteItems.Add(string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteData, SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), "1 = 1")); + deleteItems.Add(string.Format(SqlSyntax.DeleteData, SqlSyntax.GetQuotedTableName(TableName), "1 = 1")); } else { @@ -42,13 +39,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions foreach (KeyValuePair item in row) { whereClauses.Add(string.Format("{0} {1} {2}", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + SqlSyntax.GetQuotedColumnName(item.Key), item.Value == null ? "IS" : "=", GetQuotedValue(item.Value))); } - deleteItems.Add(string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteData, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), + deleteItems.Add(string.Format(SqlSyntax.DeleteData, + SqlSyntax.GetQuotedTableName(TableName), String.Join(" AND ", whereClauses.ToArray()))); } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs index 43677d23e3..70a74da28e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs @@ -4,12 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteDefaultConstraintExpression : MigrationExpressionBase { - public DeleteDefaultConstraintExpression() - { - } - - public DeleteDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) + public DeleteDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -22,7 +18,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions if (IsExpressionSupported() == false) return string.Empty; - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DeleteDefaultConstraint, + return string.Format(SqlSyntax.DeleteDefaultConstraint, TableName, ColumnName); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs index 34e3bd65c5..e0fef06f3f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs @@ -8,24 +8,11 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteForeignKeyExpression : MigrationExpressionBase { - [Obsolete("Use the other constructors specifying an ILogger instead")] - public DeleteForeignKeyExpression() - { - ForeignKey = new ForeignKeyDefinition(); - } - - [Obsolete("Use the other constructors specifying an ILogger instead")] - public DeleteForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) - { - ForeignKey = new ForeignKeyDefinition(); - } - - public DeleteForeignKeyExpression(ISqlSyntaxProvider sqlSyntax) - : base(sqlSyntax) - { - ForeignKey = new ForeignKeyDefinition(); - } + //public DeleteForeignKeyExpression(ISqlSyntaxProvider sqlSyntax) + // : base(sqlSyntax) + //{ + // ForeignKey = new ForeignKeyDefinition(); + //} public DeleteForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) : base(current, databaseProviders, sqlSyntax) @@ -33,7 +20,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions ForeignKey = new ForeignKeyDefinition(); } - public virtual ForeignKeyDefinition ForeignKey { get; set; } + public ForeignKeyDefinition ForeignKey { get; set; } public override string ToString() { @@ -45,7 +32,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions if (CurrentDatabaseProvider == DatabaseProviders.MySql) { - //MySql naming "convention" for foreignkeys, which aren't explicitly named + //MySql naming "convention" for foreignkeys, which aren't explicitly named if (string.IsNullOrEmpty(ForeignKey.Name)) ForeignKey.Name = string.Format("{0}_ibfk_1", ForeignKey.ForeignTable.ToLower()); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs index 3e1a959925..e504223669 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs @@ -5,23 +5,26 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteIndexExpression : MigrationExpressionBase { - public DeleteIndexExpression() + + public DeleteIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { Index = new IndexDefinition(); } - public DeleteIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + public DeleteIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, IndexDefinition index) + : base(current, databaseProviders, sqlSyntax) { - Index = new IndexDefinition(); + Index = index; } - public virtual IndexDefinition Index { get; set; } + public IndexDefinition Index { get; private set; } public override string ToString() { - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DropIndex, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedName(Index.Name), - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Index.TableName)); + return string.Format(SqlSyntax.DropIndex, + SqlSyntax.GetQuotedName(Index.Name), + SqlSyntax.GetQuotedTableName(Index.TableName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs index 2fff4e75e0..0456091a4b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs @@ -4,11 +4,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions { public class DeleteTableExpression : MigrationExpressionBase { - public DeleteTableExpression() - { - } - - public DeleteTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + + public DeleteTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -17,8 +15,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions public override string ToString() { - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.DropTable, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName)); + return string.Format(SqlSyntax.DropTable, + SqlSyntax.GetQuotedTableName(TableName)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs index 6521d043a7..4a31c3bf37 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs @@ -12,7 +12,6 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete void Table(string tableName); IDeleteColumnFromTableSyntax Column(string columnName); IDeleteForeignKeyFromTableSyntax ForeignKey(); - [Obsolete("Do not use this construct as it does not work with MySql, use the syntax: Delete.ForeignKey().FromTable(\"umbracoUser2app\").ForeignColumn(... instead")] IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName); IDeleteDataSyntax FromTable(string tableName); IDeleteIndexForTableSyntax Index(); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs index e537253664..549a4a0c66 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs @@ -1,34 +1,31 @@ using System; using Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute { public class ExecuteBuilder : IExecuteBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; private readonly DatabaseProviders[] _databaseProviders; - public ExecuteBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) + public ExecuteBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; _databaseProviders = databaseProviders; } public void Sql(string sqlStatement) { - var expression = _databaseProviders == null - ? new ExecuteSqlStatementExpression {SqlStatement = sqlStatement} - : new ExecuteSqlStatementExpression(_context.CurrentDatabaseProvider, - _databaseProviders) {SqlStatement = sqlStatement}; + var expression = new ExecuteSqlStatementExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {SqlStatement = sqlStatement}; _context.Expressions.Add(expression); } public void Code(Func codeStatement) { - var expression = _databaseProviders == null - ? new ExecuteCodeStatementExpression { CodeStatement = codeStatement } - : new ExecuteCodeStatementExpression(_context.CurrentDatabaseProvider, - _databaseProviders) { CodeStatement = codeStatement }; + var expression = new ExecuteCodeStatementExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { CodeStatement = codeStatement }; _context.Expressions.Add(expression); } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs index 0f88fe2ee6..e5f487d15d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs @@ -1,15 +1,12 @@ using System; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions { public class ExecuteCodeStatementExpression : MigrationExpressionBase { - public ExecuteCodeStatementExpression() - { - } - - public ExecuteCodeStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) + public ExecuteCodeStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs index 308a7719dd..1164ba7caa 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs @@ -1,12 +1,12 @@ -namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions { public class ExecuteSqlStatementExpression : MigrationExpressionBase { - public ExecuteSqlStatementExpression() - { - } - public ExecuteSqlStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + public ExecuteSqlStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs index 5c7bf3c0b7..ad16da5286 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs @@ -5,20 +5,16 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions { public class CreateColumnExpression : MigrationExpressionBase { - public CreateColumnExpression() + + public CreateColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { Column = new ColumnDefinition { ModificationType = ModificationType.Create }; } - public CreateColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) - { - Column = new ColumnDefinition { ModificationType = ModificationType.Create }; - } - - public virtual string SchemaName { get; set; } - public virtual string TableName { get; set; } - public virtual ColumnDefinition Column { get; set; } + public string SchemaName { get; set; } + public string TableName { get; set; } + public ColumnDefinition Column { get; set; } public override string ToString() { @@ -28,9 +24,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions if (string.IsNullOrEmpty(Column.TableName)) Column.TableName = TableName; - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.AddColumn, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(Column.TableName), - SqlSyntaxContext.SqlSyntaxProvider.Format(Column)); + return string.Format(SqlSyntax.AddColumn, + SqlSyntax.GetQuotedTableName(Column.TableName), + SqlSyntax.Format(Column)); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs index c6c0c437ef..45e40e961f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs @@ -5,25 +5,26 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions { public class CreateForeignKeyExpression : MigrationExpressionBase { - public CreateForeignKeyExpression() + public CreateForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ForeignKeyDefinition fkDef) + : base(current, databaseProviders, sqlSyntax) + { + ForeignKey = fkDef; + } + + public CreateForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { ForeignKey = new ForeignKeyDefinition(); } - public CreateForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) - { - ForeignKey = new ForeignKeyDefinition(); - } - - public virtual ForeignKeyDefinition ForeignKey { get; set; } + public ForeignKeyDefinition ForeignKey { get; set; } public override string ToString() { if (IsExpressionSupported() == false) return string.Empty; - return SqlSyntaxContext.SqlSyntaxProvider.Format(ForeignKey); + return SqlSyntax.Format(ForeignKey); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs index eadb30cd9e..7fa38cd3ae 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs @@ -6,32 +6,20 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions { public class CreateIndexExpression : MigrationExpressionBase { - public CreateIndexExpression(ISqlSyntaxProvider sqlSyntax) - : base(sqlSyntax) + + public CreateIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, IndexDefinition index) + : base(current, databaseProviders, sqlSyntax) { - Index = new IndexDefinition(); + Index = index; } - + public CreateIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) : base(current, databaseProviders, sqlSyntax) { Index = new IndexDefinition(); } - - [Obsolete("Use alternate ctor specifying ISqlSyntaxProvider instead")] - public CreateIndexExpression() - : this(SqlSyntaxContext.SqlSyntaxProvider) - { - - } - - [Obsolete("Use alternate ctor specifying ISqlSyntaxProvider instead")] - public CreateIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : this(current, databaseProviders, SqlSyntaxContext.SqlSyntaxProvider) - { - } - - public virtual IndexDefinition Index { get; set; } + + public IndexDefinition Index { get; set; } public override string ToString() { diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs index b7105170f1..e0d20c4875 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs @@ -3,43 +3,46 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Delete; using Umbraco.Core.Persistence.Migrations.Syntax.Execute; using Umbraco.Core.Persistence.Migrations.Syntax.Rename; using Umbraco.Core.Persistence.Migrations.Syntax.Update; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase { public class IfDatabaseBuilder : IIfDatabaseBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; private readonly DatabaseProviders[] _databaseProviders; - public IfDatabaseBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) + public IfDatabaseBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; _databaseProviders = databaseProviders; } public ICreateBuilder Create { - get { return new CreateBuilder(_context, _databaseProviders); } + get { return new CreateBuilder(_context, _sqlSyntax, _databaseProviders); } } public IExecuteBuilder Execute { - get { return new ExecuteBuilder(_context, _databaseProviders); } + get { return new ExecuteBuilder(_context, _sqlSyntax, _databaseProviders); } } public IDeleteBuilder Delete { - get { return new DeleteBuilder(_context, _databaseProviders); } + get { return new DeleteBuilder(_context, _sqlSyntax, _databaseProviders); } } public IRenameBuilder Rename { - get { return new RenameBuilder(_context, _databaseProviders); } + get { return new RenameBuilder(_context, _sqlSyntax, _databaseProviders); } } public IUpdateBuilder Update { - get { return new UpdateBuilder(_context, _databaseProviders); } + get { return new UpdateBuilder(_context, _sqlSyntax, _databaseProviders); } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs index 568087733b..28b8c71d39 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.SqlSyntax; @@ -8,56 +9,76 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert.Expressions public class InsertDataExpression : MigrationExpressionBase { private readonly List _rows = new List(); - - public InsertDataExpression() - { - } - - public InsertDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + + public InsertDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } public string SchemaName { get; set; } public string TableName { get; set; } + public bool EnabledIdentityInsert { get; set; } - public List Rows { get { return _rows; } } - - public override string ToString() { - //TODO: This works for single insertion entries, not sure if it is valid for bulk insert operations!!! - if (IsExpressionSupported() == false) return string.Empty; - - var insertItems = new List(); - foreach (var item in Rows) + var sb = new StringBuilder(); + + if (EnabledIdentityInsert && SqlSyntax.SupportsIdentityInsert()) { - var cols = ""; - var vals = ""; - foreach (var keyVal in item) + sb.AppendLine(string.Format("SET IDENTITY_INSERT {0} ON;", SqlSyntax.GetQuotedTableName(TableName))); + if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE) { - cols += keyVal.Key + ","; - vals += GetQuotedValue(keyVal.Value) + ","; + sb.AppendLine("GO"); } - cols = cols.TrimEnd(','); - vals = vals.TrimEnd(','); - - - var sql = string.Format(SqlSyntaxContext.SqlSyntaxProvider.InsertData, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), - cols, vals); - - insertItems.Add(sql); } - return string.Join(",", insertItems); + try + { + foreach (var item in Rows) + { + var cols = ""; + var vals = ""; + foreach (var keyVal in item) + { + cols += SqlSyntax.GetQuotedColumnName(keyVal.Key) + ","; + vals += GetQuotedValue(keyVal.Value) + ","; + } + cols = cols.TrimEnd(','); + vals = vals.TrimEnd(','); + + + var sql = string.Format(SqlSyntax.InsertData, + SqlSyntax.GetQuotedTableName(TableName), + cols, vals); + + sb.AppendLine(string.Format("{0};", sql)); + if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE) + { + sb.AppendLine("GO"); + } + } + } + finally + { + if (EnabledIdentityInsert && SqlSyntax.SupportsIdentityInsert()) + { + sb.AppendLine(string.Format("SET IDENTITY_INSERT {0} OFF;", SqlSyntax.GetQuotedTableName(TableName))); + if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE) + { + sb.AppendLine("GO"); + } + } + } + + return sb.ToString(); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs index fb0585f0e6..5791f0cb83 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs @@ -2,6 +2,7 @@ { public interface IInsertDataSyntax : IFluentSyntax { + IInsertDataSyntax EnableIdentityInsert(); IInsertDataSyntax Row(object dataAsAnonymousType); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs index 889c3e4008..2d3ce9c64d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs @@ -1,19 +1,24 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Insert.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert { public class InsertBuilder : IInsertBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; + private readonly DatabaseProviders[] _databaseProviders; - public InsertBuilder(IMigrationContext context) + public InsertBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; + _databaseProviders = databaseProviders; } public IInsertDataSyntax IntoTable(string tableName) { - var expression = new InsertDataExpression { TableName = tableName }; + var expression = new InsertDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; _context.Expressions.Add(expression); return new InsertDataBuilder(expression); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs index 9b419f8daf..4f8bf2dfcc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs @@ -14,6 +14,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert _expression = expression; } + public IInsertDataSyntax EnableIdentityInsert() + { + _expression.EnabledIdentityInsert = true; + return this; + } + public IInsertDataSyntax Row(object dataAsAnonymousType) { _expression.Rows.Add(GetData(dataAsAnonymousType)); diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs index 4f626e2117..1f84401923 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs @@ -3,13 +3,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions { public class RenameColumnExpression : MigrationExpressionBase - { - public RenameColumnExpression() - { - } - - public RenameColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) + { + public RenameColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -52,7 +48,7 @@ SELECT CONCAT( if (IsExpressionSupported() == false) return string.Empty; - return SqlSyntaxContext.SqlSyntaxProvider.FormatColumnRename(TableName, OldName, NewName); + return SqlSyntax.FormatColumnRename(TableName, OldName, NewName); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs index 956cc32bfc..ec60117d7e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs @@ -4,12 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions { public class RenameTableExpression : MigrationExpressionBase { - public RenameTableExpression() - { - } - - public RenameTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) - : base(current, databaseProviders) + public RenameTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -22,7 +18,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions if (IsExpressionSupported() == false) return string.Empty; - return SqlSyntaxContext.SqlSyntaxProvider.FormatTableRename(OldName, NewName); + return SqlSyntax.FormatTableRename(OldName, NewName); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs index 7116935d77..ebfb08b862 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs @@ -1,34 +1,33 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Column; using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Table; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename { public class RenameBuilder : IRenameBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; private readonly DatabaseProviders[] _databaseProviders; - public RenameBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) + public RenameBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; _databaseProviders = databaseProviders; } public IRenameTableSyntax Table(string oldName) { - var expression = _databaseProviders == null - ? new RenameTableExpression {OldName = oldName} - : new RenameTableExpression(_context.CurrentDatabaseProvider, _databaseProviders) { OldName = oldName }; + var expression = new RenameTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { OldName = oldName }; _context.Expressions.Add(expression); return new RenameTableBuilder(expression); } public IRenameColumnTableSyntax Column(string oldName) { - var expression = _databaseProviders == null - ? new RenameColumnExpression {OldName = oldName} - : new RenameColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders) { OldName = oldName }; + var expression = new RenameColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { OldName = oldName }; _context.Expressions.Add(expression); return new RenameColumnBuilder(expression); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs index fd470dd7f2..18bec8b938 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs @@ -5,11 +5,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions { public class UpdateDataExpression : MigrationExpressionBase { - public UpdateDataExpression() - { - } - - public UpdateDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders) : base(current, databaseProviders) + public UpdateDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax) + : base(current, databaseProviders, sqlSyntax) { } @@ -31,7 +28,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions foreach (var item in Set) { updateItems.Add(string.Format("{0} = {1}", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + SqlSyntax.GetQuotedColumnName(item.Key), GetQuotedValue(item.Value))); } @@ -44,13 +41,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions foreach (var item in Where) { whereClauses.Add(string.Format("{0} {1} {2}", - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(item.Key), + SqlSyntax.GetQuotedColumnName(item.Key), item.Value == null ? "IS" : "=", GetQuotedValue(item.Value))); } } - return string.Format(SqlSyntaxContext.SqlSyntaxProvider.UpdateData, - SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName), + return string.Format(SqlSyntax.UpdateData, + SqlSyntax.GetQuotedTableName(TableName), string.Join(", ", updateItems.ToArray()), string.Join(" AND ", whereClauses.ToArray())); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs index e2e118c05f..3816e65132 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs @@ -1,23 +1,24 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Syntax.Update { public class UpdateBuilder : IUpdateBuilder { private readonly IMigrationContext _context; + private readonly ISqlSyntaxProvider _sqlSyntax; private readonly DatabaseProviders[] _databaseProviders; - public UpdateBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders) + public UpdateBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders) { _context = context; + _sqlSyntax = sqlSyntax; _databaseProviders = databaseProviders; } public IUpdateSetSyntax Table(string tableName) { - var expression = _databaseProviders == null - ? new UpdateDataExpression { TableName = tableName } - : new UpdateDataExpression(_context.CurrentDatabaseProvider, _databaseProviders) { TableName = tableName }; + var expression = new UpdateDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName }; _context.Expressions.Add(expression); return new UpdateDataBuilder(expression, _context); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs index 4ca0a6eeb8..d18cb430c0 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs @@ -3,6 +3,7 @@ using System.CodeDom; 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.SqlSyntax; @@ -14,12 +15,12 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven [Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)] public class AddIndexToCmsMacroTable : MigrationBase { - private readonly bool _skipIndexCheck; + private readonly bool _forTesting; - internal AddIndexToCmsMacroTable(bool skipIndexCheck, ISqlSyntaxProvider sqlSyntax, ILogger logger) + internal AddIndexToCmsMacroTable(bool forTesting, ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) { - _skipIndexCheck = skipIndexCheck; + _forTesting = forTesting; } public AddIndexToCmsMacroTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) @@ -28,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven public override void Up() { - var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database) + var dbIndexes = _forTesting ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database) .Select(x => new DbIndexDefinition() { TableName = x.Item1, @@ -40,6 +41,30 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven //make sure it doesn't already exist if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacro_Alias")) == false) { + //in order to create this index, we need to ensure that there are no duplicates. This could have happened with very old/corrupt umbraco versions. + // So we'll remove any duplicates based on alias and only keep the one with the smallest id since I'm pretty sure we'd always choose the 'first' one + // when running a query. + + if (_forTesting == false) + { + //NOTE: Using full SQL statement here in case the DTO has changed between versions + var macros = Context.Database.Fetch("SELECT * FROM cmsMacro") + .GroupBy(x => x.Alias) + .Where(x => x.Count() > 1); + + foreach (var m in macros) + { + //get the min id (to keep) + var minId = m.Min(x => x.Id); + //delete all the others + foreach (var macroDto in m.Where(x => x.Id != minId)) + { + Delete.FromTable("cmsMacro").Row(new { id = macroDto.Id }); + } + } + } + + Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique(); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs index 6060b76fa4..d069d8222d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs @@ -27,6 +27,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven }).ToArray(); //add a foreign key to the parent id column too! + + //In some cases in very old corrupted db's this will fail, so it means we need to clean the data first + //set the parentID to NULL where it doesn't actually exist in the normal ids + Execute.Sql(@"UPDATE cmsTags SET parentId = NULL WHERE parentId IS NOT NULL AND parentId NOT IN (SELECT id FROM cmsTags)"); + Create.ForeignKey("FK_cmsTags_cmsTags") .FromTable("cmsTags") .ForeignColumn("ParentId") diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs index a2e2056d3a..830adfd7fb 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs @@ -55,9 +55,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven } var propertyTypeIds = propertyData.Select(x => x.PropertyTypeId).Distinct(); - var propertyTypes = database.Fetch( - "WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds }); + //NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns + // won't exist yet + var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds }); + foreach (var data in propertyData) { if (string.IsNullOrEmpty(data.Text) == false) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs index 81b93a8883..eea56031d4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs @@ -3,6 +3,8 @@ using System.Data; 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.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero @@ -30,6 +32,53 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe Delete.FromTable("cmsLanguageText").Row(new { pk = pk }); } + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray(); + + if (columns.Any(x => x.ColumnName.InvariantEquals("id") + && x.TableName.InvariantEquals("umbracoLanguage") + && x.DataType.InvariantEquals("smallint"))) + { + //Ensure that the umbracoLanguage PK is INT and not SmallInt (which it might be in older db versions) + // In order to 'change' this to an INT, we have to run a full migration script which is super annoying + Create.Table("umbracoLanguage_TEMP") + .WithColumn("id").AsInt32().NotNullable().Identity() + .WithColumn("languageISOCode").AsString(10).Nullable() + .WithColumn("languageCultureName").AsString(50).Nullable(); + + var currentData = this.Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax)); + foreach (var languageDto in currentData) + { + Insert.IntoTable("umbracoLanguage_TEMP") + .EnableIdentityInsert() + .Row(new {id = languageDto.Id, languageISOCode = languageDto.IsoCode, languageCultureName = languageDto.CultureName}); + } + + //ok, all data has been copied over, drop the old table, rename the temp table and re-add constraints. + Delete.Table("umbracoLanguage"); + Rename.Table("umbracoLanguage_TEMP").To("umbracoLanguage"); + + //add the pk + Create.PrimaryKey("PK_language").OnTable("umbracoLanguage").Column("id"); + } + + var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + + //make sure it doesn't already exist + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDictionary_id")) == false) + { + Create.Index("IX_cmsDictionary_id").OnTable("cmsDictionary") + .OnColumn("id").Ascending() + .WithOptions().NonClustered() + .WithOptions().Unique(); + } + //now we need to create a foreign key Create.ForeignKey("FK_cmsLanguageText_umbracoLanguage_id").FromTable("cmsLanguageText").ForeignColumn("languageId") .ToTable("umbracoLanguage").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None); @@ -52,7 +101,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe public override void Down() { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs new file mode 100644 index 0000000000..118dc1fc06 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs @@ -0,0 +1,58 @@ +using System; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero +{ + [Migration("7.3.0", 17, GlobalSettings.UmbracoMigrationName)] + public class AddServerRegistrationColumnsAndLock : MigrationBase + { + public AddServerRegistrationColumnsAndLock(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { } + + public override void Up() + { + // don't execute if the column is already there + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + if (columns.Any(x => x.TableName.InvariantEquals("umbracoServer") && x.ColumnName.InvariantEquals("isMaster")) == false) + { + Create.Column("isMaster").OnTable("umbracoServer").AsBoolean().NotNullable().WithDefaultValue(0); + } + + EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers"); + } + + public override void Down() + { + // not implemented + } + + private void EnsureLockObject(int id, string uniqueId, string text) + { + var exists = Context.Database.Exists(id); + if (exists) return; + + Insert + .IntoTable("umbracoNode") + .EnableIdentityInsert() + .Row(new + { + id = id, // NodeId + trashed = false, + parentId = -1, + nodeUser = 0, + level = 1, + path = "-1," + id, + sortOrder = 0, + uniqueId = new Guid(uniqueId), + text = text, + nodeObjectType = new Guid(Constants.ObjectTypes.LockObject), + createDate = DateTime.Now + }); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs new file mode 100644 index 0000000000..d62ad7645d --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs @@ -0,0 +1,49 @@ +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.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero +{ + /// + /// Older corrupted dbs might have multiple published flags for a content item, this shouldn't be possible + /// so we need to clear the content flag on the older version + /// + [Migration("7.3.0", 18, GlobalSettings.UmbracoMigrationName)] + public class CleanUpCorruptedPublishedFlags : MigrationBase + { + public CleanUpCorruptedPublishedFlags(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { + } + + public override void Up() + { + //Get all cmsDocument items that have more than one published date set + var sql = @"SELECT * FROM cmsDocument WHERE nodeId IN +(SELECT nodeId +FROM cmsDocument +GROUP BY nodeId +HAVING SUM(CASE WHEN published = 0 THEN 0 ELSE 1 END) > 1) +AND published = 1 +ORDER BY nodeId, updateDate"; + + var docs = Context.Database.Fetch(sql).GroupBy(x => x.NodeId); + + foreach (var doc in docs) + { + var latest = doc.OrderByDescending(x => x.UpdateDate).First(); + foreach (var old in doc.Where(x => x.VersionId != latest.VersionId)) + { + Update.Table("cmsDocument").Set(new {published = 0}).Where(new {nodeId = old.NodeId}); + } + } + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs index 00f968941d..afa5a1e675 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs @@ -28,14 +28,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe var textType = SqlSyntax.GetSpecialDbType(SpecialDbTypes.NTEXT); Create.Table("umbracoCacheInstruction") - .WithColumn("id").AsInt32().Identity().NotNullable() + .WithColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoCacheInstruction").Identity() .WithColumn("utcStamp").AsDateTime().NotNullable() .WithColumn("jsonInstruction").AsCustom(textType).NotNullable() .WithColumn("originated").AsString(500).NotNullable(); - - Create.PrimaryKey("PK_umbracoCacheInstruction") - .OnTable("umbracoCacheInstruction") - .Column("id"); + } public override void Down() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs index 3cea873f73..cb0ad58a49 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs @@ -23,34 +23,92 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe { //Don't execute anything if there is no 'master' column - this might occur if the db is already upgraded - var cols = SqlSyntax.GetColumnsInSchema(Context.Database); + var cols = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); if (cols.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")) == false) { return; } + + var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); //update the parentId column for all templates to be correct so it matches the current 'master' template - //NOTE: we are using dynamic because we need to get the data in a column that no longer exists in the schema - var templates = Context.Database.Fetch(new Sql().Select("*").From()); - foreach (var template in templates) - { - Update.Table("umbracoNode").Set(new {parentID = template.master ?? -1}).Where(new {id = template.nodeId}); - //now build the correct path for the template - Update.Table("umbracoNode").Set(new { path = BuildPath (template, templates)}).Where(new { id = template.nodeId }); - - } + //In some old corrupted databases, the information will not be correct in the master column so we need to fix that + //first by nulling out the master column where the id doesn't actually exist + Execute.Sql(@"UPDATE cmsTemplate SET master = NULL WHERE " + + SqlSyntax.GetQuotedColumnName("master") + @" IS NOT NULL AND " + + SqlSyntax.GetQuotedColumnName("master") + @" NOT IN (" + + //Stupid MySQL... needs this stupid syntax because it can do an update with a sub query of itself, + // yet it can do one with a sub sub query + // ... this will work in all dbs too + @"SELECT nodeId FROM (SELECT * FROM cmsTemplate a) b)"); + + //Now we can bulk update the parentId column + + //NOTE: This single statement should be used but stupid SQLCE doesn't support Update with a FROM !! + // so now we have to do this by individual rows :( + //Execute.Sql(@"UPDATE umbracoNode + //SET parentID = COALESCE(t2." + SqlSyntax.GetQuotedColumnName("master") + @", -1) + //FROM umbracoNode t1 + //INNER JOIN cmsTemplate t2 + //ON t1.id = t2.nodeId"); + Execute.Code(database => + { + var templateData = database.Fetch("SELECT * FROM cmsTemplate"); + + foreach (var template in templateData) + { + var sql = "SET parentID=@parentId WHERE id=@nodeId"; + + LogHelper.Info("Executing sql statement: UPDATE umbracoNode " + sql); + + database.Update(sql, + new {parentId = template.master ?? -1, nodeId = template.nodeId}); + } + + return string.Empty; + }); + + //Now we can update the path, but this needs to be done in a delegate callback so that the query runs after the updates just completed + Execute.Code(database => + { + //NOTE: we are using dynamic because we need to get the data in a column that no longer exists in the schema + var templates = database.Fetch(new Sql().Select("*").From()); + foreach (var template in templates) + { + var sql = string.Format(SqlSyntax.UpdateData, + SqlSyntax.GetQuotedTableName("umbracoNode"), + "path=@buildPath", + "id=@nodeId"); + + LogHelper.Info("Executing sql statement: " + sql); + + //now build the correct path for the template + database.Execute(sql, new + { + buildPath = BuildPath(template, templates), + nodeId = template.nodeId + }); + } + + return string.Empty; + }); + + //now remove the master column and key if (this.Context.CurrentDatabaseProvider == DatabaseProviders.MySql) { - Delete.ForeignKey().FromTable("cmsTemplate").ForeignColumn("master").ToTable("umbracoUser").PrimaryColumn("id"); + //Because MySQL doesn't name keys with what you want, we need to query for the one that is associated + // this is required for this specific case because there are 2 foreign keys on the cmsTemplate table + var fkName = constraints.FirstOrDefault(x => x.Item1.InvariantEquals("cmsTemplate") && x.Item2.InvariantEquals("master")); + if (fkName != null) + { + Delete.ForeignKey(fkName.Item3).OnTable("cmsTemplate"); + } } else { - //These are the old aliases, before removing them, check they exist - var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); - if (constraints.Any(x => x.Item1.InvariantEquals("cmsTemplate") && x.Item3.InvariantEquals("FK_cmsTemplate_cmsTemplate"))) { Delete.ForeignKey("FK_cmsTemplate_cmsTemplate").OnTable("cmsTemplate"); @@ -59,8 +117,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe //TODO: Hopefully it's not named something else silly in some crazy old versions } - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray(); - if (columns.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate"))) + + if (cols.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate"))) { Delete.Column("master").FromTable("cmsTemplate"); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs index 5f27bfa4f5..6598719454 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs @@ -28,8 +28,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne { if (database != null) { - //Fetch all PropertyTypes that belongs to a PropertyTypeGroup - var propertyTypes = database.Fetch("WHERE propertyTypeGroupId > 0"); + //Fetch all PropertyTypes that belongs to a PropertyTypeGroup + //NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns + // won't exist yet + var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0"); + var propertyGroups = database.Fetch("WHERE id > 0"); foreach (var propertyType in propertyTypes) diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index 42ab8ab610..d55ad8fc14 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -215,6 +215,10 @@ namespace Umbraco.Core.Persistence _paramPrefix = "?"; if (_dbType == DBType.Oracle) _paramPrefix = ":"; + + // by default use MSSQL default ReadCommitted level + //TODO change to RepeatableRead - but that is breaking + _isolationLevel = IsolationLevel.ReadCommitted; } // Automatically close one open shared connection @@ -237,7 +241,27 @@ namespace Umbraco.Core.Persistence _sharedConnection.ConnectionString = _connectionString; _sharedConnection.OpenWithRetry();//Changed .Open() => .OpenWithRetry() extension method - _sharedConnection = OnConnectionOpened(_sharedConnection); + // ensure we have the proper isolation level, as levels can leak in pools + // read http://stackoverflow.com/questions/9851415/sql-server-isolation-level-leaks-across-pooled-connections + // and http://stackoverflow.com/questions/641120/what-exec-sp-reset-connection-shown-in-sql-profiler-means + // + // NPoco has that code in OpenSharedConnectionImp (commented out?) + //using (var cmd = _sharedConnection.CreateCommand()) + //{ + // cmd.CommandText = GetSqlForTransactionLevel(_isolationLevel); + // cmd.CommandTimeout = CommandTimeout; + // cmd.ExecuteNonQuery(); + //} + // + // although MSDN documentation for SQL CE clearly states that the above method + // should work, it fails & reports an error parsing the query on 'TRANSACTION', + // and Google is no help (others have faced the same issue... no solution). So, + // switching to another method that does work on all databases. + var tr = _sharedConnection.BeginTransaction(_isolationLevel); + tr.Commit(); + tr.Dispose(); + + _sharedConnection = OnConnectionOpened(_sharedConnection); if (KeepConnectionAlive) _sharedConnectionDepth++; // Make sure you call Dispose @@ -269,10 +293,20 @@ namespace Umbraco.Core.Persistence // Helper to create a transaction scope public Transaction GetTransaction() { - return new Transaction(this); - } + return GetTransaction(_isolationLevel); + } - // Use by derived repo generated by T4 templates + public Transaction GetTransaction(IsolationLevel isolationLevel) + { + return new Transaction(this, isolationLevel); + } + + public IsolationLevel CurrentTransactionIsolationLevel + { + get { return _transaction == null ? IsolationLevel.Unspecified : _transaction.IsolationLevel; } + } + + // Use by derived repo generated by T4 templates public virtual void OnBeginTransaction() { } public virtual void OnEndTransaction() { } @@ -281,17 +315,23 @@ namespace Umbraco.Core.Persistence // Use `using (var scope=db.Transaction) { scope.Complete(); }` to ensure correct semantics public void BeginTransaction() { - _transactionDepth++; + BeginTransaction(_isolationLevel); + } + + public void BeginTransaction(IsolationLevel isolationLevel) + { + _transactionDepth++; if (_transactionDepth == 1) { OpenSharedConnection(); - _transaction = _sharedConnection.BeginTransaction(); + _transaction = _sharedConnection.BeginTransaction(isolationLevel); _transactionCancelled = false; OnBeginTransaction(); } - - } + else if (isolationLevel > _transaction.IsolationLevel) + throw new Exception("Already in a transaction with a lower isolation level."); + } // Internal helper to cleanup transaction stuff void CleanupTransaction() @@ -313,6 +353,7 @@ namespace Umbraco.Core.Persistence public void AbortTransaction() { _transactionCancelled = true; + //TODO what shall we do if transactionDepth is already zero? if ((--_transactionDepth) == 0) CleanupTransaction(); } @@ -320,11 +361,32 @@ namespace Umbraco.Core.Persistence // Complete the transaction public void CompleteTransaction() { - if ((--_transactionDepth) == 0) + //TODO what shall we do if transactionDepth is already zero? + if ((--_transactionDepth) == 0) CleanupTransaction(); } - // Helper to handle named parameters from object properties + // in NPoco this is in DatabaseType + private static string GetSqlForTransactionLevel(IsolationLevel isolationLevel) + { + switch (isolationLevel) + { + case IsolationLevel.ReadCommitted: + return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; + case IsolationLevel.ReadUncommitted: + return "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; + case IsolationLevel.RepeatableRead: + return "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"; + case IsolationLevel.Serializable: + return "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"; + case IsolationLevel.Snapshot: + return "SET TRANSACTION ISOLATION LEVEL SNAPSHOT"; + default: + return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; + } + } + + // Helper to handle named parameters from object properties static Regex rxParams = new Regex(@"(? args_dest) { @@ -2264,16 +2326,17 @@ namespace Umbraco.Core.Persistence string _lastSql; object[] _lastArgs; string _paramPrefix = "@"; + IsolationLevel _isolationLevel; } // Transaction object helps maintain transaction depth counts public class Transaction : IDisposable { - public Transaction(Database db) - { - _db = db; - _db.BeginTransaction(); - } + public Transaction(Database db, IsolationLevel isolationLevel) + { + _db = db; + _db.BeginTransaction(isolationLevel); + } public virtual void Complete() { @@ -2283,6 +2346,7 @@ namespace Umbraco.Core.Persistence public void Dispose() { + //TODO prevent multiple calls to Dispose if (_db != null) _db.AbortTransaction(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 61e94568e3..60d7d8e7ef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -654,8 +654,8 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate() .Where(x => x.Published) - .OrderBy(x => x.Level) - .OrderBy(x => x.SortOrder); + .OrderBy(x => x.Level, SqlSyntax) + .OrderBy(x => x.SortOrder, SqlSyntax); //NOTE: This doesn't allow properties to be part of the query var dtos = Database.Fetch(sql); @@ -896,7 +896,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion. /// - /// + /// /// /// /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 9ac905cf03..c75c061452 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1130,6 +1130,11 @@ AND umbracoNode.id <> @id", out IDictionary allPropertyTypeCollection, out IDictionary allPropertyGroupCollection) { + allPropertyGroupCollection = new Dictionary(); + allPropertyTypeCollection = new Dictionary(); + + // query below is not safe + pointless if array is empty + if (contentTypeIds.Length == 0) return; // first part Gets all property groups including property type data even when no property type exists on the group // second part Gets all property types including ones that are not on a group @@ -1165,10 +1170,8 @@ AND umbracoNode.id <> @id", INNER JOIN cmsDataType as DT ON PT.dataTypeId = DT.nodeId LEFT JOIN cmsPropertyTypeGroup as PG - ON PG.id = PT.propertyTypeGroupId"); - - if (contentTypeIds.Any()) - sqlBuilder.AppendLine(" WHERE (PT.contentTypeId in (@contentTypeIds))"); + ON PG.id = PT.propertyTypeGroupId + WHERE (PT.contentTypeId in (@contentTypeIds))"); sqlBuilder.AppendLine(" ORDER BY (pgId)"); @@ -1179,9 +1182,6 @@ AND umbracoNode.id <> @id", var result = db.Fetch(sqlBuilder.ToString(), new { contentTypeIds = contentTypeIds }); - allPropertyGroupCollection = new Dictionary(); - allPropertyTypeCollection = new Dictionary(); - foreach (var contentTypeId in contentTypeIds) { //from this we need to make : diff --git a/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs index a87ec1e764..0b5b42660d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DeepCloneRuntimeCacheProvider.cs @@ -79,7 +79,7 @@ namespace Umbraco.Core.Persistence.Repositories public object GetCacheItem(string cacheKey, Func getCacheItem) { - return InnerProvider.GetCacheItem(cacheKey, () => + var cached = InnerProvider.GetCacheItem(cacheKey, () => { var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem); var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache @@ -87,18 +87,21 @@ namespace Umbraco.Core.Persistence.Repositories return CheckCloneableAndTracksChanges(value); }); + return CheckCloneableAndTracksChanges(cached); } public object GetCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) { - return InnerProvider.GetCacheItem(cacheKey, () => + var cached = InnerProvider.GetCacheItem(cacheKey, () => { var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem); var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache if (value == null) return null; // do not store null values (backward compat) return CheckCloneableAndTracksChanges(value); - }, timeout, isSliding, priority, removedCallback, dependentFiles); + }, timeout, isSliding, priority, removedCallback, dependentFiles); + + return CheckCloneableAndTracksChanges(cached); } public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index 391a777e5e..e1e4c3105a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -147,10 +147,6 @@ namespace Umbraco.Core.Persistence.Repositories protected virtual void PersistUpdatedItem(TEntity entity) { - //TODO: A big problem here is if the entities 'Path' changes, if that is the case then - // we'd need to rename the underlying file, BUT how would we do this since we aren't storing an - // original path property. - using (var stream = GetContentStream(entity.Content)) { FileSystem.AddFile(entity.Path, stream, true); @@ -219,6 +215,15 @@ namespace Umbraco.Core.Persistence.Repositories return list; } + protected string GetFileContent(string filename) + { + using (var stream = FileSystem.OpenFile(filename)) + using (var reader = new StreamReader(stream, Encoding.UTF8, true)) + { + return reader.ReadToEnd(); + } + } + /// /// Dispose any disposable properties /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs index 8ba28eeaee..3fbcdd3283 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs @@ -6,5 +6,6 @@ namespace Umbraco.Core.Persistence.Repositories { void AddFolder(string folderPath); void DeleteFolder(string folderPath); + bool ValidatePartialView(IPartialView partialView); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PartialViewMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PartialViewMacroRepository.cs index f4d6906cd1..e055c3dd93 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PartialViewMacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PartialViewMacroRepository.cs @@ -7,7 +7,6 @@ namespace Umbraco.Core.Persistence.Repositories { internal class PartialViewMacroRepository : PartialViewRepository { - public PartialViewMacroRepository(IUnitOfWork work) : this(work, new PhysicalFileSystem(SystemDirectories.MvcViews + "/MacroPartials/")) { @@ -18,5 +17,6 @@ namespace Umbraco.Core.Persistence.Repositories { } + protected override PartialViewType ViewType { get { return PartialViewType.PartialViewMacro; } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs index 2fd0240701..e9352a945f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PartialViewRepository.cs @@ -20,43 +20,52 @@ namespace Umbraco.Core.Persistence.Repositories { } + protected virtual PartialViewType ViewType { get { return PartialViewType.PartialView; } } + public override IPartialView Get(string id) { - if (FileSystem.FileExists(id) == false) - { - return null; - } - - string content; - using (var stream = FileSystem.OpenFile(id)) - { - var bytes = new byte[stream.Length]; - stream.Position = 0; - stream.Read(bytes, 0, (int)stream.Length); - content = Encoding.UTF8.GetString(bytes); - } - + // get the relative path within the filesystem + // (though... id should be relative already) var path = FileSystem.GetRelativePath(id); + + if (FileSystem.FileExists(path) == false) + return null; + + // content will be lazy-loaded when required var created = FileSystem.GetCreated(path).UtcDateTime; var updated = FileSystem.GetLastModified(path).UtcDateTime; + //var content = GetFileContent(path); - - var script = new PartialView(path) + var view = new PartialView(path, file => GetFileContent(file.OriginalPath)) { //id can be the hash Id = path.GetHashCode(), - Content = content, Key = path.EncodeAsGuid(), + //Content = content, CreateDate = created, UpdateDate = updated, - VirtualPath = FileSystem.GetUrl(id) + VirtualPath = FileSystem.GetUrl(id), + ViewType = ViewType }; //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - script.ResetDirtyProperties(false); + view.ResetDirtyProperties(false); - return script; + return view; + } + + public override void AddOrUpdate(IPartialView entity) + { + var partialView = entity as PartialView; + if (partialView != null) + partialView.ViewType = ViewType; + + base.AddOrUpdate(entity); + + // ensure that from now on, content is lazy-loaded + if (partialView != null && partialView.GetFileContent == null) + partialView.GetFileContent = file => GetFileContent(file.OriginalPath); } public override IEnumerable GetAll(params string[] ids) @@ -81,6 +90,29 @@ namespace Umbraco.Core.Persistence.Repositories } } + private static readonly List ValidExtensions = new List { "cshtml" }; + + public virtual bool ValidatePartialView(IPartialView partialView) + { + // get full path + string fullPath; + try + { + // may throw for security reasons + fullPath = FileSystem.GetFullPath(partialView.Path); + } + catch + { + return false; + } + + // validate path & extension + var validDir = SystemDirectories.MvcViews; + var isValidPath = IOHelper.VerifyEditPath(fullPath, validDir); + var isValidExtension = IOHelper.VerifyFileExtension(fullPath, ValidExtensions); + return isValidPath && isValidExtension; + } + /// /// Gets a stream that is used to write to the file /// diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs index 977c2a54cd..be1f03a73f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -44,6 +44,11 @@ namespace Umbraco.Core.Persistence.Repositories { FormatDeleteStatement("umbracoUser2NodeNotify", "nodeId"), FormatDeleteStatement("umbracoUser2NodePermission", "nodeId"), + @"DELETE FROM umbracoAccessRule WHERE umbracoAccessRule.accessId IN ( + SELECT TB1.id FROM umbracoAccess as TB1 + INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id + WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)", + FormatDeleteStatement("umbracoAccess", "nodeId"), FormatDeleteStatement("umbracoRelation", "parentId"), FormatDeleteStatement("umbracoRelation", "childId"), FormatDeleteStatement("cmsTagRelationship", "nodeId"), @@ -75,9 +80,9 @@ namespace Umbraco.Core.Persistence.Repositories } catch (Exception ex) { - trans.Dispose(); + // transaction will rollback Logger.Error>("An error occurred while emptying the Recycle Bin: " + ex.Message, ex); - return false; + throw; } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs index b48e096e3f..13b2877c50 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ScriptRepository.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -28,33 +26,27 @@ namespace Umbraco.Core.Persistence.Repositories public override Script Get(string id) { - if(FileSystem.FileExists(id) == false) - { - return null; - } - - string content; - using (var stream = FileSystem.OpenFile(id)) - { - var bytes = new byte[stream.Length]; - stream.Position = 0; - stream.Read(bytes, 0, (int)stream.Length); - content = Encoding.UTF8.GetString(bytes); - } - + // get the relative path within the filesystem + // (though... id should be relative already) var path = FileSystem.GetRelativePath(id); + + if (FileSystem.FileExists(path) == false) + return null; + + // content will be lazy-loaded when required var created = FileSystem.GetCreated(path).UtcDateTime; var updated = FileSystem.GetLastModified(path).UtcDateTime; + //var content = GetFileContent(path); - var script = new Script(path) + var script = new Script(path, file => GetFileContent(file.OriginalPath)) { //id can be the hash Id = path.GetHashCode(), - Content = content, Key = path.EncodeAsGuid(), + //Content = content, CreateDate = created, UpdateDate = updated, - VirtualPath = FileSystem.GetUrl(id) + VirtualPath = FileSystem.GetUrl(path) }; //on initial construction we don't want to have dirty properties tracked @@ -64,6 +56,15 @@ namespace Umbraco.Core.Persistence.Repositories return script; } + public override void AddOrUpdate(Script entity) + { + base.AddOrUpdate(entity); + + // ensure that from now on, content is lazy-loaded + if (entity.GetFileContent == null) + entity.GetFileContent = file => GetFileContent(file.OriginalPath); + } + public override IEnumerable - + diff --git a/src/Umbraco.Web.UI.Client/src/loader.js b/src/Umbraco.Web.UI.Client/src/loader.dev.js similarity index 100% rename from src/Umbraco.Web.UI.Client/src/loader.js rename to src/Umbraco.Web.UI.Client/src/loader.dev.js diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.controller.js index 7fabd5c7df..d898ec48f8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.controller.js @@ -1,14 +1,21 @@ angular.module("umbraco") - .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService) { + .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) { $scope.section = $routeParams.section; $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; if(!$scope.section){ - $scope.section ="content"; + $scope.section = "content"; } + $scope.sectionName = $scope.section; + var rq = {}; rq.section = $scope.section; + + //translate section name + localizationService.localize("sections_" + rq.section).then(function (value) { + $scope.sectionName = value; + }); userService.getCurrentUser().then(function(user){ diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html index 80ce8f80c0..f066da8e7c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/help.html @@ -1,13 +1,13 @@
-

Help

+

Help

Umbraco version {{version}}
-
Help topics for: {{section}}
+
Help topics for: {{sectionName}}
-
Video chapters for: {{section}}
+
Video chapters for: {{sectionName}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index 92d0ab6c30..94826b07b4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -36,7 +36,7 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/legacy.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/legacy.controller.js index 8e58535c74..fcc8e8c2ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/legacy.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/legacy.controller.js @@ -9,7 +9,7 @@ */ function LegacyController($scope, $routeParams, $element) { - var url = decodeURIComponent($routeParams.url.toLowerCase().replace(/javascript\:/g, "")); + var url = decodeURIComponent($routeParams.url.replace(/javascript\:/gi, "")); //split into path and query var urlParts = url.split("?"); var extIndex = urlParts[0].lastIndexOf("."); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index e02ea36b5a..d3bc28bc55 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -39,7 +39,7 @@
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/delete.html b/src/Umbraco.Web.UI.Client/src/views/content/delete.html index d3e44fce25..f3bdb0d121 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/delete.html @@ -2,7 +2,7 @@

- Are you sure you want to delete {{currentNode.name}} ? + Are you sure you want to delete {{currentNode.name}} ?

diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/ourumbraco.png b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/ourumbraco.png index 3795b27228..68f0cffe30 100644 Binary files a/src/Umbraco.Web.UI.Client/src/views/dashboard/default/ourumbraco.png and b/src/Umbraco.Web.UI.Client/src/views/dashboard/default/ourumbraco.png differ diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js index e439d0e37f..4a19ea9926 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/examinemgmt.controller.js @@ -43,7 +43,7 @@ function examineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeo umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", { searcherName: searcher.name, - query: searcher.searchText, + query: encodeURIComponent(searcher.searchText), queryType: searcher.searchType })), 'Failed to search') diff --git a/src/Umbraco.Web.UI.Client/src/views/datatype/delete.html b/src/Umbraco.Web.UI.Client/src/views/datatype/delete.html index cddc1b34ad..b7bcae8e3d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatype/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/datatype/delete.html @@ -2,7 +2,7 @@

- Are you sure you want to delete {{currentNode.name}} ? + Are you sure you want to delete {{currentNode.name}} ?

diff --git a/src/Umbraco.Web.UI.Client/src/views/media/delete.html b/src/Umbraco.Web.UI.Client/src/views/media/delete.html index dcbedf8ed7..3c9716eb3f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/delete.html @@ -2,7 +2,7 @@

- Are you sure you want to delete {{currentNode.name}} ? + Are you sure you want to delete {{currentNode.name}} ?

diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js index 4c1cd7678a..fcbb9dd0c7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js @@ -128,6 +128,13 @@ function mediaEditController($scope, $routeParams, appState, mediaResource, enti rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, err.data) }); + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + editorState.set($scope.content); $scope.busy = false; $scope.page.saveButtonState = "error"; diff --git a/src/Umbraco.Web.UI.Client/src/views/member/create.html b/src/Umbraco.Web.UI.Client/src/views/member/create.html index 9dcd7ff836..03f0237e38 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/create.html @@ -1,7 +1,7 @@