2013-01-25 15:05:42 -01:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2016-04-12 15:11:07 +02:00
using NPoco ;
2013-01-25 15:05:42 -01:00
using Umbraco.Core.Persistence.DatabaseModelDefinitions ;
2012-11-30 15:01:52 -01:00
namespace Umbraco.Core.Persistence.SqlSyntax
2012-10-19 13:20:57 -02:00
{
2012-10-23 08:09:01 -02:00
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Server
/// </summary>
2016-04-12 19:55:50 +02:00
[SqlSyntaxProvider(Constants.DbProviderNames.SqlServer)]
2014-09-24 13:51:16 +10:00
public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase < SqlServerSyntaxProvider >
2012-10-19 13:20:57 -02:00
{
2016-04-12 19:55:50 +02:00
// IDatabaseFactory to be lazily injected
public SqlServerSyntaxProvider ( Lazy < IDatabaseFactory > lazyFactory )
2012-10-19 13:20:57 -02:00
{
2016-04-12 19:55:50 +02:00
_serverVersion = new Lazy < ServerVersionInfo >(() =>
{
var factory = lazyFactory . Value ;
if ( factory == null )
throw new InvalidOperationException ( "Failed to determine Sql Server version (no database factory)." );
return DetermineVersion ( factory );
});
2012-10-19 13:20:57 -02:00
}
2016-04-12 19:55:50 +02:00
private readonly Lazy < ServerVersionInfo > _serverVersion ;
internal ServerVersionInfo ServerVersion => _serverVersion . Value ;
internal enum VersionName
{
Invalid = - 1 ,
Unknown = 0 ,
V7 = 1 ,
V2000 = 2 ,
V2005 = 3 ,
V2008 = 4 ,
V2012 = 5 ,
V2014 = 6 ,
Other = 99
}
internal enum EngineEdition
{
Unknown = 0 ,
Desktop = 1 ,
Standard = 2 ,
Enterprise = 3 ,
Express = 4 ,
Azure = 5
}
internal class ServerVersionInfo
{
public string Edition { get ; set ; }
public string InstanceName { get ; set ; }
public string ProductVersion { get ; set ; }
public VersionName ProductVersionName { get ; private set ; }
public EngineEdition EngineEdition { get ; set ; }
public bool IsAzure => EngineEdition == EngineEdition . Azure ;
public string MachineName { get ; set ; }
public string ProductLevel { get ; set ; }
public void Initialize ()
{
var firstPart = string . IsNullOrWhiteSpace ( ProductVersion ) ? "??" : ProductVersion . Split ( '.' )[ 0 ];
switch ( firstPart )
{
case "??" :
ProductVersionName = VersionName . Invalid ;
break ;
case "12" :
ProductVersionName = VersionName . V2014 ;
break ;
case "11" :
ProductVersionName = VersionName . V2012 ;
break ;
case "10" :
ProductVersionName = VersionName . V2008 ;
break ;
case "9" :
ProductVersionName = VersionName . V2005 ;
break ;
case "8" :
ProductVersionName = VersionName . V2000 ;
break ;
case "7" :
ProductVersionName = VersionName . V7 ;
break ;
default :
ProductVersionName = VersionName . Other ;
break ;
}
}
}
private static ServerVersionInfo DetermineVersion ( IDatabaseFactory factory )
{
// Edition: "Express Edition", "Windows Azure SQL Database..."
// EngineEdition: 1/Desktop 2/Standard 3/Enterprise 4/Express 5/Azure
// ProductLevel: RTM, SPx, CTP...
const string sql = @"select
SERVERPROPERTY('Edition') Edition,
SERVERPROPERTY('EditionID') EditionId,
SERVERPROPERTY('InstanceName') InstanceName,
SERVERPROPERTY('ProductVersion') ProductVersion,
SERVERPROPERTY('BuildClrVersion') BuildClrVersion,
SERVERPROPERTY('EngineEdition') EngineEdition,
SERVERPROPERTY('IsClustered') IsClustered,
SERVERPROPERTY('MachineName') MachineName,
SERVERPROPERTY('ResourceLastUpdateDateTime') ResourceLastUpdateDateTime,
SERVERPROPERTY('ProductLevel') ProductLevel;" ;
try
{
var database = factory . GetDatabase ();
var version = database . Fetch < ServerVersionInfo >( sql ). First ();
version . Initialize ();
return version ;
}
catch ( Exception e )
{
// can't ignore, really
throw new Exception ( "Failed to determine Sql Server version (see inner exception)." , e );
}
}
2014-10-16 11:24:29 +10:00
/// <summary>
/// SQL Server stores default values assigned to columns as constraints, it also stores them with named values, this is the only
/// server type that does this, therefore this method doesn't exist on any other syntax provider
/// </summary>
/// <returns></returns>
public IEnumerable < Tuple < string , string , string , string >> GetDefaultConstraintsPerColumn ( Database db )
{
var items = db . Fetch < dynamic >( "SELECT TableName = t.Name,ColumnName = c.Name,dc.Name,dc.[Definition] FROM sys.tables t INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND c.column_id = dc.parent_column_id" );
return items . Select ( x => new Tuple < string , string , string , string >( x . TableName , x . ColumnName , x . Name , x . Definition ));
}
2012-12-19 15:23:05 -01:00
2013-01-25 15:05:42 -01:00
public override IEnumerable < string > GetTablesInSchema ( Database db )
{
var items = db . Fetch < dynamic >( "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES" );
return items . Select ( x => x . TABLE_NAME ). Cast < string >(). ToList ();
}
public override IEnumerable < ColumnInfo > GetColumnsInSchema ( Database db )
{
var items = db . Fetch < dynamic >( "SELECT TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS" );
return
items . Select (
item =>
new ColumnInfo ( item . TABLE_NAME , item . COLUMN_NAME , item . ORDINAL_POSITION , item . COLUMN_DEFAULT ,
item . IS_NULLABLE , item . DATA_TYPE )). ToList ();
}
public override IEnumerable < Tuple < string , string >> GetConstraintsPerTable ( Database db )
{
var items =
db . Fetch < dynamic >(
2013-01-28 14:01:38 -01:00
"SELECT TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE" );
2013-01-25 15:05:42 -01:00
return items . Select ( item => new Tuple < string , string >( item . TABLE_NAME , item . CONSTRAINT_NAME )). ToList ();
}
public override IEnumerable < Tuple < string , string , string >> GetConstraintsPerColumn ( Database db )
{
var items =
db . Fetch < dynamic >(
"SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE" );
return items . Select ( item => new Tuple < string , string , string >( item . TABLE_NAME , item . COLUMN_NAME , item . CONSTRAINT_NAME )). ToList ();
}
2014-03-12 17:17:52 +11:00
public override IEnumerable < Tuple < string , string , string , bool >> GetDefinedIndexes ( Database db )
{
var items =
db . Fetch < dynamic >(
@"select T.name as TABLE_NAME, I.name as INDEX_NAME, AC.Name as COLUMN_NAME,
CASE WHEN I.is_unique_constraint = 1 OR I.is_unique = 1 THEN 1 ELSE 0 END AS [UNIQUE]
from sys.tables as T inner join sys.indexes as I on T.[object_id] = I.[object_id]
inner join sys.index_columns as IC on IC.[object_id] = I.[object_id] and IC.[index_id] = I.[index_id]
inner join sys.all_columns as AC on IC.[object_id] = AC.[object_id] and IC.[column_id] = AC.[column_id]
2014-03-14 13:04:20 +11:00
WHERE I.name NOT LIKE 'PK_%'
2014-03-12 17:17:52 +11:00
order by T.name, I.name" );
2014-03-12 18:37:22 +11:00
return items . Select ( item => new Tuple < string , string , string , bool >( item . TABLE_NAME , item . INDEX_NAME , item . COLUMN_NAME ,
item . UNIQUE == 1 )). ToList ();
2014-03-12 17:17:52 +11:00
}
2012-10-19 13:20:57 -02:00
public override bool DoesTableExist ( Database db , string tableName )
{
var result =
db . ExecuteScalar < long >( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName" ,
new { TableName = tableName });
return result > 0 ;
}
2012-11-30 15:01:52 -01:00
2012-12-20 11:36:20 -01:00
public override string FormatColumnRename ( string tableName , string oldName , string newName )
{
return string . Format ( RenameColumn , tableName , oldName , newName );
}
public override string FormatTableRename ( string oldName , string newName )
{
return string . Format ( RenameTable , oldName , newName );
}
2012-12-07 13:48:38 -01:00
protected override string FormatIdentity ( ColumnDefinition column )
2012-11-30 15:01:52 -01:00
{
return column . IsIdentity ? GetIdentityString ( column ) : string . Empty ;
}
2016-11-03 10:31:44 +01:00
public override Sql < SqlContext > SelectTop ( Sql < SqlContext > sql , int top )
2016-08-02 12:11:35 +02:00
{
2016-11-03 10:31:44 +01:00
return new Sql < SqlContext >( sql . SqlContext , sql . SQL . Insert ( sql . SQL . IndexOf ( ' ' ), " TOP " + top ), sql . Arguments );
2016-08-02 12:11:35 +02:00
}
2012-11-30 15:01:52 -01:00
2012-12-07 13:48:38 -01:00
private static string GetIdentityString ( ColumnDefinition column )
2012-11-30 15:01:52 -01:00
{
return "IDENTITY(1,1)" ;
}
protected override string FormatSystemMethods ( SystemMethods systemMethod )
{
switch ( systemMethod )
{
case SystemMethods . NewGuid :
2015-09-11 17:49:47 +02:00
return "NEWID()" ;
2012-11-30 15:01:52 -01:00
case SystemMethods . CurrentDateTime :
return "GETDATE()" ;
2015-09-11 17:49:47 +02:00
//case SystemMethods.NewSequentialId:
// return "NEWSEQUENTIALID()";
//case SystemMethods.CurrentUTCDateTime:
// return "GETUTCDATE()";
2012-11-30 15:01:52 -01:00
}
return null ;
}
2016-04-12 19:55:50 +02:00
public override string DeleteDefaultConstraint => "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]" ;
2012-12-20 11:36:20 -01:00
2016-04-12 19:55:50 +02:00
public override string DropIndex => "DROP INDEX {0} ON {1}" ;
2012-12-20 11:36:20 -01:00
2016-04-12 19:55:50 +02:00
public override string RenameColumn => "sp_rename '{0}.{1}', '{2}', 'COLUMN'" ;
2012-10-19 13:20:57 -02:00
}
}