using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using ColumnDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.ColumnDefinition;
using TableDefinition = Umbraco.Core.Persistence.SqlSyntax.ModelDefinitions.TableDefinition;
namespace Umbraco.Core.Persistence.SqlSyntax
{
///
/// Represents the Base Sql Syntax provider implementation.
///
///
/// All Sql Syntax provider implementations should derive from this abstract class.
///
///
internal abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider
where TSyntax : ISqlSyntaxProvider
{
protected SqlSyntaxProviderBase()
{
ClauseOrder = new List>
{
FormatString,
FormatType,
FormatNullable,
FormatDefaultValue,
FormatPrimaryKey,
FormatIdentity
};
}
public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})";
public string DefaultValueFormat = " DEFAULT ({0})";
public int DefaultStringLength = 255;
//Set by Constructor
public string StringColumnDefinition;
public string StringLengthColumnDefinitionFormat;
public string AutoIncrementDefinition = "AUTOINCREMENT";
public string IntColumnDefinition = "INTEGER";
public string LongColumnDefinition = "BIGINT";
public string GuidColumnDefinition = "GUID";
public string BoolColumnDefinition = "BOOL";
public string RealColumnDefinition = "DOUBLE";
public string DecimalColumnDefinition = "DECIMAL";
public string BlobColumnDefinition = "BLOB";
public string DateTimeColumnDefinition = "DATETIME";
public string TimeColumnDefinition = "DATETIME";
protected IList> ClauseOrder { get; set; }
protected DbTypes DbTypeMap = new DbTypes();
protected void InitColumnTypeMap()
{
DbTypeMap.Set(DbType.String, StringColumnDefinition);
DbTypeMap.Set(DbType.StringFixedLength, StringColumnDefinition);
DbTypeMap.Set(DbType.StringFixedLength, StringColumnDefinition);
DbTypeMap.Set(DbType.String, StringColumnDefinition);
DbTypeMap.Set(DbType.Boolean, BoolColumnDefinition);
DbTypeMap.Set(DbType.Boolean, BoolColumnDefinition);
DbTypeMap.Set(DbType.Guid, GuidColumnDefinition);
DbTypeMap.Set(DbType.Guid, GuidColumnDefinition);
DbTypeMap.Set(DbType.DateTime, DateTimeColumnDefinition);
DbTypeMap.Set(DbType.DateTime, DateTimeColumnDefinition);
DbTypeMap.Set(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set(DbType.Byte, IntColumnDefinition);
DbTypeMap.Set(DbType.Byte, IntColumnDefinition);
DbTypeMap.Set(DbType.SByte, IntColumnDefinition);
DbTypeMap.Set(DbType.SByte, IntColumnDefinition);
DbTypeMap.Set(DbType.Int16, IntColumnDefinition);
DbTypeMap.Set(DbType.Int16, IntColumnDefinition);
DbTypeMap.Set(DbType.UInt16, IntColumnDefinition);
DbTypeMap.Set(DbType.UInt16, IntColumnDefinition);
DbTypeMap.Set(DbType.Int32, IntColumnDefinition);
DbTypeMap.Set(DbType.Int32, IntColumnDefinition);
DbTypeMap.Set(DbType.UInt32, IntColumnDefinition);
DbTypeMap.Set(DbType.UInt32, IntColumnDefinition);
DbTypeMap.Set(DbType.Int64, LongColumnDefinition);
DbTypeMap.Set(DbType.Int64, LongColumnDefinition);
DbTypeMap.Set(DbType.UInt64, LongColumnDefinition);
DbTypeMap.Set(DbType.UInt64, LongColumnDefinition);
DbTypeMap.Set(DbType.Single, RealColumnDefinition);
DbTypeMap.Set(DbType.Single, RealColumnDefinition);
DbTypeMap.Set(DbType.Double, RealColumnDefinition);
DbTypeMap.Set(DbType.Double, RealColumnDefinition);
DbTypeMap.Set(DbType.Decimal, DecimalColumnDefinition);
DbTypeMap.Set(DbType.Decimal, DecimalColumnDefinition);
DbTypeMap.Set(DbType.Binary, BlobColumnDefinition);
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
}
public virtual string GetQuotedColumnName(string columnName)
{
return string.Format("\"{0}\"", columnName);
}
public virtual string GetQuotedName(string name)
{
return string.Format("\"{0}\"", name);
}
public virtual string GetQuotedValue(string value)
{
return string.Format("'{0}'", 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(SpecialDbTypes dbTypes)
{
if (dbTypes == SpecialDbTypes.NCHAR)
{
return "NCHAR";
}
else if (dbTypes == SpecialDbTypes.NTEXT)
return "NTEXT";
return "NVARCHAR";
}
public virtual string GetColumnDefinition(ColumnDefinition column, string tableName)
{
string dbTypeDefinition;
if (column.HasSpecialDbType)
{
if (column.DbTypeLength.HasValue)
{
dbTypeDefinition = string.Format("{0}({1})",
GetSpecialDbType(column.DbType),
column.DbTypeLength.Value);
}
else
{
dbTypeDefinition = GetSpecialDbType(column.DbType);
}
}
else if (column.PropertyType == typeof(string))
{
dbTypeDefinition = string.Format(StringLengthColumnDefinitionFormat, column.DbTypeLength.GetValueOrDefault(DefaultStringLength));
}
else
{
if (!DbTypeMap.ColumnTypeMap.TryGetValue(column.PropertyType, out dbTypeDefinition))
{
dbTypeDefinition = "";
}
}
var sql = new StringBuilder();
sql.AppendFormat("{0} {1}", GetQuotedColumnName(column.ColumnName), dbTypeDefinition);
if (column.IsPrimaryKeyIdentityColumn)
{
sql.Append(" NOT NULL ").Append(AutoIncrementDefinition);
}
else
{
sql.Append(column.IsNullable ? " NULL" : " NOT NULL");
}
if(column.HasConstraint)
{
sql.Append(GetConstraintDefinition(column, tableName));
}
return sql.ToString();
}
public virtual string GetConstraintDefinition(ColumnDefinition column, string tableName)
{
var sql = new StringBuilder();
sql.AppendFormat(" CONSTRAINT {0}",
string.IsNullOrEmpty(column.ConstraintName)
? GetQuotedName(string.Format("DF_{0}_{1}", tableName, column.ColumnName))
: column.ConstraintName);
string value = column.PropertyType == typeof (string)
? GetQuotedValue(column.ConstraintDefaultValue)
: column.ConstraintDefaultValue;
sql.AppendFormat(DefaultValueFormat, value);
return sql.ToString();
}
public virtual string GetPrimaryKeyStatement(ColumnDefinition column, string tableName)
{
string constraintName = string.IsNullOrEmpty(column.PrimaryKeyName)
? string.Format("PK_{0}", tableName)
: column.PrimaryKeyName;
string columns = string.IsNullOrEmpty(column.PrimaryKeyColumns)
? GetQuotedColumnName(column.ColumnName)
: column.PrimaryKeyColumns;
string sql = string.Format("ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY {2} ({3}); \n",
GetQuotedTableName(tableName),
GetQuotedName(constraintName),
column.IsPrimaryKeyClustered ? "CLUSTERED" : "NONCLUSTERED",
columns);
return sql;
}
public virtual string ToCreateTableStatement(TableDefinition table)
{
var columns = new StringBuilder();
foreach (var column in table.ColumnDefinitions)
{
columns.Append(GetColumnDefinition(column, table.TableName) + ", \n");
}
string sql = string.Format("CREATE TABLE {0} \n(\n {1} \n); \n",
table.TableName,
columns.ToString().TrimEnd(", \n".ToCharArray()));
return sql;
}
public virtual string ToCreatePrimaryKeyStatement(TableDefinition table)
{
var columnDefinition = table.ColumnDefinitions.FirstOrDefault(x => x.IsPrimaryKey);
if (columnDefinition == null)
return string.Empty;
var sql = GetPrimaryKeyStatement(columnDefinition, table.TableName);
return sql;
}
public virtual List ToCreateForeignKeyStatements(TableDefinition table)
{
var foreignKeys = new List();
foreach (var key in table.ForeignKeyDefinitions)
{
string constraintName = string.IsNullOrEmpty(key.ConstraintName)
? string.Format("FK_{0}_{1}_{2}", table.TableName, key.ReferencedTableName, key.ReferencedColumnName)
: key.ConstraintName;
foreignKeys.Add(string.Format("ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}); \n",
GetQuotedTableName(table.TableName),
GetQuotedName(constraintName),
GetQuotedColumnName(key.ColumnName),
GetQuotedTableName(key.ReferencedTableName),
GetQuotedColumnName(key.ReferencedColumnName)));
}
return foreignKeys;
}
public virtual List ToCreateIndexStatements(TableDefinition table)
{
var indexes = new List();
foreach (var index in table.IndexDefinitions)
{
string name = string.IsNullOrEmpty(index.IndexName)
? string.Format("IX_{0}_{1}", table.TableName, index.IndexForColumn)
: index.IndexName;
string columns = string.IsNullOrEmpty(index.ColumnNames)
? GetQuotedColumnName(index.IndexForColumn)
: index.ColumnNames;
indexes.Add(string.Format("CREATE {0} INDEX {1} ON {2} ({3}); \n",
GetIndexType(index.IndexType),
GetQuotedName(name),
GetQuotedTableName(table.TableName),
columns));
}
return indexes;
}
public virtual List ToAlterIdentitySeedStatements(TableDefinition table)
{
var seeds = new List();
foreach (var definition in table.ColumnDefinitions)
{
if (definition.PrimaryKeySeeding > 0)
{
seeds.Add(string.Format("ALTER TABLE {0} ALTER COLUMN {1} IDENTITY({2},1); \n",
GetQuotedTableName(table.TableName),
GetQuotedColumnName(definition.ColumnName),
definition.PrimaryKeySeeding));
}
}
return seeds;
}
public virtual bool DoesTableExist(Database db, string tableName)
{
return false;
}
public virtual DbType GetColumnDbType(Type valueType)
{
if (valueType.IsEnum)
return DbTypeMap.ColumnDbTypeMap[typeof(string)];
return DbTypeMap.ColumnDbTypeMap[valueType];
}
public virtual string Format(DatabaseModelDefinitions.ColumnDefinition column)
{
var clauses = new List();
foreach (var action in ClauseOrder)
{
string clause = action(column);
if (!string.IsNullOrEmpty(clause))
clauses.Add(clause);
}
return string.Join(" ", clauses.ToArray());
}
public virtual string FormatString(DatabaseModelDefinitions.ColumnDefinition column)
{
return GetQuotedColumnName(column.Name);
}
protected virtual string FormatType(DatabaseModelDefinitions.ColumnDefinition column)
{
if (!column.Type.HasValue)
return column.CustomType;
var dbType = DbTypeMap.ColumnDbTypeMap.First(x => x.Value == column.Type.Value).Key;
var definition = DbTypeMap.ColumnTypeMap.First(x => x.Key == dbType).Value;
string dbTypeDefinition = column.Size != default(int)
? string.Format("{0}({1})", definition, column.Size)
: definition;
//NOTE Percision is left out
return dbTypeDefinition;
}
protected virtual string FormatNullable(DatabaseModelDefinitions.ColumnDefinition column)
{
return column.IsNullable ? string.Empty : "NOT NULL";
}
protected virtual string FormatDefaultValue(DatabaseModelDefinitions.ColumnDefinition column)
{
if (column.DefaultValue == null)
return string.Empty;
// see if this is for a system method
if (column.DefaultValue is SystemMethods)
{
string method = FormatSystemMethods((SystemMethods)column.DefaultValue);
if (string.IsNullOrEmpty(method))
return string.Empty;
return "DEFAULT " + method;
}
return "DEFAULT " + GetQuotedValue(column.DefaultValue.ToString());
}
protected virtual string FormatPrimaryKey(DatabaseModelDefinitions.ColumnDefinition column)
{
return string.Empty;
}
protected abstract string FormatSystemMethods(SystemMethods systemMethod);
protected abstract string FormatIdentity(DatabaseModelDefinitions.ColumnDefinition column);
public virtual string CreateTable { get { return "CREATE TABLE {0} ({1})"; } }
public virtual string DropTable { get { return "DROP TABLE {0}"; } }
public virtual string AddColumn { get { return "ALTER TABLE {0} ADD COLUMN {1}"; } }
public virtual string DropColumn { get { return "ALTER TABLE {0} DROP COLUMN {1}"; } }
public virtual string AlterColumn { get { return "ALTER TABLE {0} ALTER COLUMN {1}"; } }
public virtual string RenameColumn { get { return "ALTER TABLE {0} RENAME COLUMN {1} TO {2}"; } }
public virtual string RenameTable { get { return "RENAME TABLE {0} TO {1}"; } }
public virtual string CreateSchema { get { return "CREATE SCHEMA {0}"; } }
public virtual string AlterSchema { get { return "ALTER SCHEMA {0} TRANSFER {1}.{2}"; } }
public virtual string DropSchema { get { return "DROP SCHEMA {0}"; } }
public virtual string CreateIndex { get { return "CREATE {0}{1}INDEX {2} ON {3} ({4})"; } }
public virtual string DropIndex { get { return "DROP INDEX {0}"; } }
public virtual string InsertData { get { return "INSERT INTO {0} ({1}) VALUES ({2})"; } }
public virtual string UpdateData { get { return "UPDATE {0} SET {1} WHERE {2}"; } }
public virtual string DeleteData { get { return "DELETE FROM {0} WHERE {1}"; } }
public virtual string CreateConstraint { get { return "ALTER TABLE {0} ADD CONSTRAINT {1} {2} ({3})"; } }
public virtual string DeleteConstraint { get { return "ALTER TABLE {0} DROP CONSTRAINT {1}"; } }
public virtual string CreateForeignKeyConstraint { get { return "ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}){5}{6}"; } }
}
}