Files
Umbraco-CMS/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs

584 lines
24 KiB
C#
Raw Normal View History

using System;
2018-06-29 19:52:40 +02:00
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
2018-07-17 17:20:40 +02:00
using System.Text.RegularExpressions;
2018-06-29 19:52:40 +02:00
using NPoco;
Merge remote-tracking branch 'origin/v8/dev' into netcore/dev # Conflicts: # build/NuSpecs/UmbracoCms.Core.nuspec # build/NuSpecs/UmbracoCms.Web.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Cache/CacheKeys.cs # src/Umbraco.Core/Composing/TypeFinder.cs # src/Umbraco.Core/Configuration/GlobalSettings.cs # src/Umbraco.Core/Configuration/GlobalSettingsExtensions.cs # src/Umbraco.Core/Configuration/IGlobalSettings.cs # src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs # src/Umbraco.Core/Configuration/UmbracoSettings/ContentSectionExtensions.cs # src/Umbraco.Core/Constants-AppSettings.cs # src/Umbraco.Core/Editors/UserEditorAuthorizationHelper.cs # src/Umbraco.Core/Extensions/StringExtensions.cs # src/Umbraco.Core/Extensions/UriExtensions.cs # src/Umbraco.Core/IO/IOHelper.cs # src/Umbraco.Core/IO/PhysicalFileSystem.cs # src/Umbraco.Core/Media/Exif/MathEx.cs # src/Umbraco.Core/Media/UploadAutoFillProperties.cs # src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs # src/Umbraco.Core/Models/Membership/User.cs # src/Umbraco.Core/Models/UserExtensions.cs # src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs # src/Umbraco.Core/PropertyEditors/ListViewConfiguration.cs # src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs # src/Umbraco.Core/PropertyEditors/ValueConverters/MultiNodeTreePickerValueConverter.cs # src/Umbraco.Core/Routing/AliasUrlProvider.cs # src/Umbraco.Core/Routing/DefaultUrlProvider.cs # src/Umbraco.Core/Routing/UriUtility.cs # src/Umbraco.Core/Routing/UrlProviderExtensions.cs # src/Umbraco.Core/Runtime/CoreRuntime.cs # src/Umbraco.Core/RuntimeOptions.cs # src/Umbraco.Core/RuntimeState.cs # src/Umbraco.Core/Security/BackOfficeUserStore.cs # src/Umbraco.Core/Security/ContentPermissions.cs # src/Umbraco.Core/Sync/ApplicationUrlHelper.cs # src/Umbraco.Core/Trees/TreeNode.cs # src/Umbraco.Core/Udi.cs # src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs # src/Umbraco.Examine/Umbraco.Examine.csproj # src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs # src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs # src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs # src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs # src/Umbraco.Infrastructure/Scoping/Scope.cs # src/Umbraco.Infrastructure/Search/ExamineComponent.cs # src/Umbraco.Infrastructure/Security/IdentityMapDefinition.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.Infrastructure/Services/Implement/MediaService.cs # src/Umbraco.Infrastructure/Services/Implement/NotificationService.cs # src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs # src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/LocksTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/Models/UserExtensionsTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Examine/UmbracoContentValueSetValidatorTests.cs # src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs # src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config # src/Umbraco.Tests/TestHelpers/SettingsForTests.cs # src/Umbraco.Tests/Testing/TestDatabase.cs # src/Umbraco.Tests/Web/Controllers/ContentControllerUnitTests.cs # src/Umbraco.Tests/Web/Controllers/FilterAllowedOutgoingContentAttributeTests.cs # src/Umbraco.Tests/Web/Controllers/MediaControllerUnitTests.cs # src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs # src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs # src/Umbraco.Web.BackOffice/Controllers/ContentController.cs # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs # src/Umbraco.Web.BackOffice/Controllers/MediaController.cs # src/Umbraco.Web.BackOffice/Controllers/PackageInstallController.cs # src/Umbraco.Web.BackOffice/Controllers/TourController.cs # src/Umbraco.Web.BackOffice/Controllers/UserGroupEditorAuthorizationHelper.cs # src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingContentAttribute.cs # src/Umbraco.Web.BackOffice/Filters/FilterAllowedOutgoingMediaAttribute.cs # src/Umbraco.Web.BackOffice/Mapping/ContentMapDefinition.cs # src/Umbraco.Web.BackOffice/Services/IconService.cs # src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs # src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs # src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs # src/Umbraco.Web.Common/Extensions/FormCollectionExtensions.cs # src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js # src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en.xml # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml # src/Umbraco.Web.UI/config/umbracoSettings.Release.config # src/Umbraco.Web/Cache/MemberCacheRefresher.cs # src/Umbraco.Web/Composing/ModuleInjector.cs # src/Umbraco.Web/Editors/BackOfficeController.cs # src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs # src/Umbraco.Web/Editors/ContentTypeController.cs # src/Umbraco.Web/Editors/Filters/ContentSaveValidationAttribute.cs # src/Umbraco.Web/Editors/Filters/MediaItemSaveValidationAttribute.cs # src/Umbraco.Web/Editors/Filters/UserGroupAuthorizationAttribute.cs # src/Umbraco.Web/Editors/TinyMceController.cs # src/Umbraco.Web/Editors/UserGroupsController.cs # src/Umbraco.Web/Editors/UsersController.cs # src/Umbraco.Web/ImageCropperTemplateExtensions.cs # src/Umbraco.Web/Logging/WebProfiler.cs # src/Umbraco.Web/Logging/WebProfilerProvider.cs # src/Umbraco.Web/Macros/PublishedContentHashtableConverter.cs # src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs # src/Umbraco.Web/Mvc/JsonNetResult.cs # src/Umbraco.Web/Mvc/MemberAuthorizeAttribute.cs # src/Umbraco.Web/Mvc/RenderRouteHandler.cs # src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs # src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs # src/Umbraco.Web/RoutableDocumentFilter.cs # src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs # src/Umbraco.Web/Routing/NotFoundHandlerHelper.cs # src/Umbraco.Web/Routing/PublishedRouter.cs # src/Umbraco.Web/Runtime/WebInitialComposer.cs # src/Umbraco.Web/Scheduling/KeepAlive.cs # src/Umbraco.Web/Security/AppBuilderExtensions.cs # src/Umbraco.Web/Security/BackOfficeClaimsIdentityFactory.cs # src/Umbraco.Web/Security/Providers/UmbracoMembershipProvider.cs # src/Umbraco.Web/Trees/DictionaryTreeController.cs # src/Umbraco.Web/Trees/LanguageTreeController.cs # src/Umbraco.Web/Trees/LogViewerTreeController.cs # src/Umbraco.Web/Trees/PackagesTreeController.cs # src/Umbraco.Web/UmbracoApplication.cs # src/Umbraco.Web/UmbracoApplicationBase.cs # src/Umbraco.Web/UmbracoInjectedModule.cs # src/Umbraco.Web/WebApi/Filters/AdminUsersAuthorizeAttribute.cs # src/Umbraco.Web/WebApi/Filters/CheckIfUserTicketDataIsStaleAttribute.cs # src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs # src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForMediaAttribute.cs # src/Umbraco.Web/WebApi/MemberAuthorizeAttribute.cs
2021-03-05 15:36:27 +01:00
using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
using Umbraco.Cms.Infrastructure.Persistence.Querying;
using Umbraco.Extensions;
2018-06-29 19:52:40 +02:00
namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
2018-06-29 19:52:40 +02:00
{
/// <summary>
/// Represents the Base Sql Syntax provider implementation.
/// </summary>
/// <remarks>
/// All Sql Syntax provider implementations should derive from this abstract class.
/// </remarks>
/// <typeparam name="TSyntax"></typeparam>
public abstract class SqlSyntaxProviderBase<TSyntax> : ISqlSyntaxProvider
where TSyntax : ISqlSyntaxProvider
{
protected SqlSyntaxProviderBase()
{
ClauseOrder = new List<Func<ColumnDefinition, string>>
{
FormatString,
FormatType,
FormatNullable,
FormatConstraint,
FormatDefaultValue,
FormatPrimaryKey,
FormatIdentity
};
//defaults for all providers
StringLengthColumnDefinitionFormat = StringLengthUnicodeColumnDefinitionFormat;
StringColumnDefinition = string.Format(StringLengthColumnDefinitionFormat, DefaultStringLength);
DecimalColumnDefinition = string.Format(DecimalColumnDefinitionFormat, DefaultDecimalPrecision, DefaultDecimalScale);
InitColumnTypeMap();
2018-07-17 17:20:40 +02:00
// ReSharper disable VirtualMemberCallInConstructor
// ok to call virtual GetQuotedXxxName here - they don't depend on any state
var col = Regex.Escape(GetQuotedColumnName("column")).Replace("column", @"\w+");
var fld = Regex.Escape(GetQuotedTableName("table") + ".").Replace("table", @"\w+") + col;
// ReSharper restore VirtualMemberCallInConstructor
AliasRegex = new Regex("(" + fld + @")\s+AS\s+(" + col + ")", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
2018-06-29 19:52:40 +02:00
}
2018-07-17 17:20:40 +02:00
public Regex AliasRegex { get; }
public string GetWildcardPlaceholder() => "%";
2018-06-29 19:52:40 +02:00
public string StringLengthNonUnicodeColumnDefinitionFormat { get; } = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat { get; } = "NVARCHAR({0})";
public string DecimalColumnDefinitionFormat { get; } = "DECIMAL({0},{1})";
2018-06-29 19:52:40 +02:00
public string DefaultValueFormat { get; } = "DEFAULT ({0})";
public int DefaultStringLength { get; } = 255;
public int DefaultDecimalPrecision { get; } = 20;
public int DefaultDecimalScale { get; } = 9;
2018-06-29 19:52:40 +02:00
//Set by Constructor
public string StringColumnDefinition { get; }
public string StringLengthColumnDefinitionFormat { get; }
public string AutoIncrementDefinition { get; protected set; } = "AUTOINCREMENT";
public string IntColumnDefinition { get; } = "INTEGER";
public string LongColumnDefinition { get; } = "BIGINT";
public string GuidColumnDefinition { get; protected set; } = "GUID";
public string BoolColumnDefinition { get; protected set; } = "BOOL";
public string RealColumnDefinition { get; protected set; } = "DOUBLE";
public string DecimalColumnDefinition { get; protected set; }
public string BlobColumnDefinition { get; protected set; } = "BLOB";
public string DateTimeColumnDefinition { get; } = "DATETIME";
public string TimeColumnDefinition { get; protected set; } = "DATETIME";
2018-06-29 19:52:40 +02:00
protected IList<Func<ColumnDefinition, string>> ClauseOrder { get; }
protected DbTypes DbTypeMap { get; } = new DbTypes();
2018-06-29 19:52:40 +02:00
protected void InitColumnTypeMap()
{
DbTypeMap.Set<string>(DbType.String, StringColumnDefinition);
DbTypeMap.Set<char>(DbType.StringFixedLength, StringColumnDefinition);
DbTypeMap.Set<char?>(DbType.StringFixedLength, StringColumnDefinition);
DbTypeMap.Set<char[]>(DbType.String, StringColumnDefinition);
DbTypeMap.Set<bool>(DbType.Boolean, BoolColumnDefinition);
DbTypeMap.Set<bool?>(DbType.Boolean, BoolColumnDefinition);
DbTypeMap.Set<Guid>(DbType.Guid, GuidColumnDefinition);
DbTypeMap.Set<Guid?>(DbType.Guid, GuidColumnDefinition);
DbTypeMap.Set<DateTime>(DbType.DateTime, DateTimeColumnDefinition);
DbTypeMap.Set<DateTime?>(DbType.DateTime, DateTimeColumnDefinition);
DbTypeMap.Set<TimeSpan>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<TimeSpan?>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset>(DbType.DateTimeOffset, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset?>(DbType.DateTimeOffset, TimeColumnDefinition);
2018-06-29 19:52:40 +02:00
DbTypeMap.Set<byte>(DbType.Byte, IntColumnDefinition);
DbTypeMap.Set<byte?>(DbType.Byte, IntColumnDefinition);
DbTypeMap.Set<sbyte>(DbType.SByte, IntColumnDefinition);
DbTypeMap.Set<sbyte?>(DbType.SByte, IntColumnDefinition);
DbTypeMap.Set<short>(DbType.Int16, IntColumnDefinition);
DbTypeMap.Set<short?>(DbType.Int16, IntColumnDefinition);
DbTypeMap.Set<ushort>(DbType.UInt16, IntColumnDefinition);
DbTypeMap.Set<ushort?>(DbType.UInt16, IntColumnDefinition);
DbTypeMap.Set<int>(DbType.Int32, IntColumnDefinition);
DbTypeMap.Set<int?>(DbType.Int32, IntColumnDefinition);
DbTypeMap.Set<uint>(DbType.UInt32, IntColumnDefinition);
DbTypeMap.Set<uint?>(DbType.UInt32, IntColumnDefinition);
DbTypeMap.Set<long>(DbType.Int64, LongColumnDefinition);
DbTypeMap.Set<long?>(DbType.Int64, LongColumnDefinition);
DbTypeMap.Set<ulong>(DbType.UInt64, LongColumnDefinition);
DbTypeMap.Set<ulong?>(DbType.UInt64, LongColumnDefinition);
DbTypeMap.Set<float>(DbType.Single, RealColumnDefinition);
DbTypeMap.Set<float?>(DbType.Single, RealColumnDefinition);
DbTypeMap.Set<double>(DbType.Double, RealColumnDefinition);
DbTypeMap.Set<double?>(DbType.Double, RealColumnDefinition);
DbTypeMap.Set<decimal>(DbType.Decimal, DecimalColumnDefinition);
DbTypeMap.Set<decimal?>(DbType.Decimal, DecimalColumnDefinition);
DbTypeMap.Set<byte[]>(DbType.Binary, BlobColumnDefinition);
}
2020-04-28 07:01:30 +02:00
public abstract string ProviderName { get; }
2018-06-29 19:52:40 +02:00
public virtual string EscapeString(string val)
{
return NPocoDatabaseExtensions.EscapeAtSymbols(val.Replace("'", "''"));
}
public virtual string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return $"upper({column}) = upper(@{paramIndex})";
}
public virtual string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType)
{
//use the 'upper' method to always ensure strings are matched without case sensitivity no matter what the db setting.
return $"upper({column}) LIKE upper(@{paramIndex})";
}
public virtual string GetConcat(params string[] args)
{
return "concat(" + string.Join(",", args) + ")";
}
public virtual string GetQuotedTableName(string tableName)
{
return $"\"{tableName}\"";
}
public virtual string GetQuotedColumnName(string columnName)
{
return $"\"{columnName}\"";
}
public virtual string GetQuotedName(string name)
{
return $"\"{name}\"";
}
public virtual string GetQuotedValue(string value)
{
return $"'{value}'";
}
public virtual string GetIndexType(IndexTypes indexTypes)
{
string indexType;
if (indexTypes == IndexTypes.Clustered)
{
indexType = "CLUSTERED";
}
else
{
indexType = indexTypes == IndexTypes.NonClustered
? "NONCLUSTERED"
: "UNIQUE NONCLUSTERED";
}
return indexType;
}
public virtual string GetSpecialDbType(SpecialDbType dbType)
2018-06-29 19:52:40 +02:00
{
if (dbType == SpecialDbType.NCHAR)
2018-06-29 19:52:40 +02:00
{
return SpecialDbType.NCHAR;
2018-06-29 19:52:40 +02:00
}
else if (dbType == SpecialDbType.NTEXT)
{
return SpecialDbType.NTEXT;
}
else if (dbType == SpecialDbType.NVARCHARMAX)
{
return "NVARCHAR(MAX)";
}
2018-06-29 19:52:40 +02:00
return "NVARCHAR";
}
public abstract IsolationLevel DefaultIsolationLevel { get; }
public abstract string DbProvider { get; }
2018-06-29 19:52:40 +02:00
public virtual IEnumerable<string> GetTablesInSchema(IDatabase db)
{
return new List<string>();
}
public virtual IEnumerable<ColumnInfo> GetColumnsInSchema(IDatabase db)
{
return new List<ColumnInfo>();
}
public virtual IEnumerable<Tuple<string, string>> GetConstraintsPerTable(IDatabase db)
{
return new List<Tuple<string, string>>();
}
public virtual IEnumerable<Tuple<string, string, string>> GetConstraintsPerColumn(IDatabase db)
{
return new List<Tuple<string, string, string>>();
}
public abstract IEnumerable<Tuple<string, string, string, bool>> GetDefinedIndexes(IDatabase db);
2019-05-29 09:19:29 +02:00
public abstract bool TryGetDefaultConstraint(IDatabase db, string tableName, string columnName, out string constraintName);
2019-05-28 17:49:50 +02:00
public abstract void ReadLock(IDatabase db, params int[] lockIds);
public abstract void WriteLock(IDatabase db, params int[] lockIds);
public abstract void ReadLock(IDatabase db, TimeSpan timeout, int lockId);
public abstract void WriteLock(IDatabase db, TimeSpan timeout, int lockId);
2018-06-29 19:52:40 +02:00
public virtual bool DoesTableExist(IDatabase db, string tableName)
{
return false;
}
public virtual bool SupportsClustered()
{
return true;
}
public virtual bool SupportsIdentityInsert()
{
return true;
}
/// <summary>
/// This is used ONLY if we need to format datetime without using SQL parameters (i.e. during migrations)
/// </summary>
/// <param name="date"></param>
/// <param name="includeTime"></param>
/// <returns></returns>
/// <remarks>
/// MSSQL has a DateTime standard that is unambiguous and works on all servers:
/// YYYYMMDD HH:mm:ss
/// </remarks>
public virtual string FormatDateTime(DateTime date, bool includeTime = true)
{
// need CultureInfo.InvariantCulture because ":" here is the "time separator" and
// may be converted to something else in different cultures (eg "." in DK).
return date.ToString(includeTime ? "yyyyMMdd HH:mm:ss" : "yyyyMMdd", CultureInfo.InvariantCulture);
}
public virtual string Format(TableDefinition table)
{
var statement = string.Format(CreateTable, GetQuotedTableName(table.Name), Format(table.Columns));
return statement;
}
public virtual List<string> Format(IEnumerable<IndexDefinition> indexes)
{
return indexes.Select(Format).ToList();
}
public virtual string Format(IndexDefinition index)
{
var name = string.IsNullOrEmpty(index.Name)
? $"IX_{index.TableName}_{index.ColumnName}"
: index.Name;
var columns = index.Columns.Any()
? string.Join(",", index.Columns.Select(x => GetQuotedColumnName(x.Name)))
: GetQuotedColumnName(index.ColumnName);
return string.Format(CreateIndex, GetIndexType(index.IndexType), " ", GetQuotedName(name),
GetQuotedTableName(index.TableName), columns);
}
public virtual List<string> Format(IEnumerable<ForeignKeyDefinition> foreignKeys)
{
return foreignKeys.Select(Format).ToList();
}
public virtual string Format(ForeignKeyDefinition foreignKey)
{
var constraintName = string.IsNullOrEmpty(foreignKey.Name)
? $"FK_{foreignKey.ForeignTable}_{foreignKey.PrimaryTable}_{foreignKey.PrimaryColumns.First()}"
: foreignKey.Name;
return string.Format(CreateForeignKeyConstraint,
GetQuotedTableName(foreignKey.ForeignTable),
GetQuotedName(constraintName),
GetQuotedColumnName(foreignKey.ForeignColumns.First()),
GetQuotedTableName(foreignKey.PrimaryTable),
GetQuotedColumnName(foreignKey.PrimaryColumns.First()),
FormatCascade("DELETE", foreignKey.OnDelete),
FormatCascade("UPDATE", foreignKey.OnUpdate));
}
public virtual string Format(IEnumerable<ColumnDefinition> columns)
{
var sb = new StringBuilder();
foreach (var column in columns)
{
sb.Append(Format(column) + ",\n");
}
return sb.ToString().TrimEnd(",\n");
}
public virtual string Format(ColumnDefinition column)
{
return string.Join(" ", ClauseOrder
.Select(action => action(column))
.Where(clause => string.IsNullOrEmpty(clause) == false));
}
public virtual string Format(ColumnDefinition column, string tableName, out IEnumerable<string> sqls)
{
var sql = new StringBuilder();
sql.Append(FormatString(column));
sql.Append(" ");
sql.Append(FormatType(column));
sql.Append(" ");
sql.Append("NULL"); // always nullable
sql.Append(" ");
sql.Append(FormatConstraint(column));
sql.Append(" ");
sql.Append(FormatDefaultValue(column));
sql.Append(" ");
sql.Append(FormatPrimaryKey(column));
sql.Append(" ");
sql.Append(FormatIdentity(column));
//var isNullable = column.IsNullable;
2018-06-29 19:52:40 +02:00
//var constraint = FormatConstraint(column)?.TrimStart("CONSTRAINT ");
//var hasConstraint = !string.IsNullOrWhiteSpace(constraint);
//var defaultValue = FormatDefaultValue(column);
//var hasDefaultValue = !string.IsNullOrWhiteSpace(defaultValue);
// TODO: This used to exit if nullable but that means this would never work
// to return SQL if the column was nullable?!? I don't get it. This was here
// 4 years ago, I've removed it so that this works for nullable columns.
//if (isNullable /*&& !hasConstraint && !hasDefaultValue*/)
//{
// sqls = Enumerable.Empty<string>();
// return sql.ToString();
//}
2018-06-29 19:52:40 +02:00
var msql = new List<string>();
sqls = msql;
var alterSql = new StringBuilder();
alterSql.Append(FormatString(column));
alterSql.Append(" ");
alterSql.Append(FormatType(column));
alterSql.Append(" ");
alterSql.Append(FormatNullable(column));
//alterSql.Append(" ");
//alterSql.Append(FormatPrimaryKey(column));
//alterSql.Append(" ");
//alterSql.Append(FormatIdentity(column));
msql.Add(string.Format(AlterColumn, tableName, alterSql));
//if (hasConstraint)
//{
// var dropConstraintSql = string.Format(DeleteConstraint, tableName, constraint);
// msql.Add(dropConstraintSql);
// var constraintType = hasDefaultValue ? defaultValue : "";
// var createConstraintSql = string.Format(CreateConstraint, tableName, constraint, constraintType, FormatString(column));
// msql.Add(createConstraintSql);
//}
return sql.ToString();
}
public virtual string FormatPrimaryKey(TableDefinition table)
{
var columnDefinition = table.Columns.FirstOrDefault(x => x.IsPrimaryKey);
if (columnDefinition == null)
return string.Empty;
var constraintName = string.IsNullOrEmpty(columnDefinition.PrimaryKeyName)
? $"PK_{table.Name}"
: columnDefinition.PrimaryKeyName;
var columns = string.IsNullOrEmpty(columnDefinition.PrimaryKeyColumns)
? GetQuotedColumnName(columnDefinition.Name)
: string.Join(", ", columnDefinition.PrimaryKeyColumns
.Split(Constants.CharArrays.CommaSpace, StringSplitOptions.RemoveEmptyEntries)
2018-06-29 19:52:40 +02:00
.Select(GetQuotedColumnName));
var primaryKeyPart = string.Concat("PRIMARY KEY", columnDefinition.IsIndexed ? " CLUSTERED" : " NONCLUSTERED");
return string.Format(CreateConstraint,
GetQuotedTableName(table.Name),
GetQuotedName(constraintName),
primaryKeyPart,
columns);
}
public virtual string FormatColumnRename(string tableName, string oldName, string newName)
{
return string.Format(RenameColumn,
GetQuotedTableName(tableName),
GetQuotedColumnName(oldName),
GetQuotedColumnName(newName));
}
public virtual string FormatTableRename(string oldName, string newName)
{
return string.Format(RenameTable, GetQuotedTableName(oldName), GetQuotedTableName(newName));
}
protected virtual string FormatCascade(string onWhat, Rule rule)
{
var action = "NO ACTION";
switch (rule)
{
case Rule.None:
return "";
case Rule.Cascade:
action = "CASCADE";
break;
case Rule.SetNull:
action = "SET NULL";
break;
case Rule.SetDefault:
action = "SET DEFAULT";
break;
}
return $" ON {onWhat} {action}";
}
protected virtual string FormatString(ColumnDefinition column)
{
return GetQuotedColumnName(column.Name);
}
protected virtual string FormatType(ColumnDefinition column)
{
if (column.Type.HasValue == false && string.IsNullOrEmpty(column.CustomType) == false)
return column.CustomType;
if (column.CustomDbType.HasValue)
2018-06-29 19:52:40 +02:00
{
if (column.Size != default)
2018-06-29 19:52:40 +02:00
{
return $"{GetSpecialDbType(column.CustomDbType.Value)}({column.Size})";
2018-06-29 19:52:40 +02:00
}
return GetSpecialDbType(column.CustomDbType.Value);
2018-06-29 19:52:40 +02:00
}
var type = column.Type.HasValue
? DbTypeMap.ColumnDbTypeMap.First(x => x.Value == column.Type.Value).Key
: column.PropertyType;
if (type == typeof(string))
{
var valueOrDefault = column.Size != default(int) ? column.Size : DefaultStringLength;
return string.Format(StringLengthColumnDefinitionFormat, valueOrDefault);
}
if (type == typeof(decimal))
{
var precision = column.Size != default(int) ? column.Size : DefaultDecimalPrecision;
var scale = column.Precision != default(int) ? column.Precision : DefaultDecimalScale;
return string.Format(DecimalColumnDefinitionFormat, precision, scale);
}
var definition = DbTypeMap.ColumnTypeMap.First(x => x.Key == type).Value;
var dbTypeDefinition = column.Size != default(int)
? $"{definition}({column.Size})"
: definition;
2019-01-22 18:03:39 -05:00
//NOTE Precision is left out
2018-06-29 19:52:40 +02:00
return dbTypeDefinition;
}
protected virtual string FormatNullable(ColumnDefinition column)
{
return column.IsNullable ? "NULL" : "NOT NULL";
}
protected virtual string FormatConstraint(ColumnDefinition column)
{
if (string.IsNullOrEmpty(column.ConstraintName) && column.DefaultValue == null)
return string.Empty;
return
$"CONSTRAINT {(string.IsNullOrEmpty(column.ConstraintName) ? GetQuotedName($"DF_{column.TableName}_{column.Name}") : column.ConstraintName)}";
}
protected virtual string FormatDefaultValue(ColumnDefinition column)
{
if (column.DefaultValue == null)
return string.Empty;
// HACK: probably not needed with latest changes
2018-06-29 19:52:40 +02:00
if (column.DefaultValue.ToString().ToLower().Equals("getdate()".ToLower()))
column.DefaultValue = SystemMethods.CurrentDateTime;
// see if this is for a system method
if (column.DefaultValue is SystemMethods)
{
var method = FormatSystemMethods((SystemMethods)column.DefaultValue);
return string.IsNullOrEmpty(method) ? string.Empty : string.Format(DefaultValueFormat, method);
}
return string.Format(DefaultValueFormat, GetQuotedValue(column.DefaultValue.ToString()));
}
protected virtual string FormatPrimaryKey(ColumnDefinition column)
{
return string.Empty;
}
protected abstract string FormatSystemMethods(SystemMethods systemMethod);
protected abstract string FormatIdentity(ColumnDefinition column);
public abstract Sql<ISqlContext> SelectTop(Sql<ISqlContext> sql, int top);
public virtual string DeleteDefaultConstraint => throw new NotSupportedException("Default constraints are not supported");
public virtual string CreateTable => "CREATE TABLE {0} ({1})";
public virtual string DropTable => "DROP TABLE {0}";
public virtual string AddColumn => "ALTER TABLE {0} ADD {1}";
public virtual string DropColumn => "ALTER TABLE {0} DROP COLUMN {1}";
public virtual string AlterColumn => "ALTER TABLE {0} ALTER COLUMN {1}";
public virtual string RenameColumn => "ALTER TABLE {0} RENAME COLUMN {1} TO {2}";
public virtual string RenameTable => "RENAME TABLE {0} TO {1}";
public virtual string CreateSchema => "CREATE SCHEMA {0}";
public virtual string AlterSchema => "ALTER SCHEMA {0} TRANSFER {1}.{2}";
public virtual string DropSchema => "DROP SCHEMA {0}";
public virtual string CreateIndex => "CREATE {0}{1}INDEX {2} ON {3} ({4})";
public virtual string DropIndex => "DROP INDEX {0}";
public virtual string InsertData => "INSERT INTO {0} ({1}) VALUES ({2})";
public virtual string UpdateData => "UPDATE {0} SET {1} WHERE {2}";
public virtual string DeleteData => "DELETE FROM {0} WHERE {1}";
public virtual string TruncateTable => "TRUNCATE TABLE {0}";
public virtual string CreateConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} {2} ({3})";
public virtual string DeleteConstraint => "ALTER TABLE {0} DROP CONSTRAINT {1}";
public virtual string CreateForeignKeyConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}){5}{6}";
2019-05-28 17:49:50 +02:00
public virtual string CreateDefaultConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} DEFAULT ({2}) FOR {3}";
2018-06-29 19:52:40 +02:00
public virtual string ConvertIntegerToOrderableString => "REPLACE(STR({0}, 8), SPACE(1), '0')";
Merge commit '94d525d88f713b36419f28bfda4d82ee68637d83' into v9/dev # Conflicts: # build/NuSpecs/UmbracoCms.Web.nuspec # src/Umbraco.Core/Composing/Current.cs # src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs # src/Umbraco.Core/Runtime/CoreRuntime.cs # src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs # src/Umbraco.Infrastructure/Persistence/NPocoDatabaseExtensions.cs # src/Umbraco.Infrastructure/Persistence/UmbracoDatabase.cs # src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs # src/Umbraco.PublishedCache.NuCache/DataSource/BTree.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataModel.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializationResult.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentCacheDataSerializerEntityType.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs # src/Umbraco.PublishedCache.NuCache/DataSource/ContentNestedData.cs # src/Umbraco.PublishedCache.NuCache/DataSource/CultureVariation.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IContentCacheDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/LazyCompressedString.cs # src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs # src/Umbraco.PublishedCache.NuCache/DataSource/PropertyData.cs # src/Umbraco.PublishedCache.NuCache/NuCacheSerializerComponent.cs # src/Umbraco.PublishedCache.NuCache/NuCacheSerializerComposer.cs # src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs # src/Umbraco.Tests/App.config # src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs # src/Umbraco.Tests/PublishedContent/NuCacheTests.cs # src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs # src/Umbraco.Web.UI.NetCore/umbraco/config/lang/da.xml # src/Umbraco.Web.UI/web.Template.Debug.config # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/Composing/ModuleInjector.cs # src/Umbraco.Web/Editors/NuCacheStatusController.cs # src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentNestedData.cs # src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs # src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs # src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs # src/Umbraco.Web/Runtime/WebRuntime.cs
2021-06-24 09:43:57 -06:00
public virtual string ConvertDateToOrderableString => "CONVERT(nvarchar, {0}, 120)";
2018-06-29 19:52:40 +02:00
public virtual string ConvertDecimalToOrderableString => "REPLACE(STR({0}, 20, 9), SPACE(1), '0')";
}
}